简体   繁体   English

如何使用动态加载的异步数据设置多个 StateNotifierProvider?

[英]How to set multiple StateNotifierProvider (s) with dynamicaly loaded async data?

I'm completely stuck with the task below.我完全坚持下面的任务。 So, the idea is to solve these steps using Riverpod所以,我们的想法是使用Riverpod解决这些步骤

  1. Fetch data from db with some kind of Future async while pausing the app (display SomeLoadingPage() etc.)在暂停应用程序时使用某种Future async从数据库中获取数据(显示SomeLoadingPage()等)

  2. 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.1 初始化多个全局StateNotifierProvider s,它们利用其构造函数中的数据,并且可以通过更新其状态的方法在整个应用程序中进一步使用。

    2.2 then show MainScreen() and the rest of UI 2.2 然后显示MainScreen()和UI的rest

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我有意使用FutureBuilder而不是.when()因为将来我可能打算将Future.wait([])与多个期货一起使用

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.到目前为止这是有效的,但是当我想在UserData中实现某种 update() 方法并通过整个应用程序监听它的变量时,麻烦就来了。 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. SomeLoadingPage()出现在每个changeLang()方法调用中。

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.简而言之:我真的希望有几个StateNotifierProvider能够从内部修改它们的 state 并从外部收听它。 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.但是从数据库中获取初始 state 并让整个应用程序等待获取此数据并初始化这些提供程序。

So, I guess I figured how to solve this:所以,我想我想出了如何解决这个问题:

final futureExampleProvider = FutureProvider<Map>((ref) async {
  final HttpsCallableResult response =
  await FirebaseFunctions.instance.httpsCallable('getUserData').call();

  return response.data as Map;
});

final exampleProvider = StateNotifierProvider<Example, Map>((ref) {

  // we get AsyncValue from FutureNotifier
  final data = ref.read(futureExampleProvider);
  // and wait for it to load
  return data.when(
    // in fact we never get loading state because of FutureBuilder in UI
    loading: () => Example({'loading': 'yes'}),
    error: (e, st) => Example({'error': 'yes'}),
    data: (data) => Example(data),
  );
});
class LoadingPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return FutureBuilder(
      // future: ref.watch(userDataProvider.future),
      future: ref.watch(futureExampleProvider.future),
      builder: (ctx, AsyncSnapshot snap) {
        final Widget toReturn;
        if (snap.data != null) {
          snap.error != null
          ? toReturn = Text(snap.error.toString())
          : toReturn = const SafeArea(child: MainPage());
        } else {
          // this is the only 'Loading' UI the user see before everything get loaded
          toReturn = const Text('loading');
        }
        return toReturn;
      },
    );
  }
}
class Example extends StateNotifier<Map> {
  Example(this.initData) : super({}) {
    // here comes initial data loaded from FutureProvider
    state = initData;
  }
  // it can be used further to refer to the initial data, kinda like cache
  Map initData;

  // this way we can extract any parts of initData
  late Map aaa = state['bbb'] as Map

  // this method can be called from UI
  void ccc() {
    
    // modify and update data
    aaa = {'someKey':'someValue'};
    // trigger update
    state = {...state};

  }
}

This works for me, at least on this level of complexity.这对我有用,至少在这个复杂度上是这样。 I'll leave question unsolved in case there are some better suggestions.如果有更好的建议,我会留下未解决的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用 StateNotifierProvider 中的 StreamProvider? - How do I use a StreamProvider from a StateNotifierProvider? 如何使用 StateNotifierProvider 在 fcm 处理程序中更新 state? - How to update state in a fcm handler with StateNotifierProvider? 如何使用 Riverpods StateNotifierProvider 处理复杂状态 - How to handle complex state with Riverpods StateNotifierProvider Riverpod 测试:如何使用 StateNotifierProvider 模拟 state? - Riverpod Testing: How to mock state with StateNotifierProvider? 如何在不传递值的情况下读取 StateNotifierProvider.family? - How to read StateNotifierProvider.family without passing value? Flutter 小部件,如何使用多个异步 Future,。 数据加载问题 - Flutter widgets, how to use multiple async Future,. data loading questions 如何将 TextEditingController 分配给 model 中动态创建的文本字段并从 flutter 中的 TextEditingController 检索数据 - How to assign TextEditingController to dynamicaly created textfield in model and retrieve data from TextEditingController in flutter 如何在 Firestore 中将多个数据设置为数组 - How to Set Multiple Data as Array in Firestore 如何确保在Widget构建之前已加载异步功能? - How to make sure that async function is loaded before Widget build? Flutter Navigator 弹出和异步多字段数据 - Flutter Navigator pop and async multiple fields data
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM