I have a flutter screen called TestMain which has a scaffold and white background. The scaffolds body is supposed to change if certain events happen. The events are stored as a boolean. There is "isLocked" and "isPaused" which get emitted by a Riverpod Stream Provider and "isCheating" which changes when Applifecyle events get triggered. All of the three booleans are stored as Riverpod StateProviders, because of its global accesibility.
This is is my "isCheatingProvider":
final isCheatingProvider = StateProvider.autoDispose<bool>((ref) => false);
The "isPausedProvider" and "isLockedProvider" are the same.
This is the TestMain screen
class TestMain extends ConsumerStatefulWidget {
const TestMain({super.key});
@override
ConsumerState<TestMain> createState() => _TestMainScreenState();
}
class _TestMainScreenState extends ConsumerState<TestMain>
with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) async {
super.didChangeAppLifecycleState(state);
final isCheating = ref.watch(isCheatingProvider.notifier);
switch (state) {
case AppLifecycleState.resumed:
case AppLifecycleState.inactive:
await sendCheatingAttempt(ref);
setState(() {
isCheating.state = true;
});
break;
case AppLifecycleState.paused:
await sendCheatingAttempt(ref);
setState(() {
isCheating.state = true;
});
break;
case AppLifecycleState.detached:
await sendCheatingAttempt(ref);
setState(() {
isCheating.state = true;
});
break;
}
}
@override
Widget build(BuildContext context) {
final List<Item> items = ref.watch(itemsProvider);
final AsyncValue<dynamic> wsTestListenerMessage =
ref.watch(testListenerProvider);
final isLocked = ref.watch(isLockedProvider.notifier);
final isPaused = ref.watch(isPausedProvider.notifier);
final isCheating = ref.watch(isCheatingProvider.notifier);
wsTestListenerMessage.when(
loading: () => {},
error: (err, stack) => print('Test State Error: $err'),
data: (message) async {
Future.delayed(const Duration(seconds: 0), () {
if (message["lock"] == true) {
isLocked.state = true;
}
if (message["unlock"] == true) {
isLocked.state = false;
}
if (message["paused"] == true) {
isPaused.state = true;
}
if (message["resumed"] == true) {
isPaused.state = false;
}
});
},
);
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: isPaused.state
? const ErrorOverlay(text: 'paused')
: isLocked.state || isCheating.state
? const ErrorOverlay(text: 'cheating')
: const TestView()),
);
}
}
But it doesnt work. No matter what I do. I added the Future.delayed(const Duration(seconds: 0), () {}
around the if-statements, because it complained about changing the provider in build method, I use setState() in didChangeAppLifecycleState()
, but can't use it in the listener, because the listener would called over and over again. It shouldnt be openend more than once.
(ErrorOverlay is a custom widget that just shows the text in big red letters, in the center)
CodePudding user response:
- remove the
setState
, this will do nothing - for set a state use
ref.read(provider.notifier).state
- for watch use
ref.watch(isCheatingProvider)
By changing all that it is good by testing on my side :
final isCheatingProvider = StateProvider.autoDispose<bool>((ref) => false);
class TestMain extends ConsumerStatefulWidget {
const TestMain({key});
@override
ConsumerState<TestMain> createState() => _TestMainScreenState();
}
class _TestMainScreenState extends ConsumerState<TestMain>
with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) async {
super.didChangeAppLifecycleState(state);
final isCheating = ref.read(isCheatingProvider.notifier);
switch (state) {
case AppLifecycleState.resumed:
case AppLifecycleState.inactive:
isCheating.state = true;
break;
case AppLifecycleState.paused:
isCheating.state = true;
break;
case AppLifecycleState.detached:
isCheating.state = true;
break;
}
}
@override
Widget build(BuildContext context) {
final isCheating = ref.watch(isCheatingProvider);
return Scaffold(
backgroundColor: isCheating ? Colors.red : Colors.white,
body: SafeArea(
child: isCheating ? Text('cheat') : Text(' good')
)
);
}
}
CodePudding user response:
You are incorrectly using StateProvider
. To watch StateNotifier
you should use
final isCheating = ref.watch(isCheatingProvider);
and to change provider use
ref.read(productSortTypeProvider.notifier).state = value;
So you have to change all provider related code.
@override
void didChangeAppLifecycleState(AppLifecycleState state) async {
super.didChangeAppLifecycleState(state);
final isCheatingNotifier = ref.read(isCheatingProvider.notifier);
switch (state) {
case AppLifecycleState.resumed:
case AppLifecycleState.inactive:
await sendCheatingAttempt(ref);
isCheatingNotifier.state = true;
break;
case AppLifecycleState.paused:
await sendCheatingAttempt(ref);
isCheatingNotifier.state = true;
break;
case AppLifecycleState.detached:
await sendCheatingAttempt(ref);
isCheatingNotifier.state = true;
break;
}
}
@override
Widget build(BuildContext context) {
final List<Item> items = ref.watch(itemsProvider);
final AsyncValue<dynamic> wsTestListenerMessage =
ref.watch(testListenerProvider);
final isLocked = ref.watch(isLockedProvider);
final isPaused = ref.watch(isPausedProvider);
final isCheating = ref.watch(isCheatingProvider);
wsTestListenerMessage.when(
loading: () => {},
error: (err, stack) => print('Test State Error: $err'),
data: (message) async {
Future.delayed(const Duration(seconds: 0), () {
final isLockedNotifier = ref.read(isLockedProvider.notifier);
final isPausedNotifier = ref.read(isPausedProvider.notifier);
if (message["lock"] == true) {
isLockedNotifier.state = true;
}
if (message["unlock"] == true) {
isLockedNotifier.state = false;
}
if (message["paused"] == true) {
isPausedNotifier.state = true;
}
if (message["resumed"] == true) {
isPausedNotifier.state = false;
}
});
},
);
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: isPaused
? const ErrorOverlay(text: 'paused')
: isLocked || isCheating
? const ErrorOverlay(text: 'cheating')
: const TestView()),
);
}