什么是 Riverpod,Flutter 最重要的状态管理介绍!

在前端系统中,“状态管理”非常重要。

之前介绍的“StatefulWidget”也是执行状态管理的功能之一,
但是,在实现具有多个屏幕和功能的应用程序时,需要管理的状态也会增加,这使得管理变得更加困难。

Riverpod是一个能够简化状态管理的优秀包。本文将介绍Riverpod。

本文介绍的Riverpod是v2版本。

什么是Riverpod

Riverpod是一个用于定义全局状态(State)的工具。

对于变量或常量,通过改变定义的范围,可以相对容易地定义全局变量。

但是,在前端系统中使用的状态(State)与Widget紧密相关,
状态的变化需要重新构建Widget,因此不是那么简单。
而且,当多个Widget需要引用相同的状态信息时,情况会变得更加复杂。

Riverpod就是一个可以简化这一过程的工具。

尽管Riverpod不仅仅是用于全局变量的工具,但本文将仅进行基本介绍。

关于状态管理

在介绍Riverpod之前,先来了解一下状态管理。

状态(State)

简单来说,状态(State)是影响屏幕上信息的变量。

例如,登录用户信息、待办事项列表信息、
复选框的选中状态等,这些影响屏幕显示的数据都是状态(State)。

Widget会监视这些状态,并在每次变化时重新构建屏幕。

状态管理

状态管理是指适当更新或删除这些状态,并在屏幕上表示这些状态的过程。
状态分为本地状态和全局状态。

  • 本地状态
    仅在一个屏幕或一个Widget中使用的状态。
    例如,仅在该屏幕中使用的文本字段或计数器的值等。
  • 全局状态
    在应用程序中共享的状态。
    例如,在首页或用户信息页面显示的用户信息等。

Flutter提供了“StatefulWidget”来进行状态管理。
但是,尝试使用“StatefulWidget”管理全局状态会变得复杂。

如果需要在其他屏幕传递状态或在其他屏幕更新状态,
则需要实现某些功能,如在使用状态之前进行同步。

使用Riverpod,Provider负责监视状态,并自动将影响传递给相关Widget,
因此不需要实现上述同步功能。

关于Riverpod

Riverpod是一个旨在简化Flutter中的状态管理的开源包。它通过以下功能进行状态管理:

  • Notifier
    通知者(Notifier)负责管理目标状态和更新删除的逻辑。
    它负责更新状态和将状态变更通知给Widget。
  • Provider
    Widget使用提供者(Provider)来引用或更新通知者(Notifier)的值。

使用Riverpod,像上述图像中一样,WidgetA的更新也会传递到WidgetB!

使用Riverpod(概要)

使用Riverpod时,需要准备以下内容:

  1. 状态类
  2. 通知者(Notifier)
  3. 提供者(Provider)

关于“3. 提供者(Provider)”,如果是v2.0及以后版本,可以通过“Riverpod Generator”自动生成。

Riverpod的基本使用方法

以待办事项应用为例,介绍基本使用方法。

配置文件

在pubspec.yaml中添加以下包。

dependencies:
  flutter_riverpod:
  riverpod_annotation:
  freezed_annotation:

dev_dependencies:
  build_runner:
  riverpod_generator:
  

Riverpod相关的包
flutter_riverpod:用于在Flutter中使用Riverpod的包
riverpod_generator:自动生成Riverpod的Provider的包
riverpod_annotation:用于判断riverpod_generator中使用的注释的包

其他包
build_runner:实际进行代码自动生成的包
freezed:创建不变类的包
freezed_annotation:用于判断freezed中使用的注释的包

状态类的定义

作为状态使用的类需要是不变(Immutable)类。
可以使用freezed等工具定义不变类,如下所示:

import 'package:freezed_annotation/freezed_annotation.dart';

part 'todo.freezed.dart';

@freezed // freezed注释
class Todo with _$Todo {
  factory Todo({
    required String id,
    required String description,
    required bool completed,
  }) = _Todo;
}

不变的好处是什么?

不变类是指无法更新值的类。
也就是说,一旦创建就无法更改其值。听起来有些不便。

非不变类的实例的数据传递是“引用传递”。
将实例传递给其他类或函数时,传递的不是值本身,而是包含值的地址值。

如果更新了作为参数传递给类或函数的实例的值,调用方的实例的值也会被更新。

