I think I'm missing something about the Riverpod philosophy.
I'm keeping all my game state in a ChangeNotifier, including the .stage
which can be "start", "playing", "winning", etc:
final ChangeNotifierProvider<GameState> gameStateProvider =
ChangeNotifierProvider<GameState>((ref) => GameState(stage="start"));
My build()
functions start by accessing this state:
build(context, ref) {
final GameState gameState = ref.watch(gameStateProvider);
And build themselves depending upon the state of gameState
.
But somewhere I have to detect that the game has progressed to the point that I should update state, e.g. gameState.stage = 'winning'
. I tried this:
build(context, ref) {
final GameState gameState = ref.watch(gameStateProvider);
if (gameState.stage == "playing" && gameState.someConditionsAreMet) {
gameState.stage = "winning"; // .stage setter triggers notifyListeners()
}
But I am not allowed to call notifyListeners
in build()
because it tries to mark the widget _needsBuild
during the build phase.
So I wrap the call in a Timer.run()
to make it asynchronous, and the widget happily rebuilds during the next frame:
build(context, ref) {
final GameState gameState = ref.watch(gameStateProvider);
if (gameState.stage == "playing" && gameState.someConditionsAreMet) {
Timer.run(() => gameState.stage = "winning"); // OK: will flag dirty only after build
// now what?!
...but now I don't know what to return from build()
when that if
gets triggered:
- I can't return what I would normally return when
gameState.stage == "winning"
, because it still thinks the stage is"playing"
(theTimer.run()
callback hasn't executed yet.) - I can't return what I would normally return when
gameState.stage == "playing"
, because the conditions that made me want to trigger the change of stage make that impossible (e.g. I print the strongest enemy's HP, but there are now zero enemies.) - I can't return
SizedBox.shrink()
as a hack because that would cause a single frame to render blank, causing a flash on the screen before the.stage = "winning"
widget can render.
I considered just doing the update to gameState.stage
inside gameState
itself, as soon as the right conditions were triggered. But then stateless widgets don't have a way of detecting that state has just changed to do one-time events, like pushing a welcome modal on top of the main screen the first time .stage
changes from "start"
to "playing"
.
So I'm clearly doing something wrong architecturally. How do experienced Riverpod users do it?
CodePudding user response:
You can not Call Notify Listener In the Build Method, You can Only Listen or Watch for State Change int the Builder, Notify Listener Should only be called From the Change Notifier Class as this is the Purpose of that class