簡體   English   中英

如何通過結合 futureprovider 和 stateprovider 來使用 riverpod?

[英]how to use riverpod, by combing futureprovider with stateprovider?

我正在使用 futureprovider 從 api 獲取響應,但我想從 api 獲取列表並使用 listprovider.state="data from api" 將其分配給 stateprovider,它將如何工作,如何將未來的 provdier 與 state 結合起來提供商。

這是我最近一直在努力思考的話題。

我認為Remi的回答中缺少的是將Future數據轉換為可操作數據的能力。

當您使用FutureProvider接收Future數據並使用when方法或使用FutureBuilder小部件實現 ui 時,它們都會在接收到遠程數據時觸發重建,因此如果您嘗試將值分配給StateProvider將在另一個將拋出的重建期間觸發重建。

我目前有 2 個解決方法,當我獲得更多相關信息時,我將更新我的答案。

對於這個例子,我們將有一個未來的提供者會等待然后返回一個假數據:

final _futureCounterProv = FutureProvider(
  (ref) async {
    Future.delayed(
      Duration(seconds: 3),
    );
    return Random().nextInt(100);
  },
);

1. Future.microtask

Future.microtask使您能夠在當前重建結束后運行操作。

您必須確保您的StateProvider依賴項位於Future.microtask調用下方的Consumer中,否則Future.microtask將在每次 state 更新時被調用,這將不斷將StateProvider的值重置為未來值

// this provider will provide the current value of the counter

final _counterProv = StateProvider((ref) => 0);

class Body extends ConsumerWidget {
  const Body({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return ref.watch(_futureCounterProv).when(
      loading: () {
        return const Center(
          child: CircularProgressIndicator(),
        );
      },
      error: (error, stackTrace) {
        return Text(error.toString());
      },
      data: (data) {
        Future.microtask(
          () {
            // Assigning the future value to the `StateProvider`
            return ref.read(_counterProv.notifier).state = data;
          },
        );
        return Consumer(
          builder: (context, ref, _) {
            final count = ref.watch(_counterProv);

            return Column(
              children: [
                IconButton(
                  onPressed: () {
                    ref
                        .read(_counterProv.notifier)
                        .update((value) => value + 1);
                  },
                  icon: const Icon(Icons.add),
                ),
                Text(
                  count.toString(),
                ),
              ],
            );
          },
        );
      },
    );
  }
}

2. ChangeNotifierProvider

StateProvider有 2 個選項來更新其值: value設置器和update方法,它們都會觸發重建。 在此解決方法中,我們希望實施不觸發重建的 state 更新。 一種方法是使用ChangeNotifierProvider而不是StateProvider 通過使用ChangeNotifierProvider ,我們可以控制我們自己的更新操作並在需要時調用notifyListeners (這將觸發重建)。

您必須確保您的ChangeNotifierProvider依賴項位於updateNoNotify調用下方的 Consumer 中,否則ChangeNotifierProvider的依賴項將繼續重置為未來的值。 此外,您還必須確保所有使用此ChangeNotifierProvider的小部件都在updateNoNotify下方的小部件樹中,否則它們將不會被重建,因為我們沒有觸發重建

// the new `_counterProv`
final _counterProv = ChangeNotifierProvider(
  (ref) => _CounterNotifier(),
);

class _CounterNotifier extends ChangeNotifier {
  int _value = 0;

  int get value => _value;

  void update(int Function(int value) update) {
    _value = update(_value);
    // trigger a rebuild
    notifyListeners();
  }

  void updateNoNotify(int Function(int value) update) {
    _value = update(_value);
  }
}

// the ui
class Body extends ConsumerWidget {
  const Body({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return ref.watch(_futureCounterProv).when(
      loading: () {
        return const Center(
          child: CircularProgressIndicator(),
        );
      },
      error: (error, stackTrace) {
        return Text(error.toString());
      },
      data: (data) {
        // calling `updateNoNotify` which does not trigger
        // trigger rebuild as it does not call `notifyListeners`
        ref.read(_counterProv.notifier).updateNoNotify(
              (e) => data,
            );
        return Consumer(
          builder: (context, ref, _) {
            final count = ref.watch(_counterProv).value;

            return Column(
              children: [
                IconButton(
                  onPressed: () {
                    ref.read(_counterProv.notifier).update(
                          (value) => value + 1,
                        );
                  },
                  icon: const Icon(Icons.add),
                ),
                Text(
                  count.toString(),
                ),
              ],
            );
          },
        );
      },
    );
  }
}

這些不是safest的解決方法,但它們是workaround ,一旦找到安全的方法,我將更新此答案。

暫無
暫無

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

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