flutter BLoC 单向数据流完整教程

BLoC 单向数据流完整教程

目录

  1. [什么是 BLoC](#什么是 BLoC)
  2. [为什么选择 BLoC](#为什么选择 BLoC)
  3. 核心概念
  4. 环境安装
  5. 计数器示例
  6. 网络请求示例
  7. [与 GoRouter 集成](#与 GoRouter 集成)
  8. 最佳实践

什么是 BLoC

BLoC(Business Logic Component,业务逻辑组件)是一种将业务逻辑与 UI 分离的状态管理架构。它通过**事件(Event)驱动状态(State)**的变化,UI 只负责展示状态并派发事件。

BLoC 的核心设计目标:

  • UI 只显示状态,不直接操作数据
  • 业务逻辑集中在 BLoC 中,便于测试和维护
  • 数据流向单一:Event → BLoC → State → UI

为什么选择 BLoC

特性 setState Riverpod BLoC
适用场景 简单页面 中小型应用 中大型应用
业务与 UI 分离 弱 中 强
可测试性 低 高 高
学习曲线 低 中 高

适合使用 BLoC 的场景:

  • 复杂的表单状态管理
  • 多步骤的业务流程
  • 需要严格状态机的页面
  • 团队协作的大型项目

核心概念

BLoC 的单向数据流:

复制代码
UI 派发事件
   ↓
Event 进入 BLoC
   ↓
BLoC 处理业务逻辑
   ↓
State 更新
   ↓
UI 重建

核心组件

  1. Event :用户操作或系统通知,如 CounterIncrementPressed
  2. State :页面当前的状态,如 CounterState(count: 1)
  3. Bloc:接收事件,处理逻辑,发射新状态
  4. UI:监听状态,派发事件

环境安装

在 pubspec.yaml 中添加:

yaml 复制代码
dependencies:
  flutter_bloc: ^8.1.4

执行:

bash 复制代码
flutter pub get

计数器示例

定义 State

dart 复制代码
class CounterState {
  final int count;

  const CounterState({required this.count});

  CounterState copyWith({int? count}) {
    return CounterState(count: count ?? this.count);
  }
}

定义 Event

dart 复制代码
abstract class CounterEvent {}

class CounterIncrementPressed extends CounterEvent {}

class CounterDecrementPressed extends CounterEvent {}

定义 Bloc

dart 复制代码
import 'package:flutter_bloc/flutter_bloc.dart';

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(const CounterState(count: 0)) {
    on<CounterIncrementPressed>((event, emit) {
      emit(state.copyWith(count: state.count + 1));
    });

    on<CounterDecrementPressed>((event, emit) {
      emit(state.copyWith(count: state.count - 1));
    });
  }
}

UI 使用

dart 复制代码
class CounterPage extends StatelessWidget {
  const CounterPage({super.key});

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => CounterBloc(),
      child: const CounterView(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final bloc = context.read<CounterBloc>();

    return Scaffold(
      appBar: AppBar(title: const Text('Counter')),
      body: Center(
        child: BlocBuilder<CounterBloc, CounterState>(
          builder: (context, state) {
            return Text('Count: ${state.count}');
          },
        ),
      ),
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: () => bloc.add(CounterIncrementPressed()),
            child: const Icon(Icons.add),
          ),
          const SizedBox(height: 8),
          FloatingActionButton(
            onPressed: () => bloc.add(CounterDecrementPressed()),
            child: const Icon(Icons.remove),
          ),
        ],
      ),
    );
  }
}

网络请求示例

定义状态

dart 复制代码
sealed class UserState {}

class UserInitial extends UserState {}

class UserLoading extends UserState {}

class UserLoaded extends UserState {
  UserLoaded(this.user);
  final User user;
}

class UserError extends UserState {
  UserError(this.message);
  final String message;
}

定义事件

dart 复制代码
abstract class UserEvent {}

