精选文章

Flutter 状态管理实现

2021-08-16 · 状态管理

本文用多个方案给出完整可落地的状态管理实现范式,并附上优缺点与使用场景,方便你根据项目复杂度做选择。

1. Provider:轻量、易上手

适合:中小项目、快速迭代
优点:简单、和 Flutter 生态深度兼容
缺点:依赖隐式、复杂业务容易膨胀

1.1 定义 State

class CounterState extends ChangeNotifier {
  int _count = 0;

  int get count => _count;

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

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

1.2 注入 Provider

MultiProvider(
  providers: [
    ChangeNotifierProvider(create: (_) => CounterState()),
  ],
  child: const MyApp(),
)

1.3 UI 订阅与渲染

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Consumer<CounterState>(
          builder: (_, state, __) => Text('${state.count}'),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: context.read<CounterState>().increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

1.4 异步状态(加载 + 错误)

class UserState extends ChangeNotifier {
  bool loading = false;
  String? error;
  User? user;

  Future<void> loadUser() async {
    loading = true;
    error = null;
    notifyListeners();
    try {
      user = await api.fetchUser();
    } catch (e) {
      error = e.toString();
    } finally {
      loading = false;
      notifyListeners();
    }
  }
}

1.5 使用场景

  • 页面级状态、局部模块状态
  • 简单业务逻辑与轻量协作团队

2. Riverpod:更强的可测试性

适合:中大型项目、依赖复杂、强调可测试性
优点:类型安全、无 BuildContext 依赖、测试友好
缺点:学习成本略高,概念更多

2.1 StateProvider(简单状态)

final counterProvider = StateProvider<int>((ref) => 0);

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Scaffold(
      body: Center(child: Text('$count')),
      floatingActionButton: FloatingActionButton(
        onPressed: () => ref.read(counterProvider.notifier).state++,
        child: const Icon(Icons.add),
      ),
    );
  }
}

2.2 StateNotifier(复杂状态)

class UserState {
  final bool loading;
  final String? error;
  final User? user;
  const UserState({this.loading = false, this.error, this.user});
}

class UserNotifier extends StateNotifier<UserState> {
  UserNotifier() : super(const UserState());

  Future<void> loadUser() async {
    state = const UserState(loading: true);
    try {
      final user = await api.fetchUser();
      state = UserState(user: user);
    } catch (e) {
      state = UserState(error: e.toString());
    }
  }
}

final userProvider = StateNotifierProvider<UserNotifier, UserState>(
  (ref) => UserNotifier(),
);

2.3 FutureProvider / StreamProvider

final userFutureProvider = FutureProvider<User>((ref) async {
  return api.fetchUser();
});

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final asyncUser = ref.watch(userFutureProvider);
    return asyncUser.when(
      data: (u) => Text(u.name),
      loading: () => const CircularProgressIndicator(),
      error: (e, _) => Text(e.toString()),
    );
  }
}

2.4 Provider 组合与依赖

final apiProvider = Provider<ApiClient>((ref) => ApiClient());
final repoProvider = Provider<UserRepo>((ref) => UserRepo(ref.read(apiProvider)));

2.5 使用场景

  • 依赖复杂、模块多、需要严格边界
  • 强调可测试与可维护

3. BLoC:流程清晰、事件驱动

适合:复杂业务流程、多人协作
优点:事件-状态模型清晰、可追踪
缺点:样板代码多、学习成本高

3.1 基本结构

abstract class AuthEvent {}
class LoginPressed extends AuthEvent {
  final String account;
  final String pwd;
  LoginPressed(this.account, this.pwd);
}

abstract class AuthState {}
class AuthInitial extends AuthState {}
class AuthLoading extends AuthState {}
class AuthSuccess extends AuthState {}
class AuthError extends AuthState {
  final String message;
  AuthError(this.message);
}

class AuthBloc extends Bloc<AuthEvent, AuthState> {
  AuthBloc() : super(AuthInitial()) {
    on<LoginPressed>((event, emit) async {
      emit(AuthLoading());
      try {
        await auth.login(event.account, event.pwd);
        emit(AuthSuccess());
      } catch (e) {
        emit(AuthError(e.toString()));
      }
    });
  }
}

3.2 BlocBuilder / BlocListener

BlocProvider(
  create: (_) => AuthBloc(),
  child: BlocListener<AuthBloc, AuthState>(
    listener: (context, state) {
      if (state is AuthError) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text(state.message)),
        );
      }
    },
    child: BlocBuilder<AuthBloc, AuthState>(
      builder: (context, state) {
        if (state is AuthLoading) return const CircularProgressIndicator();
        return ElevatedButton(
          onPressed: () => context.read<AuthBloc>().add(LoginPressed('a', 'b')),
          child: const Text('Login'),
        );
      },
    ),
  ),
);

3.3 使用场景

  • 登录、支付、流程型业务
  • 需要事件可追踪与严格状态转换

4. GetX:快速、但需约束

适合:快速开发、小团队
优点:上手快、语法简洁
缺点:全局依赖风险高、边界易模糊

4.1 基本示例

class CounterController extends GetxController {
  var count = 0.obs;
  void inc() => count.value++;
}

class CounterPage extends StatelessWidget {
  final controller = Get.put(CounterController());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(child: Obx(() => Text('${controller.count.value}'))),
      floatingActionButton: FloatingActionButton(
        onPressed: controller.inc,
        child: const Icon(Icons.add),
      ),
    );
  }
}

4.2 路由与依赖注入

GetMaterialApp(
  getPages: [
    GetPage(name: '/home', page: () => HomePage()),
  ],
);

class BindingsExample extends Bindings {
  @override
  void dependencies() {
    Get.put(CounterController());
  }
}

4.3 使用场景

  • 迭代快、状态简单的应用
  • 但需要明确全局依赖边界

5. Redux:可预测但样板多

适合:强约束、需要可追踪状态变更
优点:单向数据流清晰、调试友好
缺点:样板多、学习成本高

5.1 核心概念

class AppState {
  final int count;
  AppState(this.count);
}

class IncrementAction {}

AppState reducer(AppState state, dynamic action) {
  if (action is IncrementAction) {
    return AppState(state.count + 1);
  }
  return state;
}

5.2 Store 与 UI

final store = Store<AppState>(reducer, initialState: AppState(0));

StoreProvider(
  store: store,
  child: StoreConnector<AppState, int>(
    converter: (store) => store.state.count,
    builder: (_, count) => Text('$count'),
  ),
);

6. MobX:响应式但需规范

适合:偏响应式思维、业务状态多变
优点:写法简洁、响应式直观
缺点:需要 codegen,团队规范很重要

6.1 定义 Store

part 'counter.g.dart';

class Counter = _Counter with _$Counter;

abstract class _Counter with Store {
  @observable
  int count = 0;

  @action
  void increment() => count++;
}

6.2 UI 订阅

final counter = Counter();

Observer(
  builder: (_) => Text('${counter.count}'),
);

7. 如何选型:按复杂度决策

  • 简单页面 / 小项目:Provider / GetX
  • 中大型项目:Riverpod
  • 流程型业务/多人协作:BLoC

8. 状态划分建议

  • 页面状态:局部 Provider
  • 模块状态:模块入口注入
  • 全局状态:App 根节点注入

总结

状态管理的核心不是框架,而是边界清晰与责任划分。先选对场景,再选合适的工具,才能长期稳定维护。

JJ

作者简介

专注于内容创作、产品策略与设计实践。欢迎交流合作。

上一篇 下一篇