簡體   English   中英

如何使用動態加載的異步數據設置多個 StateNotifierProvider?

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

我完全堅持下面的任務。 所以,我們的想法是使用Riverpod解決這些步驟

  1. 在暫停應用程序時使用某種Future async從數據庫中獲取數據(顯示SomeLoadingPage()等)

  2. 數據加載后:

    2.1 初始化多個全局StateNotifierProvider s,它們利用其構造函數中的數據,並且可以通過更新其狀態的方法在整個應用程序中進一步使用。

    2.2 然后顯示MainScreen()和UI的rest

到目前為止,我已經嘗試過這樣的事情:

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;},);}}

我有意使用FutureBuilder而不是.when()因為將來我可能打算將Future.wait([])與多個期貨一起使用

到目前為止這是有效的,但是當我想在UserData中實現某種 update() 方法並通過整個應用程序監聽它的變量時,麻煩就來了。 就像是

  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()出現在每個changeLang()方法調用中。

簡而言之:我真的希望有幾個StateNotifierProvider能夠從內部修改它們的 state 並從外部收聽它。 但是從數據庫中獲取初始 state 並讓整個應用程序等待獲取此數據並初始化這些提供程序。

所以,我想我想出了如何解決這個問題:

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};

  }
}

這對我有用,至少在這個復雜度上是這樣。 如果有更好的建議,我會留下未解決的問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM