首页 关于 文章 作品集 工具 联系
返回文章列表

Flutter 状态管理全面指南:Provider、Riverpod 与 Bloc 实战对比

状态管理是 Flutter 开发中最核心、也最让人头疼的问题之一。从早期的 setState 到 InheritedWidget,从 Provider 到 Riverpod,再到 Bloc,社区给出了不下二十种方案。本文结合实际项目经验,深入对比目前最主流的三种方案,帮你做出最合适的选择。

一、为什么需要状态管理?

在 Flutter 中,setState 是最简单的状态更新方式。但当页面变复杂、数据需要在多个 Widget 间共享时,setState 就会变得难以维护:

// setState 方式 —— 数据共享困难
class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int count = 0;

  void increment() {
    setState(() => count++);
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('$count'),
        ChildA(count: count),   // 需要层层传递
        ChildB(count: count),   // 每层都要写 prop
        ElevatedButton(
          onPressed: increment,
          child: Text('+1'),
        ),
      ],
    );
  }
}

当层级变深、Widget 变多,这种 prop-drilling 模式会迅速失控。状态管理方案的核心目标就是:解耦 UI 与数据,让数据可以在任意位置读取和修改,且 UI 自动响应变化。

二、Provider:简单高效的主流方案

Provider 是 Flutter 官方推荐的状态管理方案,基于 InheritedWidget 封装,API 简洁,学习成本低。

2.1 定义数据模型

import 'package:flutter/foundation.dart';

// 计数器示例
class CounterModel extends ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();  // 通知所有监听者刷新
  }

  void decrement() {
    _count--;
    notifyListeners();
  }

  void reset() {
    _count = 0;
    notifyListeners();
  }
}

2.2 在顶层注入

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

2.3 在任意位置读取

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 自动监听变化并重建 UI
    final counter = context.watch<CounterModel>();

    return Scaffold(
      appBar: AppBar(title: Text('Provider 示例')),
      body: Center(
        child: Text('计数: ${counter.count}', style: TextStyle(fontSize: 48)),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: counter.increment,
        child: Icon(Icons.add),
      ),
    );
  }
}

2.4 处理异步数据

class UserModel extends ChangeNotifier {
  User? _user;
  bool _isLoading = false;

  User? get user => _user;
  bool get isLoading => _isLoading;

  Future<void> fetchUser() async {
    _isLoading = true;
    notifyListeners();

    try {
      final response = await Dio().get('/api/user/profile');
      _user = User.fromJson(response.data);
    } catch (e) {
      // 错误处理
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }
}

2.5 Provider 多状态组合

// 使用 MultiProvider 同时注入多个状态
MultiProvider(
  providers: [
    ChangeNotifierProvider(create: (_) => AuthModel()),
    ChangeNotifierProvider(create: (_) => CartModel()),
    ChangeNotifierProvider(create: (_) => ThemeModel()),
  ],
  child: const MyApp(),
)

Provider 适用场景:中小型项目、团队对 Flutter 不太熟悉、需要快速上手。API 简单直观,代码量少。

三、Riverpod:Provider 的进化版

Riverpod 是 Provider 作者的重构之作,解决了 Provider 的一些痛点:编译时安全不依赖 BuildContext支持自动销毁

3.1 基本用法

import 'package:flutter_riverpod/flutter_riverpod.dart';

// 1. 定义 Provider
final counterProvider = StateProvider<int>((ref) => 0);

// 2. 使用 ConsumerWidget
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).state++,
        child: Icon(Icons.add),
      ),
    );
  }
}

3.2 异步数据:AsyncNotifierProvider

// 定义 Notifier
class UserAsyncNotifier extends AsyncNotifier<User> {
  @override
  Future<User> build() async {
    // 初始加载数据
    final response = await Dio().get('/api/user/profile');
    return User.fromJson(response.data);
  }

  Future<void> refresh() async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(() async {
      final response = await Dio().get('/api/user/profile');
      return User.fromJson(response.data);
    });
  }
}

final userProvider = AsyncNotifierProvider<UserAsyncNotifier, User>(
  UserAsyncNotifier.new,
);

// 在 UI 中使用
class ProfilePage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final userAsync = ref.watch(userProvider);

    return userAsync.when(
      data: (user) => Text('欢迎, ${user.name}'),
      loading: () => CircularProgressIndicator(),
      error: (e, stack) => Text('加载失败: $e'),
    );
  }
}

3.3 依赖注入

// Provider 之间可以互相依赖
final dioProvider = Provider<Dio>((ref) {
  return Dio(BaseOptions(baseUrl: 'https://api.example.com'));
});

final userRepositoryProvider = Provider<UserRepository>((ref) {
  return UserRepository(ref.watch(dioProvider));
});

