In the following Flutter code, a ValueListenableBuilder
is used to show a dialog when a ValueNotifier
changes. However, when the same value is set again, the dialog is not triggered.
ValueNotifier is only notifying listeners when the value changes. The goal is to trigger the dialog even if the same value (true) is set multiple times.
How can the dialog be triggered even if the same value (true) is set again to adWatchNeeded?
Lets assume a simple view model class like below, OR simple try it on here
Important: No state management solutions needed like bloc, riverpod etc. This is just for experimental purpose
class FeatureViewModel extends ChangeNotifier {
ValueNotifier<bool> adWatchNeeded = ValueNotifier(false);
void useFeature() {
// Some API calls..
// Unfortunately user must watch ads to use feature..
adWatchNeeded.value = true;
}
}
Then use on UI side
Widget _buildAdWatchNeededDialog() {
return ValueListenableBuilder<bool>(
valueListenable: _viewModel.adWatchNeeded,
builder: (context, adWatchNeeded, child) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (adWatchNeeded) {
showAdWatchNeededDiaog(context);
}
});
return const SizedBox.shrink();
},
);
}
Then use it on tree
class SomeFeatureWidget extends StatefulWidget {
const SomeFeatureWidget({Key? key}) : super(key: key);
@override
State<SomeFeatureWidget> createState() => _SomeFeatureWidgetState();
}
class _SomeFeatureWidgetState extends State<SomeFeatureWidget> {
final _viewModel = FeatureViewModel();
@override
Widget build(BuildContext context) {
return Column(
children: [
_buildFeatureUsageDialog(),
_buildAdWatchNeededDialog(),
],
);
}
Widget _buildFeatureUsageDialog() {
return TextButton(onPressed: () => _viewModel.useFeature(), child: const Text('Use Feature'));
}
Widget _buildAdWatchNeededDialog() {
return ValueListenableBuilder<bool>(
valueListenable: _viewModel.adWatchNeeded,
builder: (context, adWatchNeeded, child) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (adWatchNeeded) {
showAdWatchNeededDiaog(context);
}
});
return const SizedBox.shrink();
},
);
}
void showAdWatchNeededDiaog(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: const Text('Wath ads to use this feature'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Watch Ad')),
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('No, thanks'))
],
);
},
);
}
}
2
Answers
This is how it is supposed to work, by definition. If you need events even when the value is identical, consider using the events of a Stream, or including a microsecond timestamp in your value.
the easiest way is to write your own version of
ValueNotifier
just copy the original sources of
ValueNotifier
from here (it’s ~25 lines of code) and simply remove theif
that checks if a new value is different that the old one inset value(T newValue)
setterit should look, more or less, something like this: