skip to Main Content

I’m trying to build a sudoku application in flutter but the problem is it needs a continue button in it but I don’t know how to save current played game data in the Json.


class GameController extends GetxController {
  Timer? _timer;
  int remainingSeconds = 1;
  final time = '00:00'.obs;
  RxList<List<SudokuCell>> sudoku = RxList<List<SudokuCell>>();
  RxInt mistakes = 3.obs;
  RxInt hints = 3.obs;
  SudokuCell selectedSudoku = SudokuCell(
      text: 0,
      correctText: 0,
      row: 100,
      col: 100,
      team: 100,
      isFocus: false,
      isCorrect: false,
      isDefault: false,
      isExist: false,
      note: []);
  RxBool isNote = false.obs;
  bool restartClicked = false;

  @override
  void onReady() {
    _startTimer(900);
    super.onReady();
  }

  @override
  void onClose() {
    if (_timer == null || stopTime == true) {
      _timer!.cancel();
    }
  }

  void restart(int difficultyLevel) {
    mistakes.value = 3;
    hints.value = 3;
    sudoku.clear();
    restartClicked = true;
    List<List<int>> boxValues = s.generator(difficultyLevel: difficultyLevel);
    List<List<int>> boxValueSolution = s.toSudokuList(boxValues);
    s.solver(boxValueSolution);
    for (var i = 0; i < 9; i++) {
      sudoku.add([]);
      for (var j = 0; j < 9; j++) {
        int team = 0;
        if (i < 3 && j < 3) {
          team = 1;
        } else if (i < 3 && j < 6) {
          team = 2;
        } else if (i < 3 && j < 9) {
          team = 3;
        } else if (i < 6 && j < 3) {
          team = 4;
        } else if (i < 6 && j < 6) {
          team = 5;
        } else if (i < 6 && j < 9) {
          team = 6;
        } else if (i < 9 && j < 3) {
          team = 7;
        } else if (i < 9 && j < 6) {
          team = 8;
        } else if (i < 9 && j < 9) {
          team = 9;
        }
        SudokuCell value = SudokuCell(
            text: boxValues[i][j],
            correctText: boxValueSolution[i][j],
            row: i,
            col: j,
            team: team,
            isFocus: false,
            isCorrect: boxValues[i][j] == boxValueSolution[i][j],
            isDefault: boxValues[i][j] != 0,
            isExist: false,
            note: []);
        sudoku[i].add(value);
      }
    }
  }

  isComplete() {
    bool isComplete = true;
    for (var i = 0; i < sudoku.length; i++) {
      for (var j = 0; j < sudoku.length; j++) {
        if (sudoku[i][j].text == 0) {
          isComplete = false;
        }
      }
    }
    if (isComplete == true) {
      levelCompleted();
    }
  }

  void onErase() {
    if (_unChangable()) return;
    sudoku[selectedSudoku.row][selectedSudoku.col].text = 0;
    sudoku[selectedSudoku.row][selectedSudoku.col].isCorrect = false;
    sudoku[selectedSudoku.row][selectedSudoku.col].note.clear();
    selectedSudoku.text = 0;
    selectedSudoku.isCorrect = false;
    selectedSudoku.note.clear();
    update();
  }

  void onNoteFill() {
    if (_unChangable()) return;
    sudoku[selectedSudoku.row][selectedSudoku.col].note =
        List.generate(9, (index) => index + 1);
    fetchSafeValues();
    update();
  }

  // ignore: duplicate_ignore
  void onHint() {
    if (_unChangable()) return;
    // ignore: unrelated_type_equality_checks
    if (hints == 0) return;
    sudoku[selectedSudoku.row][selectedSudoku.col].text =
        sudoku[selectedSudoku.row][selectedSudoku.col].correctText;
    sudoku[selectedSudoku.row][selectedSudoku.col].isCorrect = true;
    removeNoteValue(sudoku[selectedSudoku.row][selectedSudoku.col].correctText);
    isComplete();
    hints--;
  }

