Flutter BLoC 入门

前言:嘿,朋友!如果你想学习前端技术又觉得枯燥难懂,这个系列就是为你准备的------我们把 AI 当成一起成长的伙伴,用接地气的方式拆解每一项前端技能。常用提示词:"言简意赅的讲给文化程度不高的我"。

BLoC(Business Logic Component,业务逻辑组件) 让UI与业务逻辑分离变得更加容易,使你的代码更快, 易于测试, 并且易于复用。

BLoC 核心概念

本章节以"水管"这一比喻,讲解 Stream(数据水管)、Cubit(管工)与 BLoC(高级管工)这三个BLoC核心概念。

流(水管)

一句话概念: 流(Stream)="一条能源源不断送东西来的水管"

1. 把数据想成一桶桶水
  • 数组 / 列表:像把几桶水一次性摆在你面前,你要就自己去拿。
  • :像一根连着水厂的水管,水一桶桶顺序流过来,你不用跑,只要把杯子放在管口"接水"就行。
2. 为什么要用水管而不是整桶搬?
  1. 东西可能很多,分批送更省力:一首 3 小时的网络电台节目,如果一次全塞给手机,得等很久、占很多内存;换成"流",主播说一句你就听一句,边到边用。
  2. 不知道什么时候来,用流就能"等水" :比如传感器温度数据,啥时候变化你根本提前不知道,让它自己往水管里"滴",你只要接着就能实时知道温度。
  3. 出问题也容易止损:水管坏了,最多漏后面的水;一次性搬 100 桶,搬到 50 桶就发现漏了,那 50 桶前功尽弃。
3. 怎么用水管?
  • 放水:写代码的人把数据"add"进去,就像往管子里放水。
  • 接水 :你写 listen(),就像把杯子(回调函数)放在管口,水来了就会自动倒进杯子。
  • 关阀门 :如果不想喝了,可以 cancel(),等于拧紧水阀,后面来的水你就不管了。
4. 和 BLoC 框架有啥关系?

在 BLoC 里:

  • 事件流(Event Stream) :你把"按钮点击""滚动到底"等事件当水倒进去。
  • 状态流(State Stream) :Bloc 里会加工、过滤,再把新的 UI 状态当水从另一根水管放出来。
  • 你在 Widget 端只要接住"状态"那根水管,UI 就能跟着更新。你要做的事只有两件:把水倒进去/把杯子接上去
5. "流"示例代码
python 复制代码
Stream<int> countStream(int max) async* {
    for (int i = 0; i < max; i++) {
        yield i;
    }
}
Future<int> sumStream(Stream<int> stream) async {
    int sum = 0;
    await for (int value in stream) {
        sum += value;
    }
    return sum;
}
void main() async {
    Stream<int> stream = countStream(10);
    int sum = await sumStream(stream);
    print(sum); // 45
}

Cubit(管工)

Cubit = 一个专门负责"记状态、改状态、广播状态"的小工头。

你把它想成管工,专门照管那根"状态水管"(流)。

1. 回顾一下"水管"故事
  • 流(Stream) 就像一根水管,水(数据/状态)一口口流出来。
  • 拿着茶杯(监听函数)接水,水来了就能喝到最新状态。
2. 可问题来了------水谁来倒?

如果每次都要自己 yieldadd 往管子里倒水,

  • 逻辑杂七杂八,
  • 容易忘记关阀门,
  • 代码到处都是"倒水"细节。

这时候就需要派 Cubit 出场:
"交给我!倒水 + 记账 + 广播,我全包!"

3. Cubit 的三件法宝
法宝 通俗解释 关键点
state 水桶:当前存着的状态 任何时候想知道"现在啥情况",直接看它
emit(newState) 倒水:把最新状态倒进水管 一调用,所有接水的人都同时喝到新水
close() 关阀门、收工 用完 Cubit 要关,不然水管还在占资源
4. Cubit 的基本用法
scss 复制代码
class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);
  void increment() => emit(state + 1);
}
void main() {
  final cubit = CounterCubit();
  print(cubit.state); // 0
  cubit.increment();
  print(cubit.state); // 1
  cubit.close();
}

BLoC(高级管工)

BLoC 就是一名 高级管工------先收集指令,再调度工人和材料,最后把处理好的"状态水"统一放进水管,界面只管接水更新,不必关心后台怎么忙碌。

1 先回顾两位同事

BLoC(Business Logic Component,业务逻辑组件) 让UI与业务逻辑分离变得更加容易,使你的代码 更快, 易于测试, 并且 易于复用。

1 先回顾两位同事
角色 职责
Stream(水管) 源源不断送水(数据),杯子(监听函数)一接就喝到最新内容。
Cubit(小管工) 手拿一个水桶 state,收到命令立刻往水管倒新水(emit),动作简单直接。
2 高级管工 BLoC 与小管工 Cubit 的区别
  • Cubit: "老板说刷墙白,我立即刷,刷完马上告诉你。" → 适合一步到位的小活儿。
  • BLoC: "老板发来'翻新客厅'指令,我先登记事件 → 查油漆库存 → 排班 → 协调脚手架 → 完工后一次性汇报新状态。" → 适合流程多、环节多的复杂活。
3 为什么需要这位高级管工?
价值 说明
彻底分离 UI 与业务 UI 只监听状态水管;所有 if/else、网络、缓存逻辑都由 BLoC 内部处理
统一处理副作用 网络请求、本地存储等都集中在 on<Event> / mapEventToState 中,方便维护
测试友好 给一串事件,预期输出一串状态,单元测试变简单、稳定
逻辑可复用 一个 BLoC 可以被多个页面共享(如登录状态、购物车),无需复制粘贴
4 整条流水线

指令 Event → 高级管工 BLoC → emit 新 State → Stream 水管 → UI 接水刷新

  • 收指令add(Event) 把需求交给管工
  • 调度作业 :BLoC 内部 on<Event> 里查料、排队、加工
  • 倒状态水emit(State) 一次性把结果送上水管
  • 界面喝水BlocBuilder/Listener 自动刷新界面或触发副作用

Flutter BLoC

Flutter BLoC极简Demo

要跑起来 BLoC 其实只需要三样:Bloc(逻辑) → BlocProvider(把逻辑送到界面) → BlocBuilder(把界面跟状态绑在一起) ,下面是一个例子:打开一个页面,显示一个数字;按浮动按钮,数字 +1。

1. 添加依赖

在 pubspec.yaml 里加入(最新版号可用 flutter pub add flutter_bloc 自动写):

yaml 复制代码
dependencies:
  flutter:
    sdk: flutter
  flutter_bloc: ^8.1.5  

2. 代码(lib/main.dart)
scala 复制代码
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

/// ① 定义事件(只有一个增量事件)
abstract class CounterEvent {}
class CounterIncremented extends CounterEvent {}

/// ② Bloc:接收事件 → 输出状态
class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<CounterIncremented>((event, emit) => emit(state + 1));
  }
}

void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: BlocProvider(
        create: (_) => CounterBloc(),      // 提供 Bloc
        child: const CounterPage(),
      ),
    );
  }
}

/// ③ BlocBuilder 监听 Bloc 的状态
class CounterPage extends StatelessWidget {
  const CounterPage({super.key});

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

    return Scaffold(
      appBar: AppBar(title: const Text('BLoC 极简 Demo')),
      body: Center(
        child: BlocBuilder<CounterBloc, int>(
          builder: (_, count) => Text(
            '$count',
            style: const TextStyle(fontSize: 52),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => bloc.add(CounterIncremented()),   // 发送事件
        child: const Icon(Icons.add),
      ),
    );
  }
}

3. 运行
arduino 复制代码
flutter pub get
flutter run

Flutter BLoC核心概念

名字 作用 属于哪一类
BlocProvider 把一个 Bloc 实例放进树里,下面的界面都能拿到 核心
BlocBuilder 监听状态,重建 UI 核心
BlocListener 只做一次性动作(弹 Toast / 跳页面),不重建 UI 常用,但可用 BlocConsumer 代替
BlocConsumer Builder + Listener 二合一,图省事 辅助
BlocSelector 只听状态里一小块,减少不必要重建 优化
MultiBlocProvider / MultiBlocListener 一次提供/监听好几个 Bloc,用语法糖少写嵌套 辅助
RepositoryProvider / MultiRepositoryProvider 注入"仓库"或网络 DAO,Bloc 里可以拿来用 辅助
context.read() 立即拿 Bloc/Repo,不会重建 工具方法
context.watch() 拿 Bloc/Repo,每次它变都会重建 工具方法
context.select<T,S>() 只监听某个字段 优化型工具
相关推荐
unicrom_深圳市由你创科技6 分钟前
使用Django框架构建Python Web应用
前端·python·django
火车叼位8 分钟前
Node vs Element:DOM 节点类型区分
前端
颜酱41 分钟前
使用useReducer和Context进行React中的页面内部数据共享
前端·javascript·react.js
Data_Adventure1 小时前
大屏应用中的动态缩放适配工具
前端
wenke00a1 小时前
C函数实现strcopy strcat strcmp strstr
c语言·前端
AiMuo1 小时前
FLJ性能战争战报:完全抛弃 Next.js 打包链路,战术背断性选择 esbuild 自建 Worker 脚本经验
前端·性能优化
Lefan1 小时前
解决重复请求与取消未响应请求
前端
混水的鱼1 小时前
React + antd 实现文件预览与下载组件(支持图片、PDF、Office)
前端·react.js
程序员嘉逸1 小时前
🎨 CSS属性完全指南:从入门到精通的样式秘籍
前端
Jackson_Mseven1 小时前
🧺 Monorepo 是什么?一锅端的大杂烩式开发幸福生活
前端·javascript·架构