class UserFetched extends UserEvent {
  UserFetched(this.userId);
  final String userId;
}

定义 Bloc

dart 复制代码
class UserBloc extends Bloc<UserEvent, UserState> {
  UserBloc(this._userRepository) : super(UserInitial()) {
    on<UserFetched>(_onUserFetched);
  }

  final UserRepository _userRepository;

  Future<void> _onUserFetched(
    UserFetched event,
    Emitter<UserState> emit,
  ) async {
    emit(UserLoading());
    try {
      final user = await _userRepository.fetchUser(event.userId);
      emit(UserLoaded(user));
    } catch (e) {
      emit(UserError(e.toString()));
    }
  }
}

UI 使用

dart 复制代码
class UserPage extends StatelessWidget {
  const UserPage({required this.userId, super.key});

  final String userId;

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => UserBloc(UserRepository())..add(UserFetched(userId)),
      child: const UserView(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('User')),
      body: BlocBuilder<UserBloc, UserState>(
        builder: (context, state) {
          return switch (state) {
            UserInitial() => const Center(child: Text('请点击加载')),
            UserLoading() => const Center(child: CircularProgressIndicator()),
            UserLoaded(:final user) => Center(child: Text(user.name)),
            UserError(:final message) => Center(child: Text('错误: $message')),
          };
        },
      ),
    );
  }
}

与 GoRouter 集成

BLoC 可以放在路由上层或页面内。页面级 BLoC 通常使用 BlocProvider 包裹。

dart 复制代码
GoRoute(
  path: '/users/:userId',
  builder: (context, state) {
    final userId = state.pathParameters['userId']!;
    return BlocProvider(
      create: (_) => UserBloc(UserRepository())..add(UserFetched(userId)),
      child: const UserPage(),
    );
  },
),

最佳实践

  1. State 不可变 :使用 copyWith 创建新状态,不要直接修改字段。
  2. Event 命名清晰 :使用 动词+名词+时态 命名,如 LoginSubmitted。
  3. Bloc 只处理逻辑:UI 不直接调用 Repository,只派发事件。
  4. 使用 sealed class 表示状态 :利用 Dart 3 的 switch 表达式进行穷尽匹配。
  5. 单一职责:一个 Bloc 管理一个功能模块的状态。
  6. 及时关闭 Bloc :BlocProvider 会自动处理,无需手动 dispose。

结语

BLoC 通过严格的单向数据流,将业务逻辑从 UI 中彻底抽离。虽然学习曲线较高,但在复杂应用中能提供清晰的架构和优秀的可测试性。掌握 BLoC 后,可以结合 Dio、GoRouter、Riverpod 等工具构建结构良好的 Flutter 应用。

相关推荐
恋猫de小郭6 小时前
Flutter 官方表示:AI 时代代码越来越容易写,为什么还要一套代码库?
android·前端·flutter
喵个咪1 天前
GoWind ShortPlay 拆解:Flutter 沉浸式 Feed 的播放器工程——生命周期、手势状态机与游客降级
前端·flutter·架构
恋猫de小郭2 天前
Flutter 的文字为什么比 SwiftUI 更细?问题主要在 Skia 的 256 项查找表里
android·前端·flutter
好的佩奇3 天前
Dart 之类型系统
flutter·dart
恋猫de小郭3 天前
Dart 3.15 新特性:this 终于支持类型提升了
android·前端·flutter
FungLeo4 天前
成为全栈·Flutter App 篇·主题与可访问性:深浅色、大字号和触控体验
flutter·移动开发·深色模式·可访问性·成为全栈·大字号适配
用户95421573334855 天前
只是选个照片,怎么留下了这么多访问记录?Flutter 图片选择器排查与优化实录
flutter
FungLeo5 天前
成为全栈·Flutter App 篇·第四批复盘:复用了什么,移动端又新增了什么
flutter·移动开发·跨端开发·成为全栈·api契约·全栈复盘