I'm completely stuck with the task below.
So, the idea is to solve these steps using Riverpod
Fetch data from db with some kind of
Future async
while pausing the app (displaySomeLoadingPage()
etc.)Once the data has loaded:
2.1 initialize multiple global
StateNotifierProvider
s which utilize the data in their constructors and can further be used throughout the app with methods to update their states.2.2 then show
MainScreen()
and the rest of UI
So far I've tried something like this:
class UserData extends StateNotifier<AsyncValue<Map>> { // just <Map> for now, for simplicity
UserData() : super(const AsyncValue.loading()) {
init();
}
Future<void> init() async {
state = const AsyncValue.loading();
try {
final HttpsCallableResult response =
await FirebaseFunctions.instance.httpsCallable('getUserData').call();
state = AsyncValue.data(response.data as Map<String, dynamic>);
} catch (e) {
state = AsyncValue.error(e);
}}}
final userDataProvider = StateNotifierProvider<UserData, AsyncValue<Map>>((ref) => UserData());
final loadingAppDataProvider = FutureProvider<bool>((ref) async {
final userData = await ref.watch(userDataProvider.future);
return userData.isNotEmpty;
});
class LoadingPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return FutureBuilder(
future: ref.watch(loadingAppDataProvider.future),
builder: (ctx, AsyncSnapshot snap) {
// everything here is simplified for the sake of a question
final Widget toReturn;
if (snap.connectionState == ConnectionState.waiting) {
toReturn = const SomeLoadingPage();
} else {
snap.error != null
? toReturn = Text(snap.error.toString())
: toReturn = const SafeArea(child: MainPage());
}
return toReturn;},);}}
I intentionally use FutureBuilder
and not .when()
because in future i may intend to use Future.wait([])
with multiple futures
This works so far, but the troubles come when I want to implement some kind of update() methods inside UserData
and listen to its variables through the entire app. Something like
late Map userData = state.value ?? {};
late Map<String, dynamic> settings = userData['settings'] as Map<String, dynamic>;
void changeLang(String lang) {
print('change');
for (final key in settings.keys) {
if (key == 'lang') settings[key] = lang;
state = state.whenData((data) => {...data});
}
}
SomeLoadingPage()
appears on each changeLang()
method call.
In short:
I really want to have several StateNotifierProvider
s with the ability to modify their state from the inside and listen to it from outside. But fetch the initial state from database and make the intire app wait for this data to be fetched and these providers to be initilized.