欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net),一起共建开源鸿蒙跨平台生态。

在 Flutter 开发的迭代长河中,状态管理始终是绕不开的核心命题。Provider 曾凭借简洁的 API 和低学习成本成为主流选择,但随着业务复杂度提升,它的局限性逐渐暴露 —— 全局上下文依赖、类型安全不足、重构成本高、多状态组合繁琐等问题日益凸显。而 Riverpod 2.0 作为 Provider 的 “升级版”,彻底解决了这些痛点,同时保留了易用性,还新增了缓存、自动刷新、多线程支持等高级特性。本文将从实际项目痛点出发,手把手教你完成从 Provider 到 Riverpod 2.0 的无痛迁移,同时解锁 Riverpod 的高性能玩法,让状态管理既 “优雅” 又 “高效”。

一、为什么要从 Provider 迁移到 Riverpod?

先通过一张表直观对比两者的核心差异,理解迁移的价值:

特性 Provider Riverpod 2.0
上下文依赖 强依赖 BuildContext,无上下文无法访问 完全脱离 BuildContext,随处可访问
类型安全 依赖Provider.of<T>,类型错误运行时才暴露 编译期类型检查,杜绝类型错误
状态复用 全局单例,复用需手动封装 支持局部 / 全局作用域,复用更灵活
重构成本 改名 / 移位置需全局替换 基于唯一标识符,重构无感知
缓存机制 无原生缓存,需手动实现 内置缓存策略,支持自动失效
异步处理 需结合 FutureProvider/StreamProvider,API 繁琐 AsyncNotifier + 自动状态管理,简化异步逻辑
性能优化 依赖 Consumer/Selector,优化成本高 细粒度重建控制,默认高性能

二、前置准备:环境配置与核心概念

1. 依赖引入

pubspec.yaml中添加 Riverpod 2.0 核心依赖:

yaml

dependencies:
  flutter:
    sdk: flutter
  flutter_riverpod: ^2.4.9  # 核心依赖(包含Widget绑定)
  riverpod_annotation: ^2.3.1  # 注解支持(可选,简化代码)
  json_annotation: ^4.8.1  # 配合实体类序列化(可选)

dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: ^2.4.6  # 注解生成代码
  riverpod_generator: ^2.3.3  # Riverpod注解生成器
  json_serializable: ^6.7.1  # 序列化代码生成器

2. 核心概念速览

Riverpod 2.0 的核心设计围绕 “提供者(Provider)” 和 “消费者(Consumer)” 展开,且完全解耦上下文:

  • Provider:状态的生产者,分为Provider(只读状态)、StateProvider(简单可变状态)、NotifierProvider(复杂状态)、AsyncNotifierProvider(异步状态)等;
  • Ref:状态的引用对象,用于监听 / 更新其他 Provider、获取生命周期、缓存控制等;
  • ProviderScope:Riverpod 的根容器,替代 Provider 的MultiProvider,需包裹在 App 最外层;
  • Consumer/ConsumerWidget:状态的消费者,用于在 Widget 中监听状态变化。

三、从 Provider 到 Riverpod 的分步迁移实战

场景:电商 App 的购物车状态管理

我们以 “购物车添加 / 删除商品” 这个典型场景为例,先展示 Provider 的实现方式,再一步步迁移到 Riverpod 2.0,并对比差异。

步骤 1:Provider 实现(旧代码)

dart

// 1. 定义购物车模型
class CartItem {
  final String id;
  final String name;
  final double price;
  int count;

  CartItem({
    required this.id,
    required this.name,
    required this.price,
    this.count = 1,
  });
}

// 2. 定义Provider
final cartProvider = ChangeNotifierProvider((ref) => CartProvider());

// 3. 实现ChangeNotifier
class CartProvider extends ChangeNotifier {
  final List<CartItem> _items = [];

  List<CartItem> get items => _items;

  // 添加商品
  void addItem(CartItem item) {
    final index = _items.indexWhere((i) => i.id == item.id);
    if (index >= 0) {
      _items[index].count++;
    } else {
      _items.add(item);
    }
    notifyListeners();
  }

  // 删除商品
  void removeItem(String id) {
    _items.removeWhere((item) => item.id == id);
    notifyListeners();
  }

  // 计算总价
  double get totalPrice {
    return _items.fold(0, (sum, item) => sum + item.price * item.count);
  }
}

// 4. Widget中使用
class CartPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("购物车")),
      body: Consumer<CartProvider>(
        builder: (context, cart, child) {
          if (cart.items.isEmpty) {
            return const Center(child: Text("购物车为空"));
          }
          return ListView.builder(
            itemCount: cart.items.length,
            itemBuilder: (context, index) {
              final item = cart.items[index];
              return ListTile(
                title: Text(item.name),
                subtitle: Text("¥${item.price} x ${item.count}"),
                trailing: IconButton(
                  icon: const Icon(Icons.delete, color: Colors.red),
                  onPressed: () {
                    cart.removeItem(item.id);
                  },
                ),
              );
            },
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 添加商品(依赖上下文)
          Provider.of<CartProvider>(context, listen: false).addItem(
            CartItem(
              id: DateTime.now().microsecondsSinceEpoch.toString(),
              name: "新款T恤",
              price: 99.9,
            ),
          );
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

// 5. 根Widget配置
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CartProvider(),
      child: const MyApp(),
    ),
  );
}
步骤 2:Riverpod 2.0 迁移(新代码)
第一步:重构根 Widget,添加 ProviderScope

dart

// main.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(
    // 替代ChangeNotifierProvider,包裹整个App
    const ProviderScope(
      child: MyApp(),
    ),
  );
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Riverpod购物车示例',
      home: const CartPage(),
    );
  }
}
第二步:用 Notifier 重构购物车状态(核心)

dart

// cart_notifier.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';

// 1. 保持CartItem模型不变(无需修改)
class CartItem {
  final String id;
  final String name;
  final double price;
  int count;

  CartItem({
    required this.id,
    required this.name,
    required this.price,
    this.count = 1,
  });
}

// 2. 定义Notifier(替代ChangeNotifier)
class CartNotifier extends Notifier<List<CartItem>> {
  // 初始化状态(替代构造函数)
  @override
  List<CartItem> build() {
    // 可在这里执行初始化逻辑(如从本地缓存加载购物车)
    return [];
  }

  // 添加商品(无notifyListeners,状态更新自动通知)
  void addItem(CartItem item) {
    // 注意:必须创建新列表(不可变状态),Riverpod通过引用对比检测变化
    final newItems = List<CartItem>.from(state);
    final index = newItems.indexWhere((i) => i.id == item.id);
    if (index >= 0) {
      // 同样创建新对象,保证不可变性
      newItems[index] = CartItem(
        id: newItems[index].id,
        name: newItems[index].name,
        price: newItems[index].price,
        count: newItems[index].count + 1,
      );
    } else {
      newItems.add(item);
    }
    // 更新状态
    state = newItems;
  }

  // 删除商品
  void removeItem(String id) {
    final newItems = List<CartItem>.from(state)
      ..removeWhere((item) => item.id == id);
    state = newItems;
  }

  // 计算总价(封装为计算属性,也可抽离为单独Provider)
  double get totalPrice {
    return state.fold(0, (sum, item) => sum + item.price * item.count);
  }
}

// 3. 定义Provider(全局唯一,无上下文依赖)
final cartProvider = NotifierProvider<CartNotifier, List<CartItem>>(() {
  return CartNotifier();
});

// 4. 抽离总价为单独Provider(细粒度控制重建)
final cartTotalPriceProvider = Provider<double>((ref) {
  // 监听购物车状态变化,仅当状态改变时重新计算
  final cartItems = ref.watch(cartProvider);
  return cartItems.fold(0, (sum, item) => sum + item.price * item.count);
});
第三步:重构 Widget,脱离上下文依赖

dart

// cart_page.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';

// 使用ConsumerWidget替代StatelessWidget(自动关联Ref)
class CartPage extends ConsumerWidget {
  const CartPage({super.key});