  void onNumberclick(int index) {
    if (selectedSudoku.row == 100) return;
    if (isNote.value) {
      if (sudoku[selectedSudoku.row][selectedSudoku.col]
          .note
          .contains(index + 1)) {
        sudoku[selectedSudoku.row][selectedSudoku.col].note.remove((index + 1));
      } else {
        sudoku[selectedSudoku.row][selectedSudoku.col].note.add((index + 1));
      }
      fetchSafeValues();
    } else {
      if (selectedSudoku.isCorrect) return;
      selectedSudoku.text = index + 1;
      selectedSudoku.isCorrect =
          selectedSudoku.text == selectedSudoku.correctText;
      sudoku[selectedSudoku.row][selectedSudoku.col] = selectedSudoku;
      if (selectedSudoku.correctText != (index + 1)) {
        mistakes--;
        if (mistakes == 0.obs) {
          showRestartDialogue('Game Over!');
        }
      } else {
        removeNoteValue(index + 1);
      }
      isComplete();
    }
    update();
  }

  bool _unChangable() {
    if (selectedSudoku.row == 100) return true;
    if (selectedSudoku.isDefault) return true;
    return false;
  }

  void showRestartDialogue(String text) => Get.defaultDialog(
        backgroundColor: Colors.blue.shade50,
        barrierDismissible: false,
        buttonColor: Colors.greenAccent,
        title: text,
        content: SizedBox(
          height: 200,
          child: Column(
            children: [
              TextButton(
                  onPressed: () {
                    restart(1);
                    Get.back();
                  },
                  child: const Text('Beginner')),
              TextButton(
                  onPressed: () {
                    restart(2);
                    Get.back();
                  },
                  child: const Text('Easy')),
              TextButton(
                  onPressed: () {
                    restart(3);
                    Get.back();
                  },
                  child: const Text("Medium")),
              TextButton(
                  onPressed: () {
                    restart(4);
                    Get.back();
                  },
                  child: const Text('Hard')),
            ],
          ),
        ),
      );

  bool isSafe(int row, int col) {
    return selectedSudoku.col == sudoku[row][col].col ||
        selectedSudoku.row == sudoku[row][col].row ||
        selectedSudoku.team == sudoku[row][col].team;
  }

  void fetchSafeValues() {
    List<int> safeValues = [];
    for (var i = 0; i < 9; i++) {
      for (var j = 0; j < 9; j++) {
        if (selectedSudoku.row == i) {
          safeValues.add(sudoku[i][j].text);
        } else if (selectedSudoku.col == j) {
          safeValues.add(sudoku[i][j].text);
        } else if (selectedSudoku.team == sudoku[i][j].team) {
          safeValues.add(sudoku[i][j].text);
        }
      }
    }
    safeValues.removeWhere((element) => element == 0);
    for (var value in safeValues) {
      sudoku[selectedSudoku.row][selectedSudoku.col].note.remove(value);
      selectedSudoku.note.remove(value);
    }
  }

  void removeNoteValue(int number) {
    for (var i = 0; i < 9; i++) {
      for (var j = 0; j < 9; j++) {
        if (isSafe(i, j)) {
          sudoku[i][j].note.remove(number);
        }
      }
    }
  }

  _startTimer(int seconds) {
    const duration = Duration(seconds: 1);
    remainingSeconds = seconds;
    _timer = Timer.periodic(duration, (Timer timer) {
      if (remainingSeconds == 0 || isComplete() == true) {
        timer.cancel();
        return showGameOverDialog();
      } else {
        int minutes = remainingSeconds ~/ 60;
        int seconds = (remainingSeconds % 60);
        time.value =
            "${minutes.toString().padLeft(2, "0")}:${seconds.toString().padLeft(2, "0")}";
        remainingSeconds--;
      }
    });
  }

  restartTimer(int seconds) {
    const duration = Duration(seconds: 1);
    remainingSeconds = seconds;
    _timer = Timer.periodic(duration, (Timer timer) {
      if (remainingSeconds == 0 || isComplete() == true) {
        timer.cancel();
        return showGameOverDialog();
      } else {
        int minutes = remainingSeconds ~/ 60;
        int seconds = (remainingSeconds % 60);
        time.value =
            "${minutes.toString().padLeft(2, "0")}:${seconds.toString().padLeft(2, "0")}";
        remainingSeconds--;
      }
    });
  }

