Widgets:
HomePageView
-- GuessTheWordPage
I have a provider that keeps track of stats. When the StateNotifier instantiates it loads the saved stats from SharedPrefferences. The issue is that when it is first used it calls incrementStarts() but the _load() has not completed by then so it increments before it loads the saved data.
I can solve this by doing a watch on the provider in HomePageView instead of GameView. But it feels dirty as it is never used there.
How can I make sure the data is loaded from SharedPrefferences before it is used? I have tried to change it so _load() is called from another function _init() async { await _load(); }, but it does not solve the issue.
As I have said I can change HomeView to a ConsumerWidget and add: StatsState statsState = ref.watch(statsProvider); but it feels dirty.
logs
I/flutter (11187): StatsStateNotifier::load()
I/flutter (11187): StatsStateNotifier::incrementStarts()
I/flutter (11187): starts: 0
I/flutter (11187): starts: 1
I/flutter (11187): json: {"wins":0,"loses":0,"starts":4,"tried":0,"guesses":[]}
I/flutter (11187): StatsStateNotifier::load() -- done
class GuessTheWordStats {
int wins; // won a game
int loses; // lost a game, did not find the solution
int starts; // started a new game
int tried; // tried at least one solution
List<int> guesses; // number of guesses to get solution
GuessTheWordStats({
required this.wins,
required this.loses,
required this.starts,
required this.tried,
required this.guesses,
});
factory GuessTheWordStats.fromJson(Map<String, dynamic> json) {
return GuessTheWordStats(
wins: json['wins'],
loses: json['loses'],
starts: json['starts'],
tried: json['tried'],
guesses: List<int>.from(json['guesses']),
);
}
Map<String, dynamic> toJson() => {
'wins': wins,
'loses': loses,
'starts': starts,
'tried': tried,
'guesses': guesses,
};
static GuessTheWordStats init() {
return GuessTheWordStats(
wins: 0, loses: 0, starts: 0, tried: 0, guesses: []);
}
}
@immutable
class StatsState {
const StatsState({
required this.guessTheWordStats,
});
final GuessTheWordStats guessTheWordStats;
StatsState copyWith({
GuessTheWordStats? guessTheWordStats,
}) {
return StatsState(
guessTheWordStats: guessTheWordStats ?? this.guessTheWordStats,
);
}
}
StatsState settingsInitialState() {
return StatsState(
guessTheWordStats: GuessTheWordStats.init(),
);
}
class StatsStateNotifier extends StateNotifier<StatsState> {
StatsStateNotifier() : super(settingsInitialState()) {
_load();
}
Future<void> _load() async {
print('StatsStateNotifier::load()');
final prefs = await SharedPreferences.getInstance();
final String? json = prefs.getString('GuessTheWordStats');
print('json: $json');
GuessTheWordStats guessTheWordStats = GuessTheWordStats.init();
if (json != null) {
guessTheWordStats = GuessTheWordStats.fromJson(jsonDecode(json));
}
state = state.copyWith(
guessTheWordStats: guessTheWordStats,
);
// print('GuessTheWordStats.wins: ${guessTheWordStats.wins}');
// print('GuessTheWordStats.loses: ${guessTheWordStats.loses}');
// print('GuessTheWordStats.starts: ${guessTheWordStats.starts}');
// print('GuessTheWordStats.tried: ${guessTheWordStats.tried}');
// print('GuessTheWordStats.guesses: ${guessTheWordStats.guesses}');
print('StatsStateNotifier::load() -- done');
}
Future<void> _save() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(
'GuessTheWordStats', jsonEncode(state.guessTheWordStats.toJson()));
}
incrementWins() {
GuessTheWordStats guessTheWordStats = state.guessTheWordStats;
guessTheWordStats.wins++;
state = state.copyWith(
guessTheWordStats: guessTheWordStats,
);
_save();
}
incrementStarts() {
print('StatsStateNotifier::incrementStarts()');
print('starts: ${state.guessTheWordStats.starts}');
GuessTheWordStats guessTheWordStats = state.guessTheWordStats;
guessTheWordStats.starts++;
state = state.copyWith(
guessTheWordStats: guessTheWordStats,
);
_save();
print('starts: ${state.guessTheWordStats.starts}');
}
incrementLoses() {
GuessTheWordStats guessTheWordStats = state.guessTheWordStats;
guessTheWordStats.loses++;
state = state.copyWith(
guessTheWordStats: guessTheWordStats,
);
_save();
}
}
final StateNotifierProvider<StatsStateNotifier, StatsState> statsProvider =
StateNotifierProvider<StatsStateNotifier, StatsState>((ref) {
return StatsStateNotifier();
});
class GuessTheWordPage extends ConsumerStatefulWidget {
const GuessTheWordPage({Key? key}) : super(key: key);
@override
GuessTheWordPageState createState() => GuessTheWordPageState();
}
class GuessTheWordPageState extends ConsumerState<GuessTheWordPage> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
MainAppSettings mainAppSettings = MainAppSettings();
_initSettings() async {
await mainAppSettings.loadFromPrefs();
}
_initNewGame() async {
await ref.read(guessTheWordProvider.notifier).newGame();
// first use of statsProvider //
ref.read(statsProvider.notifier).incrementStarts();
// -- //
}
@override
void initState() {
if (kDebugMode) print('GuessTheWordPage::initState()');
super.initState();
_initSettings();
_initNewGame();
}
@override
Widget build(BuildContext context) {