  @override
  // 新增WidgetRef参数,用于访问Provider
  Widget build(BuildContext context, WidgetRef ref) {
    // 监听购物车状态(仅当列表变化时重建)
    final cartItems = ref.watch(cartProvider);
    // 监听总价(仅当总价变化时重建,而非列表变化就重建)
    final totalPrice = ref.watch(cartTotalPriceProvider);

    return Scaffold(
      appBar: AppBar(
        title: const Text("购物车"),
        // 总价仅在变化时更新,无需整个AppBar重建
        actions: [
          Padding(
            padding: const EdgeInsets.only(right: 16),
            child: Text("总价:¥${totalPrice.toStringAsFixed(2)}"),
          )
        ],
      ),
      body: cartItems.isEmpty
          ? const Center(child: Text("购物车为空"))
          : ListView.builder(
              itemCount: cartItems.length,
              itemBuilder: (context, index) {
                final item = cartItems[index];
                return ListTile(
                  title: Text(item.name),
                  subtitle: Text("¥${item.price} x ${item.count}"),
                  trailing: IconButton(
                    icon: const Icon(Icons.delete, color: Colors.red),
                    onPressed: () {
                      // 访问Notifier,无上下文依赖
                      ref.read(cartProvider.notifier).removeItem(item.id);
                    },
                  ),
                );
              },
            ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 添加商品,完全脱离上下文
          ref.read(cartProvider.notifier).addItem(
                CartItem(
                  id: DateTime.now().microsecondsSinceEpoch.toString(),
                  name: "新款T恤",
                  price: 99.9,
                ),
              );
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}
步骤 3:关键差异解析
  1. 状态不可变性:Provider 中直接修改_items并调用notifyListeners(),而 Riverpod 要求状态不可变 —— 必须创建新列表 / 新对象更新state,这避免了隐式状态修改,提升代码可维护性。

  2. 上下文解耦:Provider 的Provider.of/Consumer强依赖BuildContext,而 Riverpod 通过WidgetRef访问状态,可在任意位置(如工具类、异步函数)调用ref.read(cartProvider),无需上下文。

  3. 细粒度重建:将 “总价” 抽离为单独的cartTotalPriceProvider,仅当总价变化时,AppBar 的价格文本才重建,而 Provider 中只要购物车列表变化,整个 Consumer 包裹的区域都会重建。

  4. 类型安全:Riverpod 的ref.watch(cartProvider)编译期就确定返回List<CartItem>,而 Provider 的Provider.of<CartProvider>若类型写错,只有运行时才会报错。

四、Riverpod 2.0 高级特性:解锁高性能状态管理

1. 异步状态管理(AsyncNotifier)

在实际项目中,购物车通常需要从网络 / 本地缓存加载,Riverpod 的AsyncNotifier简化了异步状态处理:

dart

// 1. 定义异步Notifier
class AsyncCartNotifier extends AsyncNotifier<List<CartItem>> {
  // 模拟从本地缓存加载购物车
  Future<List<CartItem>> _loadCartFromLocal() async {
    await Future.delayed(const Duration(seconds: 1)); // 模拟耗时操作
    // 实际项目中可从SharedPreferences/SQLite读取
    return [
      CartItem(id: "1", name: "默认商品", price: 59.9),
    ];
  }

  @override
  Future<List<CartItem>> build() async {
    // 初始化时自动加载数据,状态自动转为AsyncValue.loading/error/data
    return _loadCartFromLocal();
  }

  // 添加商品并同步到本地
  Future<void> addItem(CartItem item) async {
    // 标记为加载中
    state = const AsyncValue.loading();
    try {
      final currentItems = state.value ?? [];
      final newItems = List<CartItem>.from(currentItems);
      final index = newItems.indexWhere((i) => i.id == item.id);
      if (index >= 0) {
        newItems[index] = CartItem(
          id: newItems[index].id,
          name: newItems[index].name,
          price: newItems[index].price,
          count: newItems[index].count + 1,
        );
      } else {
        newItems.add(item);
      }
      // 模拟同步到本地
      await Future.delayed(const Duration(milliseconds: 500));
      // 更新状态
      state = AsyncValue.data(newItems);
    } catch (e) {
      // 捕获异常,状态自动转为error
      state = AsyncValue.error(e, StackTrace.current);
    }
  }
}

// 2. 定义异步Provider
final asyncCartProvider = AsyncNotifierProvider<AsyncCartNotifier, List<CartItem>>(() {
  return AsyncCartNotifier();
});

// 3. Widget中使用
class AsyncCartPage extends ConsumerWidget {
  const AsyncCartPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 监听异步状态,自动处理loading/error/data
    final cartAsyncValue = ref.watch(asyncCartProvider);

    return Scaffold(
      appBar: AppBar(title: const Text("异步购物车")),
      body: cartAsyncValue.when(
        loading: () => const Center(child: CircularProgressIndicator()),
        error: (error, stack) => Center(child: Text("加载失败:$error")),
        data: (cartItems) {
          if (cartItems.isEmpty) {
            return const Center(child: Text("购物车为空"));
          }
          return ListView.builder(
            itemCount: cartItems.length,
            itemBuilder: (context, index) {
              final item = cartItems[index];
              return ListTile(title: Text(item.name));
            },
          );
        },
      ),
    );
  }
}

2. 缓存与自动刷新

Riverpod 支持缓存状态,并可通过ref.refresh/ref.invalidate手动刷新,或设置自动刷新:

dart

// 定义带缓存的商品列表Provider
final productListProvider = FutureProvider<List<String>>((ref) async {
  // 设置缓存时间(5秒),5秒后自动失效刷新
  ref.keepAliveFor(const Duration(seconds: 5));
  
  // 模拟网络请求
  await Future.delayed(const Duration(seconds: 1));
  return ["商品1", "商品2", "商品3"];
});

// Widget中手动刷新
class ProductPage extends ConsumerWidget {
  const ProductPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final productsAsync = ref.watch(productListProvider);

    return Scaffold(
      appBar: AppBar(title: const Text("商品列表")),
      body: productsAsync.when(
        loading: () => const Center(child: CircularProgressIndicator()),
        error: (e, s) => Text("错误:$e"),
        data: (products) => ListView.builder(
          itemCount: products.length,
          itemBuilder: (_, i) => ListTile(title: Text(products[i])),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 手动刷新商品列表
          ref.refresh(productListProvider);
        },
        child: const Icon(Icons.refresh),
      ),
    );
  }
}

3. 局部作用域(避免全局状态污染)

Provider 的状态默认全局,而 Riverpod 支持局部作用域,比如为每个页面创建独立的购物车状态:

dart

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return ProviderScope(
      // 覆盖全局cartProvider,创建局部状态
      overrides: [
        cartProvider.overrideWith(() => CartNotifier()),
      ],
      child: const CartPage(), // 复用之前的CartPage,使用局部状态
    );
  }
}