  void showGameOverDialog() => Get.defaultDialog(
      title: 'Game Over',
      content: SizedBox(
          height: 50,
          child: TextButton(
              onPressed: () => Get.back(),
              child: const Text('Start New Game!'))));

  void levelCompleted() => Get.defaultDialog(
        backgroundColor: Colors.blue.shade50,
        title: 'Level Completed',
        content: SizedBox(
          height: 100,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              const Text("Earned a star"),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  ElevatedButton(
                    style: ButtonStyle(
                      backgroundColor:
                          MaterialStateProperty.all(Colors.blue.shade400),
                      textStyle: MaterialStateProperty.all(
                        const TextStyle(
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ),
                    onPressed: () {
                      Get.offAll(const HomeScreen());
                    },
                    child: const Text('Main Menu'),
                  ),
                  ElevatedButton(
                    onPressed: () {
                      showRestartDialogue('Choose difficulty');
                    },
                    style: ButtonStyle(
                      backgroundColor:
                          MaterialStateProperty.all(Colors.blue.shade400),
                      textStyle: MaterialStateProperty.all(
                        const TextStyle(
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ),
                    child: const Text('Next Game'),
                  )
                ],
              ),
            ],
          ),
        ),
      );

  Widget button(String text, difficulty) {
    return TextButton(
      onPressed: () {
        showRestartDialogue('Choose difficulty');
        Get.back();
      },
      style: ButtonStyle(
        textStyle: MaterialStateProperty.all(
          const TextStyle(
            fontWeight: FontWeight.bold,
            color: Colors.white,
          ),
        ),
      ),
      child: Text(
        text,
        style: TextStyle(color: Colors.blue.shade300),
      ),
    );
  }
}

this is my game controller class
and


class SudokuCell {
  int text;
  int correctText;
  int row;
  int col;
  int team;
  bool isFocus;
  bool isCorrect;
  bool isDefault;
  bool isExist;
  List<int> note;

  SudokuCell({
    required this.text,
    required this.correctText,
    required this.row,
    required this.col,
    required this.team,
    required this.isFocus,
    required this.isCorrect,
    required this.isDefault,
    required this.isExist,
    required this.note,
  });

  SudokuCell copyWith({
    int? text,
    int? correctText,
    int? row,
    int? col,
    int? team,
    int? difficulty,
    bool? isFocus,
    bool? isCorrect,
    bool? isDefault,
    bool? isExist,
    List<int>? note,
  }) {
    return SudokuCell(
      text: text ?? this.text,
      correctText: correctText ?? this.correctText,
      row: row ?? this.row,
      col: col ?? this.col,
      team: team ?? this.team,
      isFocus: isFocus ?? this.isFocus,
      isCorrect: isCorrect ?? this.isCorrect,
      isDefault: isDefault ?? this.isDefault,
      isExist: isExist ?? this.isExist,
      note: note ?? this.note,
    );
  }

  Map<String, dynamic> toMap() {
    return <String, dynamic>{
      'text': text,
      'correctText': correctText,
      'row': row,
      'col': col,
      'team': team,
      'isFocus': isFocus,
      'isCorrect': isCorrect,
      'isDefault': isDefault,
      'isExist': isExist,
      'note': note,
    };
  }

  factory SudokuCell.fromMap(Map<String, dynamic> map) {
    return SudokuCell(
      text: map['text'] as int,
      correctText: map['correctText'] as int,
      row: map['row'] as int,
      col: map['col'] as int,
      team: map['team'] as int,
      isFocus: map['isFocus'] as bool,
      isCorrect: map['isCorrect'] as bool,
      isDefault: map['isDefault'] as bool,
      isExist: map['isExist'] as bool,
      note: List<int>.from(
        (map['note'] as List<int>),
      ),
    );
  }

  String toJson() => json.encode(toMap());

  factory SudokuCell.fromJson(String source) =>
      SudokuCell.fromMap(json.decode(source) as Map<String, dynamic>);