final userProvider = FutureProvider<User>((ref) async {
  return ref.watch(userRepositoryProvider).getCurrentUser();
});

Riverpod 适用场景:中大型项目、追求编译时安全、需要精细控制 Provider 生命周期。

四、Bloc:事件驱动的重型方案

Bloc(Business Logic Component)采用严格的单向数据流:Event → Bloc → State。它强制分离业务逻辑和 UI,适合大型复杂项目。

4.1 定义 Event 和 State

import 'package:flutter_bloc/flutter_bloc.dart';

// Events
abstract class CounterEvent {}
class CounterIncremented extends CounterEvent {}
class CounterDecremented extends CounterEvent {}
class CounterReset extends CounterEvent {}

// States
abstract class CounterState {}
class CounterInitial extends CounterState {
  final int count;
  CounterInitial(this.count);
}
class CounterUpdated extends CounterState {
  final int count;
  CounterUpdated(this.count);
}

4.2 实现 Bloc

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(CounterInitial(0)) {
    on<CounterIncremented>((event, emit) {
      final current = (state is CounterUpdated) ? (state as CounterUpdated).count : 0;
      emit(CounterUpdated(current + 1));
    });

    on<CounterDecremented>((event, emit) {
      final current = (state is CounterUpdated) ? (state as CounterUpdated).count : 0;
      emit(CounterUpdated(current - 1));
    });

    on<CounterReset>((event, emit) {
      emit(CounterUpdated(0));
    });
  }
}

4.3 在 UI 中使用

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => CounterBloc(),
      child: Scaffold(
        body: BlocBuilder<CounterBloc, CounterState>(
          builder: (context, state) {
            final count = (state is CounterUpdated) ? state.count : 0;
            return Center(
              child: Text('计数: $count', style: TextStyle(fontSize: 48)),
            );
          },
        ),
        floatingActionButton: Column(
          crossAxisAlignment: CrossAxisAlignment.end,
          mainAxisAlignment: MainAxisAlignment.end,
          children: [
            FloatingActionButton(
              heroTag: 'add',
              onPressed: () => context.read<CounterBloc>().add(CounterIncremented()),
              child: Icon(Icons.add),
            ),
            SizedBox(height: 8),
            FloatingActionButton(
              heroTag: 'sub',
              onPressed: () => context.read<CounterBloc>().add(CounterDecremented()),
              child: Icon(Icons.remove),
            ),
          ],
        ),
      ),
    );
  }
}

4.4 异步 Bloc

class AuthBloc extends Bloc<AuthEvent, AuthState> {
  final AuthRepository _authRepo;

  AuthBloc(this._authRepo) : super(AuthInitial()) {
    on<LoginRequested>(_onLogin);
    on<LogoutRequested>(_onLogout);
  }

  Future<void> _onLogin(LoginRequested event, Emitter<AuthState> emit) async {
    emit(AuthLoading());
    try {
      final user = await _authRepo.login(event.email, event.password);
      emit(AuthAuthenticated(user));
    } catch (e) {
      emit(AuthError(e.toString()));
    }
  }

  Future<void> _onLogout(LogoutRequested event, Emitter<AuthState> emit) async {
    await _authRepo.logout();
    emit(AuthUnauthenticated());
  }
}

Bloc 适用场景:大型项目、团队协作需要严格规范、业务逻辑复杂的场景。可维护性极强,但模板代码多,学习曲线陡。

五、三大方案对比

下面从多个维度进行对比:

维度 Provider Riverpod Bloc
学习曲线 ⭐ 低 ⭐⭐ 中 ⭐⭐⭐ 高
代码量 适中
编译时安全
可测试性 很好 极好
调试工具 基本 DevTools BlocObserver + DevTools
推荐场景 小型项目 中大型项目 大型团队

六、选型建议

根据我的实际经验,给几条选型建议:

  • 个人项目 / 小型项目:选 Provider。够用、简单、社区资源多。
  • 中型项目 / 追求工程质量:选 Riverpod。编译时安全是杀手锏,重构不怕改坏。
  • 大型项目 / 团队协作:选 Bloc。严格的单向数据流让代码可预测、可测试,新人接手成本更低。
  • 不纠结:三种方案没有绝对的好坏,选你熟悉的就行。真正重要的是代码写得清晰、测试覆盖到位。

总结

Flutter 的状态管理生态非常丰富,Provider、Riverpod、Bloc 各有优劣。Provider 简单实用,Riverpod 安全强大,Bloc 严谨可控。根据项目规模和团队情况选择合适的方案,比纠结「哪个最好」更有意义。

在我自己的项目中,小型工具类 APP 我用 Provider,涉及复杂业务逻辑的项目我倾向于 Riverpod。关键是保持一致性——一个项目里不要混用多种方案。