这本身似乎适用于状态管理,但引用传递的麻烦之处在于,
即使是作为只读传递的实例,也可以更新地址值的值,可能会导致意外的更新。
此外,这种更新不会产生错误,因此无法检测。

例如,如果复制实例并对其进行排序,复制源也会被排序,这样的意外更新就会发生。

Riverpod希望通过使用不变类管理状态来阻止这种意外变更,只在有意更改时才更新状态。

Notifier的定义

根据上述定义的类定义Notifier。
在这里,描述更新状态和添加新状态的处理。

import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'todo.dart';

// 按照以下步骤执行build runner,将创建此文件
part 'todo_notifier.g.dart';

@riverpod
class TodoNotifier extends _$TodoNotifier {
  @override
  List<Todo> build() {
    return [];
  }

  // 添加新项
  void addTodo(Todo todo) {
    state = [...state, todo];
  }

  // 删除
  void removeTodo(String todoId) {
    state = [
      for (final todo in state)
        if (todo.id != todoId) todo,
    ];
  }

  // 切换“completed”的状态
  void toggle(String todoId) {
    state = [
      for (final todo in state)
        if (todo.id == todoId)
          todo.copyWith(completed: !todo.completed)
        else
          todo,
    ];
  }
}

本文仅介绍了Riverpod的概要,因此省略了详细的处理说明。

执行Riverpod Generator

在终端中执行以下命令运行build_runner:

flutter pub run build_runner build --delete-conflicting-outputs

然后,Riverpod Generator将启动,并创建〜.d.dart文件。
由于上述命令也会启动freezed,因此也会创建〜.freezed.dart文件。

Riverpod Generator将创建Provider。
查看创建的〜.d.dart文件,您将看到定义了Provider。

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'todo_notifier.dart';

// **************************************************************************
// RiverpodGenerator
// **************************************************************************

String _$todoNotifierHash() => r'1330ee68f148d9ff18b1e0370e3f3541ab67c621';

@ProviderFor(TodoNotifier)
final todoNotifierProvider =
    AutoDisposeNotifierProvider<TodoNotifier, List<Todo>>.internal(
  TodoNotifier.new,
  name: r'todoNotifierProvider',
  debugGetCreateSourceHash:
      const bool.fromEnvironment('dart.vm.product') ? null : _$todoNotifierHash,
  dependencies: null,
  allTransitiveDependencies: null,
);

typedef _$TodoNotifier = AutoDisposeNotifier<List<Todo>>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

使用Riverpod的Widget

使用Riverpod时,需要声明使用范围。

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'todo_item.dart';
import 'todo_notifier.dart';

void main() {
  // Riverpodを使用するスコープを"ProviderScope"で宣言
  runApp(ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: TodoItemListPage(),
    );
  }
}

// 使用“ConsumerWidget”定义使用Riverpod的Widget
class TodoItemListPage extends ConsumerWidget {
  @override
  // “WidgetRef ref”是使用Provider的上下文
  Widget build(BuildContext context, WidgetRef ref) {
    // “ref.watch”监视Provider,并检测状态变更。
    List<TodoItem> todoItems = ref.watch(todoNotifierProvider);

    return Scaffold(
      appBar: AppBar(title: Text('todoItem List')),
      body: ListView.builder(
        itemCount: todoItems.length,
        itemBuilder: (context, index) {
          final todoItem = todoItems[index];
          return ListTile(
            title: Text(todoItem.title),
            leading: Checkbox(
              value: todoItem.completed,
              onChanged: (bool? newValue) {
                // “ref.read”从Provider中调用Notifier的方法。
                ref.read(todoNotifierProvider.notifier).toggle(todoItem.id);
              },
            ),
            onLongPress: () => ref.read(todoNotifierProvider.notifier).removeTodo(todoItem.id),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // addTodo
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

“ref”是reference(引用)的缩写。
Widget到状态的引用路径是ref→Provider→Notifier→State。

※上述处理中未使用Notifier的“addTodo”。

执行屏幕

执行后,如下所示,将显示初始数据。
点击复选框或长按列表项,状态将发生变化。

完成!!

最后

本文介绍了Riverpod的基本内容。Riverpod具有多种功能,希望未来能介绍这些功能。

希望本文能帮助您理解Riverpod。 感谢您的阅读!!


タイトルとURLをコピーしました