题记

从配置,到表单、到网络请求,已经写了好几篇
今天就是总结加上填坑。
之前写简单表单的时候使用了Provider状态管理,没有说明,本次也是对Provider状态管理的记录。

什么是状态管理

在 Flutter 中,状态(State) 是指在应用运行时可能发生变化的数据。例如:

  • 用户输入的文本
  • 购物车中的商品数量
  • 当前选中的 Tab 页面
  • 从网络加载的数据

Flutter 的 UI 是声明式的:当状态发生变化时,框架会重新构建受影响的 Widget 树,以更新界面。

状态管理 的本质是:
高效、安全地在 Widget 树的不同层级之间共享和更新状态,同时避免不必要的重建,提升性能并保持代码清晰。

为什么选择 Provider?

  • 官方推荐(flutter.dev 文档明确推荐)
  • 无需 Boilerplate 代码
  • 自动处理 Widget 的生命周期(避免内存泄漏)
  • 支持多种状态共享方式(ChangeNotifier、ValueNotifier、Future、Stream 等)
  • 与 devtools 完美集成,便于调试
  • 学习成本低,5 分钟即可上手

安装Provider

flutter pub add provider

看到pubspec.yaml 文件中已经存在了。
在这里插入图片描述

声明数据仓库

它是负责存放数据和更新数据的地方。

import 'package:flutter/material.dart';
import 'package:harmony_sqllite_floor_demo/models/user_model.dart';
import 'package:uuid/uuid.dart';

/// 用户数据状态管理类
/// 继承自 [ChangeNotifier] 以支持 Provider 的状态更新通知机制
class UserProvider extends ChangeNotifier {
  // 内部私有的用户列表,存放在内存中
  final List<User> _users = [];

  // 用于生成唯一标识符的服务
  final _uuid = const Uuid();

  /// 获取所有用户(只读)
  /// 使用 List.unmodifiable 确保外部不能直接通过 users.add() 修改列表,
  /// 必须通过本类提供的方法来更新数据,从而保证 notifyListeners() 能被触发。
  List<User> get users => List.unmodifiable(_users);

  /// 添加新用户
  /// [account] 账号, [password] 密码, [nickname] 昵称, [remarks] 备注, [option] 标签选项
  void addUser({
    required String account,
    required String password,
    required String nickname,
    required String remarks,
    required UserOption option,
  }) {
    final user = User(
      id: _uuid.v4(), // 自动生成随机 ID
      account: account,
      password: password,
      nickname: nickname,
      remarks: remarks,
      option: option,
    );
    _users.add(user);

    // 📢 极其重要:通知所有正在监听(Consumer)的 UI 界面进行刷新
    notifyListeners();
  }

  /// 更新用户信息
  /// 通过 [updatedUser.id] 查找并替换现有的用户对象
  void updateUser(User updatedUser) {
    final index = _users.indexWhere((u) => u.id == updatedUser.id);
    if (index != -1) {
      _users[index] = updatedUser;

      // 通知 UI 界面,该用户信息已变更
      notifyListeners();
    }
  }

  /// 删除用户
  /// 根据 [id] 从列表中移除匹配的用户
  void deleteUser(String id) {
    _users.removeWhere((u) => u.id == id);

    // 通知 UI 界面,列表已变短,需要重新渲染
    notifyListeners();
  }
}

注册这个仓库

你必须在 App 启动时告诉系统:“这是我的仓库, 任何风吹草动必须记录在案,同时通知给我”。

runApp(
  MultiProvider(
    providers: [
      ChangeNotifierProvider(create: (_) => UserProvider()), // 挂起这个大屏幕
    ],
    child: const MyApp(),
  ),
);

修改以及更新

  1. 修改
// 注意:这里用的是 context.read 而不是 Consumer。
      // 区别是:
      // - Consumer 是“我在看这个数据,变了请告诉我”(用于 UI 刷新)。
      // - context.read 是“我只是要用一下仓库的功能,别管我”(用于点击事件、一次性操作)。
      // 因为这里是点击按钮后执行的一个动作,不需要监听后续的变化,所以 read 更省资源。
      final provider = context.read<UserProvider>();

      if (widget.user == null) {
        // 3a. 如果是新用户,调用仓库的“添加”功能
        provider.addUser(
          account: _accountController.text,
          password: _passwordController.text,
          nickname: _nicknameController.text,
          remarks: _remarksController.text,
          option: _selectedOption,
        );
      } else {
        // 3b. 如果是编辑旧用户,调用仓库的“更新”功能
        provider.updateUser(
          widget.user!.copyWith(
            account: _accountController.text,
            password: _passwordController.text,
            nickname: _nicknameController.text,
            remarks: _remarksController.text,
            option: _selectedOption,
          ),
        );
      }
  1. 更新数据
// 【重点】Consumer 是 Provider 插件提供的核心组件,翻译过来叫“消费者”。
      // 它的作用是:当 UserProvider(数据源)里的数据发生变化(调用了 notifyListeners)时,
      // Consumer 会自动标记它的 builder 函数内部的代码为“过期”,并命令 Flutter 重新运行这段代码。
      body: Consumer<UserProvider>(
        // builder 是 Consumer 的核心构造器。
        // context: 上下文(Flutter 树中的位置)。
        // provider: 就是我们注册的 UserProvider 实例,通过它可以直接读到里面的数据。
        // child: 一个优化项,如果某些不随数据变化的 UI 放在这里,可以避免重复渲染。
        builder: (context, provider, child) {
          // 1. 判断仓库里的用户列表是否为空
          if (provider.users.isEmpty) {
            return const Center(child: Text('暂无数据'));
          }

          // 2. 如果仓库里有货(有用户数据),我们就用 ListView 显示出来
          return ListView.builder(
            itemCount: provider.users.length, // 告诉列表一共有多少个人
            itemBuilder: (context, index) {
              // 3. 从 provider(仓库)中取出第 index 个用户
              final user = provider.users[index];
              // 4. 将提取出的单个用户交给专门负责显示单行的视图组件 (_UserListItem)
              return _UserListItem(user: user);
            },
          );
        },
      ),

到这里我们的整个流程已经很清晰的展示出来了,首先是需要声明一个需要存储数据的仓库,然后在 runapp 的时候注册一下。声明 Provider 的变量 provider = context.read<UserProvider>(), 进行交互的时候 使用 provider调用仓库中声明的变量,然后使用Consumer 进行监控实时更新数据。
需要注意的是,自动刷新的原理:数据变化触发 notifyListeners 后,Consumer 才能自动重新构建 UI,如果只是执行数据,函数的末尾不执行 notifyListeners 也是不行的。
我们看下具体的效果(其实之前演示过了 偷个懒在)
在这里插入图片描述
我在第一幕的列表页面进行监听,在第二幕中,调用了 addUser 的方法。返回第一幕的时候,数据已经展示到列列表页面了。

总结

Provider 适合的场景:
中小型应用、快速原型、中等复杂度的状态共享需求。

进阶学习路径:

熟练掌握 ChangeNotifierProvider + Consumer
学习 ProxyProvider 和 MultiProvider
尝试 FutureProvider / StreamProvider
了解 Riverpod(Provider 2.0 版本,推荐新项目使用)
对比 Bloc、Redux 等方案,选择适合团队的方案
希望本文能帮助您快速上手 Flutter 的状态管理。如果您正在开发一个具体功能(例如购物车、表单、主题切换),欢迎提供更多细节,我可以为您编写更针对性的 Provider 示例代码。

示例地址,点击跳转到gitcode

欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