Firstly, I have a class with extends StateNotifier<bool>
named ButtonStateNotifier
:
class ButtonStateNotifier extends StateNotifier<bool> {
ButtonStateNotifier() : super(true);
void isButtonEnabled({bool isEnabled = true}) {
state = isEnabled;
}
}
secondly, I have a Provider:
final buttonProvider =
StateNotifierProvider<ButtonStateNotifier, bool>((ref) {
return ButtonStateNotifier();
});
Lastly, I have a pre-defined function for showDialog
:
class BasicClass {
void dialogFunction(
BuildContext context,
String? title,
String contentText,
Widget firstAction,
Widget secondAction,
) async {
final Brightness brightness = MediaQuery.of(context).platformBrightness;
final bool isDarkMode = brightness == Brightness.dark;
return showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor:
isDarkMode ? Colors.grey.shade900 : Colors.grey.shade100,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
title: title != null
? Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
title,
),
)
: null,
content: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(contentText),
),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
firstAction,
const SizedBox(width: 10),
secondAction,
],
),
],
),
).then((_) {
ProviderContainer()
.read(buttonProvider.notifier)
.isButtonEnabled();
});
}
}
NOTE: I am watching the provider, by the way, whereas I was using it as follows:
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(buttonProvider);
}
I added some breakpoints on:
ProviderContainer()
.read(checkButtonStateProvider.notifier)
.isButtonEnabled();
Surprisingly, the nested function isButtonEnabled
is invoked:
void isButtonEnabled({bool isEnabled = true}) {
state = isEnabled; // true
}
But when I checked the:
final state = ref.watch(buttonProvider);
It remains false
.
There's no lint or runtime error in the code; the only problem here is in the BasicClass
where I can't invoke the read()
function. I thought I could achieve it by using ProviderContainer()
to invoke the read()
function.
My goal is to invoke the read()
function inside the basic class instance, particularly when the showDialog
is disposed.
Invoking the read(buttonProvider.notifier).isButtonEnabled()
function will change the state from false
to true
.
I needed to achieve this approach, if possible, because I had already used the given showDialog
in other classes.
As far as I know, using WidgetRef
to use ref
(which is usually found in the Riverpod documentation) is not allowed here, as I'm obviously not using ConsumerWidget
or ConsumerStatefulWidget
in the given BasicClass
instance.
Am I missing something here, or did I mistakenly invoke the read()
function using ProviderContainer()
?
Passing ref
from view into function instead of using ProviderContainer()
means you're letting the existing context (provided by Flutter and Riverpod) manage state instead of manually creating a new, isolated container.
Here is a minimal production code for your issue.
class MyHomePage extends ConsumerStatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
ConsumerState<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends ConsumerState<MyHomePage> {
void _showBaseClassDialog(WidgetRef ref) {
BasicClass().dialogFunction(context, ref, 'title', 'contentText', const Text('firstAction widget'), const Text('secondAction widget'));
}
@override
Widget build(BuildContext context) {
final state = ref.watch(buttonProvider);
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(state.toString()),
FloatingActionButton(
onPressed: () => _showBaseClassDialog(ref),
tooltip: 'TAP ME!!!',
child: const Icon(Icons.add),
),
],
),
),
);
}
}
class ButtonStateNotifier extends StateNotifier<bool> {
ButtonStateNotifier() : super(true);
void isButtonEnabled({bool isEnabled = true}) {
state = isEnabled;
print('debug print [state]: $state');
}
}
final buttonProvider = StateNotifierProvider<ButtonStateNotifier, bool>((ref) {
return ButtonStateNotifier();
});
class BasicClass {
void dialogFunction(
BuildContext context,
WidgetRef ref,
String? title,
String contentText,
Widget firstAction,
Widget secondAction,
) async {
final Brightness brightness = MediaQuery.of(context).platformBrightness;
final bool isDarkMode = brightness == Brightness.dark;
return showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor:
isDarkMode ? Colors.grey.shade900 : Colors.grey.shade100,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
title: title != null
? Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
title,
),
)
: null,
content: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(contentText),
),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
firstAction,
const SizedBox(width: 10),
secondAction,
],
),
],
),
).then((_) {
print('debug print [showDialog.then]');
ref.read(buttonProvider.notifier).isButtonEnabled(isEnabled: false);
});
}
}