RiverPod 使用记录

3,267 阅读3分钟

image.png

一直被riverpod洗脑,最后投降了。完整的学习了下,然后在项目中实战了。整体来说好用,比provider方便,也解决之前一直的困扰(监听值变化来执行事件)。

1. widget替换

  1. StatelessWidget替换为ConsumerWidget
  2. StatefulWidgetState替换为ConsumerStatefulWidgetConsumerState
  3. Consumer可以直接在widget tree中使用。

2. ref的方法 (ref就是ProviderElementBase)

  1. ref.watch 获取一个provider的值并监听变化,等值变化时重建widget。
  2. ref.listen 添加一个监听到provider上,当值变化的时候执行一个动作,如打开一个新页面或展示一个新内容。(事件触发用listen)
  3. ref.read获取一个provider的值并忽略值的改变。如在点击事件中获取一个provider的值。

注意点

1. ref.watch

  • 多个provider联用,可以在provider通过ref.watch其他的provider来做到自动更新。
  • 不应该在async异步中调用,比如在button的onPresssed中。
  • 不应该在initState或其他state生命周期中调用。

2. ref.listen

  • 传入2个参数ref和(pre, new)回调函数。可在回调函数中进行操作。
  • 可在一个provider或build中。
  • 不应该在async异步中调用,比如在button的onPresssed中。
  • 不应该在initState或其他state生命周期中调用。

3. ref.read

  • 获取数据。通常在用户交互中使用。
  • 尽量避免使用ref.read,因为他不是反应式的。(即数据变化的时候,不会跟随的变化)
  • 不要在build中使用。

4. ref.refresh

  • 使之前的provider失效,然后重新获取数据。 (不是立刻重新获取数据,而是等下一次读取时刷新)
  • 重新获取数据不需要返回值时,可使用invalidate.
final productsProvider = FutureProvider((ref) async {
  final response = await httpClient.get('https://host.com/products');
  return Products.fromJson(response.data);
});

class Example extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final Products products = ref.watch(productsProvider);
    return RefreshIndicator(
      onRefresh: () => ref.refresh(productsProvider.future),
      child: ListView(
        children: [
          for (final product in products.items) ProductItem(product: product),
        ],
      ),
    );
  }
}

4. ref.invalidate

使provider失效,可以调用很多次,只刷新一次provider。

需要返回state时,使用refresh方法。

3. 使用notifier进行值的更改

使用ref.readref.watch都可以实现。

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

Widget build(BuildContext context, WidgetRef ref) {
  StateController<int> counter = ref.watch(counterProvider.notifier);
  return ElevatedButton(
    onPressed: () => counter.state++,
    child: const Text('button'),
  );
}

简单修改

ref.read(pageIndexProvider.notifier).update((state)=>state-1);

4. 监听哪个值

根据不同的需求,可以监听不同的值。如StreamProvider的使用:final userProvider=StreamProvider<User>(...);

  • AsyncValue<User>user=ref.watch(userProvider);监听本身
  • Stream<User>user=ref.watch(userProvider.stream);监听stream
  • Future<User>user=ref.watch(userProvider.future); 监听future

5. select

可在ref.watch和ref.listen中使用。

Widget build(BuildContext context, WidgetRef ref) {
  String name = ref.watch(userProvider.select((user) => user.name));
  return Text(name);
}

监听某一个值,从而减少build次数。

6. 组合provider

provider通过watch另一个provider进行操作。final cityProvider = Provider((ref) => 'London');

监听cityProvider进行获取数据

final weatherProvider = FutureProvider((ref) async {
 final city = ref.watch(cityProvider);
 return fetchWeather(city: city);
});

7. family

传入一个额外的参数来创建state。

final messagesFamily = FutureProvider.family<Message, String>((ref, id) async {
  return dio.get('http://my_api.dev/messages/$id');
});

使用

Widget build(BuildContext context, WidgetRef ref) {
 final response = ref.watch(messagesFamily('id'));
}

8. autoDispose

Provider不使用时则销毁,解决内存泄露问题。

9. Provider

  • 缓存计算结果。
  • 减少重建次数。
  1. StateNotifierProvider 包装StateNotifier并暴漏state,可通过watch进行观察。
  2. FutureProvider 参数变化会自动重新获取数据,并一直拥有最新的数据。
  3. StreamProvider 同FutureProvider,可监听流。
  4. StateProvider state可以为(1)枚举(2)字符串(3)布尔值(4)数值。 其他复杂的就是用StateNotifierProvider.
  5. ChangeNotifierProvider (不推荐使用)

9.1 StateNotifierProvider

用法和stateProvider一样,用于比较复杂的State。

final StateNotifierProvider<Counter, int> stateNotifierProvider = StateNotifierProvider<Counter, int>((_) => Counter());

class Counter extends StateNotifier<int> {
  Counter() : super(0);

  void increment() => state++;

  void decrement() => state--;

  @override
  String toString() {
    return 'state:$state';
  }
}

9.2 ChangeNotifierProvider

ChangeNotifier和StateNotifier的区别是,需要自己调用notifyListeners通知变更。


final ChangeNotifierProvider<Counter> _counterProvider = ChangeNotifierProvider((_) => Counter());

class Counter extends ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }

  void decrement() {
    _count--;
    notifyListeners();
  }
}

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('ChangeNotifierProvider Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Consumer(
              builder: (context, watch, _) {
                int count = watch.watch(_counterProvider).count;
                return Text(
                  '$count',
                  style: Theme.of(context).textTheme.headline4,
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        /// 使用read获取counterProvider。
        onPressed: () => ref.read(_counterProvider).increment(),
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

10. ProviderObserver

监听一个ProviderContainer的变化。

  • didAddProvider:在每次初始化一个Provider时被调用
  • didDisposeProvider:在每次销毁Provider的时候被调用
  • didUpdateProvider:每次在Provider更新时都会被调用

内容来自官网或其他地方(看的文章太多忘记地址了),侵删。