  @override
  String toString() {
    return 'SudokuCell(text: $text, correctText: $correctText, row: $row, col: $col, team: $team,isFocus: $isFocus, isCorrect: $isCorrect, isDefault: $isDefault, isExist: $isExist, note: $note)';
  }

  @override
  bool operator ==(covariant SudokuCell other) {
    if (identical(this, other)) return true;

    return other.text == text &&
        other.correctText == correctText &&
        other.row == row &&
        other.col == col &&
        other.team == team &&
        other.isFocus == isFocus &&
        other.isCorrect == isCorrect &&
        other.isDefault == isDefault &&
        other.isExist == isExist &&
        listEquals(other.note, note);
  }

  @override
  int get hashCode {
    return text.hashCode ^
        correctText.hashCode ^
        row.hashCode ^
        col.hashCode ^
        team.hashCode ^
        isFocus.hashCode ^
        isCorrect.hashCode ^
        isDefault.hashCode ^
        isExist.hashCode ^
        note.hashCode;
  }
}

this is the model file for everything

I’m saving the json in sharedpreferences to access it but it shows exception about being the values are null.

2

Answers


  1. You can use, Hive local database to save json files according to your needs,
    https://pub.dev/packages/hive

    See this example,

    // Create a box collection
      final collection = await BoxCollection.open(
        'MyFirstFluffyBox', // Name of your database
        {'cats', 'dogs'}, // Names of your boxes
        path: './', // Path where to store your boxes (Only used in Flutter / Dart IO)
        key: HiveCipher(), // Key to encrypt your boxes (Only used in Flutter / Dart IO)
      );
    
      // Open your boxes. Optional: Give it a type.
      final catsBox = collection.openBox<Map>('cats');
    
      // Put something in
      await catsBox.put('fluffy', {'name': 'Fluffy', 'age': 4});
      await catsBox.put('loki', {'name': 'Loki', 'age': 2});
    
      // Get values of type (immutable) Map?
      final loki = await catsBox.get('loki');
      print('Loki is ${loki?['age']} years old.');
    
      // Returns a List of values
      final cats = await catsBox.getAll(['loki', 'fluffy']);
      print(cats);
    
      // Returns a List<String> of all keys
      final allCatKeys = await catsBox.getAllKeys();
      print(allCatKeys);
    
      // Returns a Map<String, Map> with all keys and entries
      final catMap = await catsBox.getAllValues();
      print(catMap);
    
      // delete one or more entries
      await catsBox.delete('loki');
      await catsBox.deleteAll(['loki', 'fluffy']);
    
      // ...or clear the whole box at once
      await catsBox.clear();
    
      // Speed up write actions with transactions
      await collection.transaction(
        () async {
          await catsBox.put('fluffy', {'name': 'Fluffy', 'age': 4});
          await catsBox.put('loki', {'name': 'Loki', 'age': 2});
          // ...
        },
        boxNames: ['cats'], // By default all boxes become blocked.
        readOnly: false,
      );
    
    Login or Signup to reply.
  2. You are probably trying to save the data in json so that you can use it after reopening the app. In such case, you don’t need to save json file. Instead you use storage managers to save data locally into the device. There are 3 Popular Storage managers:

    1. Get Storage
    2. Shared Preference
    3. Hive

    As you are already using Getx, I would suggest you to use Get Storage

    In your main function init GetStorage

    main() async {
      await GetStorage.init();
      runApp(App());
    }
    

    To read or write to your box or local storage create a box instance

    final box = GetStorage();
    

    After that, convert your suduko list instance to its toJson format.

    const sudukoMap = sudoku.map((e) => suduko.toJson())
    

    Then json encode your sudukoMap and save it to the box with a key value

    box.write('suduko', json.encode(sudukoMap));
    

    Now it is now saved locally so where ever you want to access it, just read from box

    print(box.read('suduko'))
    

    Don’t forget to json decode the list with SudokuCell.fromJson() and jsonDecode the string. Remember, we are actually saving the object as string as GetStorage only supports primitive type

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search