五、迁移避坑指南

1. 常见问题与解决方案

问题 原因 解决方案
状态更新后 Widget 不重建 直接修改 state 对象(未创建新对象) 确保状态不可变,更新时创建新列表 / 新对象
报错 “Provider not found” 未添加 ProviderScope 根容器 在 runApp 中包裹 ProviderScope
上下文为空报错 仍在使用 BuildContext 访问状态 替换为 WidgetRef.read/watch
性能反而下降 过度使用 ref.watch,导致频繁重建 抽离细粒度 Provider,使用 select 筛选状态

2. 性能优化技巧

  • 使用 select 筛选状态:仅监听需要的状态字段,避免全量重建:

    dart

    // 仅监听购物车商品数量,而非整个列表
    final itemCount = ref.watch(cartProvider.select((items) => items.length));
    
  • 延迟加载:使用ref.watch(provider.notifier)仅获取通知器,不监听状态;
  • 避免在 build 中执行耗时操作:将异步逻辑封装在 Notifier 的 build/addItem 中;
  • 使用 keepAlive:对高频访问的状态设置ref.keepAlive,避免重复初始化。

六、总结

从 Provider 到 Riverpod 2.0 的迁移,本质上是从 “命令式、上下文绑定、可变状态” 向 “声明式、上下文解耦、不可变状态” 的思维转变。Riverpod 2.0 不仅解决了 Provider 的核心痛点,还通过异步状态管理、细粒度重建、缓存机制等特性,大幅提升了状态管理的性能和可维护性。

本文的迁移方案可直接落地到实际项目:

  1. 先替换根容器为ProviderScope
  2. 逐步将ChangeNotifierProvider重构为NotifierProvider
  3. 将 Widget 中的Consumer/Provider.of替换为ConsumerWidget+WidgetRef
  4. 利用 Riverpod 的高级特性(AsyncNotifier、缓存、局部作用域)优化性能。

相比于 Provider,Riverpod 2.0 的学习曲线稍高,但一旦掌握,会显著降低大型项目的状态管理复杂度。最后,附上完整示例代码仓库(示例):https://github.com/xxx/flutter_riverpod_migration,欢迎大家 Star、Fork,也欢迎在评论区交流迁移过程中遇到的问题和优化思路!

Logo

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

更多推荐