Home > Mobile >  Save data in Json flutter
Save data in Json flutter

Time:12-23

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.

CodePudding user response:

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,
  );

CodePudding user response:

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

  • Related