在这里插入图片描述
在这里插入图片描述

概述

Flutter 提供了多种状态管理方案,每种方案都有其适用场景和优缺点。选择合适的状态管理模式对于构建高效、可维护的应用至关重要。

常见状态管理模式

模式 1:setState

setState 是 Flutter 最基础的状态管理方式,适用于简单的组件内部状态。

基本用法
class CounterWidget extends StatefulWidget {
  const CounterWidget({super.key});

  
  State<CounterWidget> createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int _count = 0;

  void _increment() {
    setState(() => _count++);
  }

  
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Count: $_count'),
        ElevatedButton(onPressed: _increment, child: const Text('+1')),
      ],
    );
  }
}
适用场景
  • 简单的组件内部状态
  • 状态不需要跨组件共享
  • 小型应用或快速原型开发
优缺点
优点 缺点
简单直接,无需额外依赖 无法跨组件共享状态
学习曲线平缓 状态管理与UI耦合
代码量少 难以进行单元测试
性能良好,重建范围可控 扩展性差

模式 2:状态提升

状态提升是将状态从子组件移动到共同父组件的模式,适用于少数组件共享状态的场景。

基本用法
class ParentWidget extends StatefulWidget {
  const ParentWidget({super.key});

  
  State<ParentWidget> createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  int _count = 0;

  void _increment() {
    setState(() => _count++);
  }

  
  Widget build(BuildContext context) {
    return Column(
      children: [
        CounterDisplay(count: _count),
        CounterButton(onIncrement: _increment),
      ],
    );
  }
}

class CounterDisplay extends StatelessWidget {
  final int count;

  const CounterDisplay({super.key, required this.count});

  
  Widget build(BuildContext context) {
    return Text('Count: $count');
  }
}

class CounterButton extends StatelessWidget {
  final VoidCallback onIncrement;

  const CounterButton({super.key, required this.onIncrement});

  
  Widget build(BuildContext context) {
    return ElevatedButton(onPressed: onIncrement, child: const Text('+1'));
  }
}
适用场景
  • 少数组件需要共享状态
  • 状态变化逻辑相对简单
  • 组件层级较浅
优缺点
优点 缺点
状态集中管理 回调链过长
组件解耦 重建范围较大
易于理解 代码冗余
不需要额外依赖 扩展性有限

模式 3:Provider

Provider 是基于 InheritedWidget 的状态管理库,是 Flutter 社区最常用的状态管理方案之一。

基本用法
// 创建 ChangeNotifier
class CartModel extends ChangeNotifier {
  List<CartItem> _items = [];

  List<CartItem> get items => _items;

  void addItem(CartItem item) {
    _items.add(item);
    notifyListeners();
  }

  void removeItem(String id) {
    _items.removeWhere((item) => item.id == id);
    notifyListeners();
  }
}

// 在应用中提供状态
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CartModel(),
      child: const MyApp(),
    ),
  );
}

// 消费状态
class CartScreen extends StatelessWidget {
  const CartScreen({super.key});

  
  Widget build(BuildContext context) {
    final cart = Provider.of<CartModel>(context);
    return ListView(children: cart.items.map(...).toList());
  }
}
适用场景
  • 中小型应用的全局状态管理
  • 需要跨组件共享状态
  • 团队已经熟悉 Provider
优缺点
优点 缺点
API友好,学习曲线平缓 依赖 BuildContext
基于原生 InheritedWidget 难以进行单元测试
社区支持广泛 状态管理逻辑与UI耦合
文档丰富 嵌套过深时代码臃肿

模式 4:Riverpod

Riverpod 是 Provider 的改进版,解决了 Provider 的一些缺点,提供了更灵活、更可测试的状态管理方案。

基本用法
// 定义 Provider
final cartProvider = StateNotifierProvider<CartNotifier, List<CartItem>>((ref) {
  return CartNotifier();
});

// 创建 Notifier
class CartNotifier extends StateNotifier<List<CartItem>> {
  CartNotifier() : super([]);

  void addItem(CartItem item) {
    state = [...state, item];
  }

  void removeItem(String id) {
    state = state.where((item) => item.id != id).toList();
  }
}

// 在应用中使用
void main() {
  runApp(
    ProviderScope(
      child: const MyApp(),
    ),
  );
}

// 消费状态
class CartScreen extends ConsumerWidget {
  const CartScreen({super.key});

  
  Widget build(BuildContext context, WidgetRef ref) {
    final items = ref.watch(cartProvider);
    return ListView(children: items.map(...).toList());
  }
}
适用场景
  • 中大型应用的全局状态管理
  • 需要更好的测试性
  • 需要灵活的依赖注入
  • 团队愿意学习新的状态管理方案
优缺点
优点 缺点
不依赖 BuildContext 学习曲线较陡
易于进行单元测试 需要学习新的概念
灵活的依赖注入 社区支持相对较少
编译时安全 代码量相对较多

模式 5:Bloc

Bloc 是基于 Stream 的状态管理方案,提供了可预测的状态管理和强大的测试能力。

基本用法
// 定义事件
abstract class CartEvent {}
class AddItem extends CartEvent {
  final CartItem item;
  AddItem(this.item);
}
class RemoveItem extends CartEvent {
  final String id;
  RemoveItem(this.id);
}

// 定义状态
abstract class CartState {}
class CartInitial extends CartState {}
class CartLoaded extends CartState {
  final List<CartItem> items;
  CartLoaded(this.items);
}

// 创建 Bloc
class CartBloc extends Bloc<CartEvent, CartState> {
  CartBloc() : super(CartInitial()) {
    on<AddItem>((event, emit) {
      if (state is CartLoaded) {
        final items = [...(state as CartLoaded).items, event.item];
        emit(CartLoaded(items));
      } else {
        emit(CartLoaded([event.item]));
      }
    });
    on<RemoveItem>((event, emit) {
      if (state is CartLoaded) {
        final items = (state as CartLoaded).items.where((item) => item.id != event.id).toList();
        emit(CartLoaded(items));
      }
    });
  }
}

// 在应用中使用
void main() {
  runApp(
    BlocProvider(
      create: (context) => CartBloc(),
      child: const MyApp(),
    ),
  );
}

// 消费状态
class CartScreen extends StatelessWidget {
  const CartScreen({super.key});

  
  Widget build(BuildContext context) {
    return BlocBuilder<CartBloc, CartState>(
      builder: (context, state) {
        if (state is CartLoaded) {
          return ListView(children: state.items.map(...).toList());
        }
        return const Text('Loading...');
      },
    );
  }
}
适用场景
  • 复杂的状态流管理
  • 需要强大的测试能力
  • 需要处理异步操作
  • 团队规模较大
优缺点
优点 缺点
可预测性强 代码量大
易于进行单元测试 学习曲线较陡
强大的异步处理 样板代码多
状态转换清晰 性能开销相对较大

模式对比

综合对比表格

特性 setState 状态提升 Provider Riverpod Bloc
适用规模 小型 中小型 中小型 中大型 中大型
学习曲线
测试难度
性能
代码量
灵活性
社区支持

选择决策树

┌─────────────────────────────────────────────────────────────────┐
│                    状态管理模式选择决策树                         │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
                    是否需要跨组件共享?
                              │
              ┌───────────────┴───────────────┐
              ▼                               ▼
             否                               是
              │                               │
              ▼                               ▼
        使用 setState                    是否需要跨页面共享?
              │                               │
              └───────────────┬───────────────┘
                              ▼
                             否
                              │
                              ▼
                       使用状态提升
                              │
                              ▼
                             是
                              │
                              ▼
                    项目规模和团队经验如何?
                              │
              ┌───────────────┼───────────────┐
              ▼               ▼               ▼
          小型项目         中型项目         大型项目
              │               │               │
              ▼               ▼               ▼
          Provider        Riverpod         Bloc
                              │               │
                              └───────┬───────┘
                                      ▼
                              需要复杂异步流程?
                                      │
                              ┌───────┴───────┐
                              ▼               ▼
                             是               否
                              │               │
                              ▼               ▼
                            Bloc          Riverpod

