'How to update state in a fcm handler with StateNotifierProvider?
In my Flutter app i need being able to react on incoming fcm messages which can instruct the app to go a different navigation tab by having a corresponding key/value pair in its data payload. Currently the selected index is stored in the stateful widget which also hosts the bottom navigation bar:
@override
Widget build(BuildContext context) {
presentTicketsModel = ref.watch(presentTicketsModelProvider);
contractsModel = ref.watch(contractsModelProvider);
return Scaffold(
body: PersistentTabs(
currentTabIndex: index,
screenWidgets: buildPages(),
),
bottomNavigationBar: NavigationBar(
height: 60,
selectedIndex: index,
onDestinationSelected: (index) => setState(() {
this.index = index;
}),
destinations: _buildNavigationDestinations(),
),
);
}
With the new challenge i thought about moving that state index into a separate object and use Riverpod's StateNotifierProvider to provide that state object, as it is described in the official doc (https://riverpod.dev/docs/providers/state_notifier_provider).
What i don't get is: How can the following service class (which listens for incoming fcm messages) get hold of that state object and update the index in order that the watching view class gets notified and can switch to the targeted navigation tab?
class PushNotificationService {
final fcm = FirebaseMessaging.instance;
Future initialise() async {
print('initialising push notification service...');
}
/// foreground handler
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('onMessage: $message');
// here the state change would be done
});
/// handler if the app has been opened from a background state
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print('onMessageOpenedApp: $message');
});
}
}
Or asked differently: How does this service class get passed a ProviderReference in order to access the mentioned state object and change the index value?
The service currently is registered with GetIt as a lazy singleton:
GetIt locator = GetIt.instance;
void setupLocator() {
locator.registerLazySingleton(PushNotificationService.new);
}
Solution 1:[1]
The initialise
method can have a Ref
parameter like so:
class PushNotificationService {
final fcm = FirebaseMessaging.instance;
Future initialise(WidgetRef ref) async {
print('initialising push notification service...');
}
/// foreground handler
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
// Use ref.read here -> ref.read();
});
/// handler if the app has been opened from a background state
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print('onMessageOpenedApp: $message');
});
}
}
Then you can pass a ref when calling initialise.
PushNotificationService.initialise(ref);
If you'd be calling
initialise
in a widget, useWidgetRef
instead ofRef
EDIT: Where to pass ref (notice that we're using WidgetRef
now)
Follow these steps
- Make
MyApp
aConsumerStatefulWidget
- Call
PushNotificationService.initialise(ref);
ininitState
Full code:
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends ConsumerStatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
ConsumerState<ConsumerStatefulWidget> createState() => _MyAppState();
}
class _MyAppState extends ConsumerState<MyApp> {
@override
initState(){
PushNotificationService.initialise(ref);
super.initState();
}
class _MyAppState extends ConsumerState<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "App",
home: Home(),
);
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |