跳转至正文

Flutter 中的状态管理

如何使用 ChangeNotifier 管理状态的说明。

学习使用 ChangeNotifier 创建 ViewModel,并管理加载、成功与错误状态。

你将完成的内容

使用 ChangeNotifier 创建 ViewModel
管理加载、成功与错误状态
使用 notifyListeners 通知 UI 更新

步骤

1

介绍

当开发者在 Flutter 中谈论状态管理时,他们本质上指的是应用更新正确渲染所需数据,然后通知 Flutter 用新数据重新渲染 UI 的模式。

在 MVVM 中,这一职责落在 ViewModel 层,它位于 UI 与 Model 层之间并连接两者。在 Flutter 中,ViewModel 使用 Flutter 的 ChangeNotifier 类在数据变化时通知 UI。

要使用 ChangeNotifier,在你的状态管理类中继承它以获得 notifyListeners() 方法的访问权限,调用该方法时会触发 UI 重建。

2

创建基本 view model 结构

创建 ArticleViewModel 类及其基本结构与状态属性:

dart
class ArticleViewModel extends ChangeNotifier {
  final ArticleModel model;
  Summary? summary;
  Exception? error;
  bool isLoading = false;

  ArticleViewModel(this.model);
}

ArticleViewModel 保存三份状态:

  • summary:当前的 Wikipedia 文章数据。

  • error:数据获取过程中发生的任何错误。

  • isLoading:用于显示进度指示器的标志。

3

添加构造函数初始化

更新构造函数,以便在创建 ArticleViewModel 时自动获取内容:

dart
class ArticleViewModel extends ChangeNotifier {
  final ArticleModel model;
  Summary? summary;
  Exception? error;
  bool isLoading = false;

  ArticleViewModel(this.model) {
    fetchArticle();
  }

  // Methods will be added next.
  Future<void> fetchArticle() async {}
}

此构造函数初始化在创建 ArticleViewModel 对象时即可提供内容。由于构造函数不能是异步的,它将初始内容获取委托给单独的方法。

4

设置 fetchArticle 方法

添加用于获取数据并管理状态更新的 fetchArticle 方法:

dart
class ArticleViewModel extends ChangeNotifier {
  final ArticleModel model;
  Summary? summary;
  Exception? error;
  bool isLoading = false;

  ArticleViewModel(this.model) {
    fetchArticle();
  }

  Future<void> fetchArticle() async {
    isLoading = true;
    notifyListeners();

    // TODO: Add data fetching logic

    isLoading = false;
    notifyListeners();
  }
}

ViewModel 会更新 isLoading 属性并调用 notifyListeners() 通知 UI 已更新。操作完成后,它会将该属性切换回来。构建 UI 时,你将使用此 isLoading 属性在获取新文章时显示加载指示器。

5

ArticleModel 获取文章

完善 fetchArticle 方法以获取文章摘要。使用 try-catch block 优雅地处理网络错误并保存 UI 可向用户显示的错误消息。该方法在成功时清除先前的错误,在出错时清除先前的文章摘要以保持一致的状态。

dart
class ArticleViewModel extends ChangeNotifier {
  final ArticleModel model;
  Summary? summary;
  Exception? error;
  bool isLoading = false;

  ArticleViewModel(this.model) {
    fetchArticle();
  }

  Future<void> fetchArticle() async {
    isLoading = true;
    notifyListeners();
    try {
      summary = await model.getRandomArticleSummary();
      error = null; // Clear any previous errors.
    } on HttpException catch (e) {
      error = e;
      summary = null;
    }
    isLoading = false;
    notifyListeners();
  }
}
6

测试 ViewModel

在构建完整 UI 之前,通过将结果打印到控制台来测试 HTTP 请求是否有效。首先,更新 fetchArticle 方法以打印结果:

dart
Future<void> fetchArticle() async {
  isLoading = true;
  notifyListeners();
  try {
    summary = await model.getRandomArticleSummary();
    print('Article loaded: ${summary!.titles.normalized}'); // Temporary
    error = null; // Clear any previous errors.
  } on HttpException catch (e) {
    print('Error loading article: ${e.message}'); // Temporary
    error = e;
    summary = null;
  }
  isLoading = false;
  notifyListeners();
}

然后,更新 MainApp widget 以创建 ArticleViewModel,它在创建时会调用 fetchArticle 方法:

dart
class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    // Instantiate your `ArticleViewModel` to test its HTTP requests.
    final viewModel = ArticleViewModel(ArticleModel());

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Wikipedia Flutter')),
        body: const Center(child: Text('Check console for article data')),
      ),
    );
  }
}

热重载应用并查看控制台输出。你应看到文章标题或错误消息,这确认 Model 与 ViewModel 已正确连接。

7

回顾

你完成的内容

以下是你本课构建与学习内容的摘要。
使用 ChangeNotifier 创建了 ArticleViewModel

ViewModel 位于 UI 与 Model 之间,管理状态并连接两层。通过继承 ChangeNotifier,ViewModel 获得在数据变化时通知监听者的能力。

管理了加载、成功与错误状态

ViewModel 跟踪三份状态: isLoadingsummaryerror。使用 trycatch,你优雅地处理网络错误并为每种可能结果保持一致的状态。

使用 notifyListeners 通知 UI 更新

调用 notifyListeners() 会告诉所有监听的 widget 重建。你在将 loading = true 之后调用一次,在操作完成后再调用一次。这就是在 Flutter 中实现响应式 UI 更新的方式。

8

自测

状态管理测验

1 / 2
ChangeNotifier 是什么?
  1. 向用户显示通知的 widget。

    不正确。

    ChangeNotifier 不是 widget;它是用于管理状态的类。

  2. 当数据变化时可通知监听者、从而实现响应式 UI 更新的类。

    正确!

    ChangeNotifier 提供 notifyListeners 方法,在状态变化时通知 widget 重建。

  3. 用于发送推送通知的内置 Dart 类。

    不正确。

    ChangeNotifier 用于应用内状态管理,而非推送通知。

  4. Flutter 中的一种动画控制器类型。

    不正确。

    动画控制器是独立的;ChangeNotifier 用于状态管理。

在 ChangeNotifier 中调用 notifyListeners() 会做什么?
  1. 将当前状态保存到本地存储。

    不正确。

    notifyListeners() 通知 UI 更新;持久化需要单独实现。

  2. 告诉所有监听的 widget 重建并反映新状态。

    正确!

    调用 notifyListeners() 会触发监听此 ChangeNotifier 的所有 widget 重建。

  3. 将状态变化记录到控制台以便调试。

    不正确。

    它不会记录任何内容;它通知监听者重建。

  4. 将所有状态属性重置为默认值。

    不正确。

    notifyListeners() 不会修改状态;它只是表明状态已变化。