实际项目中的选择策略

策略 1:从简单开始

不要一开始就引入复杂的状态管理库,先用 setState 和状态提升解决问题。当遇到瓶颈时,再考虑引入状态管理库。

// 先用 setState
class CartWidget extends StatefulWidget {
  
  State<CartWidget> createState() => _CartWidgetState();
}

class _CartWidgetState extends State<CartWidget> {
  List<CartItem> _items = [];
  
  void _addItem(CartItem item) {
    setState(() => _items.add(item));
  }
  
  
  Widget build(BuildContext context) {
    return Column(children: [...]);
  }
}

策略 2:混合使用

在同一个项目中,可以混合使用多种状态管理模式:

  • 组件内部状态:使用 setState
  • 页面级别状态:使用状态提升或 Provider
  • 全局状态:使用 Riverpod 或 Bloc
// 组件层:setState
class ExpandablePanel extends StatefulWidget {
  
  State<ExpandablePanel> createState() => _ExpandablePanelState();
}

// 页面层:状态提升
class CartScreen extends StatefulWidget {
  
  State<CartScreen> createState() => _CartScreenState();
}

// 应用层:Riverpod
final userProvider = StateNotifierProvider<UserNotifier, User?>((ref) => UserNotifier());

策略 3:团队一致性

选择团队成员都熟悉的方案,减少学习成本。如果团队已经熟悉 Provider,就继续使用;如果团队愿意学习新方案,可以考虑 Riverpod。

策略 4:项目生命周期

  • 原型阶段:使用 setState 和状态提升,快速验证想法
  • 开发阶段:根据需求引入适当的状态管理库
  • 维护阶段:保持状态管理方案的一致性

迁移策略

从 setState 迁移到 Provider

// 迁移前
class CartWidget extends StatefulWidget {
  
  State<CartWidget> createState() => _CartWidgetState();
}

class _CartWidgetState extends State<CartWidget> {
  List<CartItem> _items = [];
  
  void _addItem(CartItem item) {
    setState(() => _items.add(item));
  }
  
  
  Widget build(BuildContext context) {
    return Column(children: [...]);
  }
}

// 迁移后
class CartModel extends ChangeNotifier {
  List<CartItem> _items = [];
  List<CartItem> get items => _items;
  
  void addItem(CartItem item) {
    _items.add(item);
    notifyListeners();
  }
}

class CartWidget extends StatelessWidget {
  
  Widget build(BuildContext context) {
    final cart = Provider.of<CartModel>(context);
    return Column(children: [...]);
  }
}

从 Provider 迁移到 Riverpod

// 迁移前
ChangeNotifierProvider(
  create: (context) => CartModel(),
  child: MyApp(),
)

final cart = Provider.of<CartModel>(context);

// 迁移后
ProviderScope(child: MyApp())

final cartProvider = StateNotifierProvider<CartNotifier, List<CartItem>>((ref) => CartNotifier());

final items = ref.watch(cartProvider);

总结

选择状态管理模式的关键原则:

  1. 从简单开始:先用 setState 和状态提升
  2. 按需引入:只有在需要时才引入状态管理库
  3. 团队一致:选择团队熟悉的方案
  4. 混合使用:根据场景选择合适的方案
  5. 保持一致:在项目中保持状态管理方案的一致性

没有最好的状态管理方案,只有最适合当前项目的方案。根据项目规模、团队经验和需求复杂度来选择合适的状态管理模式。

在下一节中,我们将探讨不可变状态设计的原则和实践。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