Riverpod: "setState() or markNeedsBuild() called during build."

Viewed 405

i am somewhat stuck in understanding Riverpod as i am new to it and also to Flutter. I get the Exception "setState() or markNeedsBuild() called during build." in that view on the line where i watch the videoSettingsProvider. This occurs in the following scenario: I switch from my VideoView to the SettingsView, change a value there (and this works just fine) and then i switch back to the VideoView. That is the point where it crashes.

I think the reason for this is that when i switch back to the VideoView it gets rebuilt (because the state is not retained) and in that rebuilt it checks the provider and also realizes that it has changed, making the widget try to rebuild itself again).

class VideoView extends ConsumerWidget {
  const VideoView({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final videoSettings = ref.watch(videoSettingsProvider);
      return const Center(child: CircularProgressIndicator());
    }
    return Container();
  }
}

I can open that view just fine when the provider didn't change somewhere else in the code (in the following example the call to change the value of that provider is in another view. The faulting view's state is not retained. I found a workaround but that doesn't seem apropiate to me, more on that later).

//Thats the callback i use to update the Value in the Provider
 void qualitySettingCallback(double val) {
      ref.read(qualitySettingProvider.notifier).changeValue(val.toInt());
    }

Heres the StateNotifier:

//This is how the StateNotifier looks like which gets called in the widgets build i first posted
class NumericSettingNotifier extends StateNotifier<NumericSetting> {
  NumericSettingNotifier(NumericSetting state) : super(state);
 
  void changeValue(int newValue) => WidgetsBinding.instance!
      .addPostFrameCallback((_) => state = NumericSetting(
          value: newValue,
          minValue: state.minValue,
          maxValue: state.maxValue,
          defaultValue: state.defaultValue));
}

This is the videoSettingsProvider which gets rebuilt when i change one of the specific StateNotifierProviders:

final videoSettingsProvider = Provider((ref) {
  final resolutionSetting = ref.watch(resolutionSettingProvider);
  final specialEffectSetting = ref.watch(specialEffectSettingProficer);
  final qualitySetting = ref.watch(qualitySettingProvider);
  final brightnessSetting = ref.watch(brightnessSettingProvider);
  final contrastSetting = ref.watch(contrastSettingProvider);
  final saturationSetting = ref.watch(saturationSettingProvider);
 
  return VideoSetting(
    resolutionSetting,
    specialEffectSetting,
    qualitySetting,
    brightnessSetting,
    contrastSetting,
    saturationSetting,
  );
});

Now to the problem itself: The exception is saying clearly what is happening. It is trying to rebuild while a rebuild is going on. When i change the VideoView to remember its state (AutomaticKeepAliveClientMixin) it works because the rebuilt is done while i change the settings in the SettingsView. But that is no solution and more of a hacky workaround. What am i doing wrong/what should i do instead?

Thanks!

Edit: Here is the requested code snippet:

  SettingCard(
    widgetName: "Saturation",
    controlWidget: Slider(
      min: vidSaturationSetting.minValue.toDouble(),
      max: vidSaturationSetting.maxValue.toDouble(),
      divisions: (vidSaturationSetting.minValue).abs() +
          (vidSaturationSetting.maxValue).abs(),
      value: vidSaturationSetting.value.toDouble(),
      onChanged: (newValue) =>
          _saveNumericSetting(AppConstants.vidSaturation, newValue),
    ),
  ),

and

void _saveNumericSetting(String saveKeyName, double valueToSave) {
  _numericNotifierSelector(ref, saveKeyName)
      .changeValue(valueToSave.toInt());
  spClient.setValue(saveKeyName, valueToSave.toInt());
}
0 Answers
Related