Reputation: 33
In my main function I placed a ChangeNotifierProvider for the volume model:
`void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => VolumeModel())
],
home: const PrimaryScreenController()
),
));
}`
These are the contents of the PrimaryScreenController class (note that I placed the vol variable there to test if the notifyListeners() and provider respond):
`@override
Widget build(BuildContext context) {
double vol = Provider.of<VolumeModel>(context, listen: true).currentBackgroundMusicVolume;
print(vol);
playBackgroundMusic();
return MaterialApp(
home: Scaffold(
body: PageView(
controller: _controller,
physics: const NeverScrollableScrollPhysics(),
children: [
const HomeScreen(),
LearnScreen(audioPlayer),
SettingsScreen(),
],
),
),
));
}`
The Settings screen contains instances of the SettingsSlider class I made. For example, the constructor:
SettingsSlider(volumeValue: currentBackgroundMusicVolume, volumeLabel: "backgroundMusicVolume"),
leads to this:
`class SettingsSlider extends StatefulWidget {
double volumeValue;
String volumeLabel;
VolumeModel volume = VolumeModel();
SettingsSlider({super.key, required this.volumeValue, required this.volumeLabel});
@override
State<SettingsSlider> createState() => _SettingsSliderState();
}
class _SettingsSliderState extends State<SettingsSlider> {
@override
Widget build(BuildContext context) {
return Slider(
value: widget.volumeValue,
min: 0,
max: 100,
label: widget.volumeValue.round().toString(),
activeColor: const Color(0xffDF9676),
thumbColor: const Color(0xff57CC85),
onChanged: (double value) {
setState(() {
widget.volumeValue = value;
widget.volume.changeVolume(value, widget.volumeLabel);
});
},
);
}
}`
Finally, the volume object of VolumeModel contains the ChangeNotifier and notifyListeners:
`class VolumeModel extends ChangeNotifier {
VolumeModel();
double currentBackgroundMusicVolume = 100;
double currentSpeechAudioVolume = 100;
double currentSoundEffectsVolume = 100;
static String backgroundMusicKey = "backgroundMusicVolume";
static String speechAudioKey = "speechAudioVolume";
static String soundEffectsKey = "soundEffectsVolume";
void changeVolume(double newValue, String label) {
if (label == backgroundMusicKey) {
currentBackgroundMusicVolume = newValue;
notifyListeners();
}
else if (label == speechAudioKey) {
currentSpeechAudioVolume = newValue;
notifyListeners();
}
else if (label == soundEffectsKey) {
currentSoundEffectsVolume = newValue;
notifyListeners();
}
saveVolumeSetting(newValue, label);
}
Future<void> saveVolumeSetting(double volumeValue, String volumeLabel) async {
final SharedPreferences pref = await SharedPreferences.getInstance();
pref.setDouble(volumeLabel, volumeValue);
//volumeLabel is either backgroundMusicVolume, speechAudioVolume, and soundEffectsVolume.
}`
Why is it that the Provider.of line earlier only captures the initialized value, then the notifyListeners no longer goes up to that Provider.of? This happens although my print() shows that the values get passed down to changeVolume() and the currentBackgroundMusicVolume variable does indeed change.
Thank you for your response and help. (Thank you for understanding; it is my first time to ask a question here.)
I tried everything I could, including using a Consumer widget. But both Provider and Consumer are unresponsive. What could possibly be the problem? Is it the widget tree or something else?
Upvotes: 0
Views: 137
Reputation: 607
Cause your are modifying a different instance of the model.
onChanged: (double value) {
setState(() {
widget.volumeValue = value;
// this should be Provider.of<VolumeModel>(context).changeVolume(value, widget.volumeLabel)
widget.volume.changeVolume(value, widget.volumeLabel);
});
EDIT
based on how you structure your widgets, the SettingsSlider should look something like this
class SettingsSlider extends Stateless {
final double volumeValue;
final String volumeLabel;
SettingsSlider({super.key, required this.volumeValue, required this.volumeLabel});
@override
Widget build(BuildContext context) {
return Slider(
value: volumeValue,
min: 0,
max: 100,
label: volumeValue.round().toString(),
activeColor: const Color(0xffDF9676),
thumbColor: const Color(0xff57CC85),
onChanged: (double value) {
Provider.of<VolumeModel>(context, listen: false).changeVolume(value, volumeLabel)
});
},
);
}
}
then would be a responsibility of you SettingScreen to listen for model updates and pass the right values to its descendant.
Something like
class SettingScreen extends StatelessWidget {
@override
void build(BuildContext context) {
final volume = Provider.of<VolumeModel>(context);
return Column(
children: [
SettingsSlider(volumeLabel: VolumeModel.backgroundMusicKey, volumeValue: volume.currentBackgroundMusicVolume),
SettingsSlider(volumeLabel: VolumeModel.speechAudioKey, volumeValue: volume.currentSpeechAudioVolume),
SettingsSlider(volumeLabel: VolumeModel.soundEffectsKey, volumeValue: volume. currentSoundEffectsVolume),
]
);
...
}
}
Upvotes: 0