ToolkitX
知识库工具箱

状态管理

setState, Provider, Riverpod, Bloc

30min·高级

01. StatefulWidget 和 setState

Flutter 中一切皆 Widget。StatelessWidget 是不可变的(属性不能改),StatefulWidget 是可变的(有内部状态)。StatefulWidget 有两个类:Widget 类和 State 类。调用 setState 通知框架重绘。setState 里改数据,Flutter 自动 diff 并高效更新 UI。这是最基本的状态管理方式。
dart
import 'package:flutter/material.dart';

class CounterWidget extends StatefulWidget {
  @override
  _CounterWidgetState createState() => _CounterWidgetState();
}

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

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

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('计数: $_count', style: TextStyle(fontSize: 24)),
        ElevatedButton(
          onPressed: _increment,
          child: Text('加一'),
        ),
      ],
    );
  }
}
setState 只重建当前 State 对应的 build,不会重建整个页面,Flutter 的 diff 机制非常高效。

02. InheritedWidget 和上下传递

InheritedWidget 能沿着 Widget 树向下传递数据,子 Widget 不需要一层层通过构造函数传参。典型的用法是 Theme.of(context) 获取主题、MediaQuery.of(context) 获取屏幕尺寸。原理是子 Widget 通过 BuildContext 向上查找最近的 InheritedWidget。
dart
class MyInherited extends InheritedWidget {
  final int data;
  
  MyInherited({required this.data, required Widget child}) : super(child: child);

  static MyInherited? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MyInherited>();
  }

  @override
  bool updateShouldNotify(MyInherited oldWidget) => data != oldWidget.data;
}

// 使用
class ChildWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final data = MyInherited.of(context)?.data ?? 0;
    return Text('数据: $data');
  }
}
InheritedWidget 是底层 API,实际开发不直接用它。Provider 库在 InheritedWidget 上做了更友好的封装。

03. Provider 状态管理

Provider 是 Flutter 团队推荐的状态管理方案,底层基于 InheritedWidget。ChangeNotifier 是可监听的数据模型,notifyListeners() 通知刷新。ChangeNotifierProvider 把数据注入 Widget 树,Consumer 或 context.watch 获取数据并自动更新。是目前最主流的方式。
dart
import 'package:provider/provider.dart';

// 数据模型
class CounterModel extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

// 注入
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => CounterModel(),
      child: MyApp(),
    ),
  );
}

// 使用
class CounterWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = context.watch<CounterModel>();
    return Column(
      children: [
        Text('计数: ${counter.count}'),
        ElevatedButton(
          onPressed: () => context.read<CounterModel>().increment(),
          child: Text('加一'),
        ),
      ],
    );
  }
}
context.watch 会监听变化自动重建,context.read 只读不监听,用于回调里。搞混了会多重建。

04. Riverpod 新一代状态管理

Riverpod 是 Provider 的升级版,解决了 Provider 的一些痛点(编译时安全、不依赖 BuildContext、支持多个同类型 Provider)。Provider 通过函数创建全局可访问的数据容器,ref 访问其他 Provider。StatelessWidget 配合 ConsumerWidget 获取数据。
dart
import 'package:flutter_riverpod/flutter_riverpod.dart';

// 定义 Provider
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
  return CounterNotifier();
});

class CounterNotifier extends StateNotifier<int> {
  CounterNotifier() : super(0);
  void increment() => state++;
}

// 使用
class CounterPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Scaffold(
      body: Center(
        child: Text('$count', style: TextStyle(fontSize: 48)),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => ref.read(counterProvider.notifier).increment(),
        child: Icon(Icons.add),
      ),
    );
  }
}
Riverpod 最大的优势是不依赖 BuildContext,可以在任何地方访问 Provider,包括测试代码。

05. BLoC 模式

BLoC 是更正式的状态管理方案,把业务逻辑从 UI 完全分离。事件进(Events)状态出(States),中间 BLoC 处理。使用了 Stream 流式架构。flutter_bloc 库提供了 BlocProvider 注入、BlocBuilder 重建、BlocListener 监听副作用。适合大型项目和团队协作。
dart
import 'package:flutter_bloc/flutter_bloc.dart';

// 事件
abstract class CounterEvent {}
class Increment extends CounterEvent {}

// BLoC
class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<Increment>((event, emit) => emit(state + 1));
  }
}

// 界面
class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => CounterBloc(),
      child: Scaffold(
        body: Center(
          child: BlocBuilder<CounterBloc, int>(
            builder: (context, count) => Text('$count', style: TextStyle(fontSize: 48)),
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => context.read<CounterBloc>().add(Increment()),
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}
BLoC 强制将 UI 和业务逻辑分离,虽然模板代码多了点,但可测试性和可维护性大幅提升。

知识测验

1/4正确 0

Flutter 中 setState 的作用?

下一节

导航与路由

下一节