深入解析 Flutter Bloc:从原理到实战

深入解析 Flutter Bloc:从原理到实战

Bloc(Business Logic Component)是 Flutter 中一个强大的状态管理工具,基于事件驱动的架构设计,适合管理复杂的业务逻辑和状态。Bloc 的核心理念是将业务逻辑与 UI 分离,通过事件(Event)和状态(State)来驱动应用的变化。


1. 什么是 Bloc?

1.1 Bloc 的核心概念

  • 事件驱动:通过事件(Event)触发状态的变化。
  • 状态不可变:每次状态变化都会生成一个新的状态对象。
  • 业务逻辑分离:将业务逻辑从 UI 中分离,提升代码的可维护性和可测试性。

1.2 Bloc 的优点

  1. 清晰的架构:事件和状态的分离使代码逻辑更加清晰。
  2. 高可测试性:业务逻辑独立于 UI,便于单元测试。
  3. 适合复杂项目:支持复杂的业务逻辑和状态管理。

2. Bloc 的核心原理

2.1 Bloc 的工作流程

  1. 事件(Event)
    • 用户操作(如点击按钮)会触发事件。
  2. Bloc
    • 处理事件并生成新的状态。
  3. 状态(State)
    • Bloc 将新的状态发送给 UI。
  4. UI
    • 根据状态的变化更新界面。

2.2 Bloc 的核心组件

  1. Bloc
    • 处理事件并生成状态。
  2. Event
    • 定义用户操作或触发条件。
  3. State
    • 定义应用的状态。
  4. BlocProvider
    • 提供 Bloc 实例。
  5. BlocBuilder
    • 监听状态变化并更新 UI。

3. Bloc 的常见用法

3.1 基本用法

示例:计数器应用
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';// 定义事件
abstract class CounterEvent {}class IncrementEvent extends CounterEvent {}// 定义状态
class CounterState {final int count;CounterState(this.count);
}// 定义 Bloc
class CounterBloc extends Bloc<CounterEvent, CounterState> {CounterBloc() : super(CounterState(0)) {on<IncrementEvent>((event, emit) {emit(CounterState(state.count + 1));});}
}void main() {runApp(BlocProvider(create: (context) => CounterBloc(),child: MyApp(),),);
}class MyApp extends StatelessWidget {Widget build(BuildContext context) {return MaterialApp(home: CounterHomePage(),);}
}class CounterHomePage extends StatelessWidget {Widget build(BuildContext context) {final counterBloc = BlocProvider.of<CounterBloc>(context);return Scaffold(appBar: AppBar(title: Text("Bloc 示例")),body: Center(child: BlocBuilder<CounterBloc, CounterState>(builder: (context, state) {return Text("点击次数:${state.count}");},),),floatingActionButton: FloatingActionButton(onPressed: () {counterBloc.add(IncrementEvent());},child: Icon(Icons.add),),);}
}
代码解析
  1. 事件(Event)
    • 定义 IncrementEvent,表示增加计数的操作。
  2. 状态(State)
    • 定义 CounterState,表示计数器的状态。
  3. Bloc
    • 使用 on 方法处理事件并生成新的状态。
  4. UI
    • 使用 BlocBuilder 监听状态变化并更新界面。

3.2 多状态管理

示例:登录状态管理
// 定义事件
abstract class LoginEvent {}class LoginSubmitted extends LoginEvent {final String username;final String password;LoginSubmitted(this.username, this.password);
}// 定义状态
abstract class LoginState {}class LoginInitial extends LoginState {}class LoginLoading extends LoginState {}class LoginSuccess extends LoginState {}class LoginFailure extends LoginState {final String error;LoginFailure(this.error);
}// 定义 Bloc
class LoginBloc extends Bloc<LoginEvent, LoginState> {LoginBloc() : super(LoginInitial()) {on<LoginSubmitted>((event, emit) async {emit(LoginLoading());await Future.delayed(Duration(seconds: 2)); // 模拟网络请求if (event.username == "admin" && event.password == "1234") {emit(LoginSuccess());} else {emit(LoginFailure("用户名或密码错误"));}});}
}void main() {runApp(BlocProvider(create: (context) => LoginBloc(),child: MyApp(),),);
}class MyApp extends StatelessWidget {Widget build(BuildContext context) {return MaterialApp(home: LoginPage(),);}
}class LoginPage extends StatelessWidget {final TextEditingController usernameController = TextEditingController();final TextEditingController passwordController = TextEditingController();Widget build(BuildContext context) {final loginBloc = BlocProvider.of<LoginBloc>(context);return Scaffold(appBar: AppBar(title: Text("登录")),body: BlocListener<LoginBloc, LoginState>(listener: (context, state) {if (state is LoginSuccess) {ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("登录成功")),);} else if (state is LoginFailure) {ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(state.error)),);}},child: BlocBuilder<LoginBloc, LoginState>(builder: (context, state) {if (state is LoginLoading) {return Center(child: CircularProgressIndicator());}return Padding(padding: const EdgeInsets.all(16.0),child: Column(children: [TextField(controller: usernameController,decoration: InputDecoration(labelText: "用户名"),),TextField(controller: passwordController,decoration: InputDecoration(labelText: "密码"),obscureText: true,),SizedBox(height: 20),ElevatedButton(onPressed: () {final username = usernameController.text;final password = passwordController.text;loginBloc.add(LoginSubmitted(username, password));},child: Text("登录"),),],),);},),),);}
}
代码解析
  1. 事件(Event)
    • 定义 LoginSubmitted,表示提交登录表单的操作。
  2. 状态(State)
    • 定义多个状态(LoginInitialLoginLoadingLoginSuccessLoginFailure)。
  3. Bloc
    • 处理 LoginSubmitted 事件,模拟网络请求并生成状态。
  4. UI
    • 使用 BlocListener 监听状态变化,显示提示信息。
    • 使用 BlocBuilder 渲染不同的状态。

3.3 使用 Cubit 简化代码

示例:计数器应用
// 定义 Cubit
class CounterCubit extends Cubit<int> {CounterCubit() : super(0);void increment() => emit(state + 1);
}void main() {runApp(BlocProvider(create: (context) => CounterCubit(),child: MyApp(),),);
}class MyApp extends StatelessWidget {Widget build(BuildContext context) {return MaterialApp(home: CounterHomePage(),);}
}class CounterHomePage extends StatelessWidget {Widget build(BuildContext context) {final counterCubit = BlocProvider.of<CounterCubit>(context);return Scaffold(appBar: AppBar(title: Text("Cubit 示例")),body: Center(child: BlocBuilder<CounterCubit, int>(builder: (context, state) {return Text("点击次数:$state");},),),floatingActionButton: FloatingActionButton(onPressed: counterCubit.increment,child: Icon(Icons.add),),);}
}
代码解析
  1. Cubit
    • Bloc 的简化版本,只处理状态,不需要事件。
  2. 适用场景
    • 适合简单的状态管理需求。

4. 项目实战:实现一个电商应用

4.1 功能需求

  1. 首页:展示商品列表。
  2. 商品详情页:展示商品详情。
  3. 购物车页:展示已添加的商品。

4.2 完整代码

// 商品事件
abstract class ProductEvent {}class AddToCart extends ProductEvent {final String product;AddToCart(this.product);
}// 商品状态
abstract class ProductState {}class ProductInitial extends ProductState {}class ProductAdded extends ProductState {final List<String> cart;ProductAdded(this.cart);
}// 商品 Bloc
class ProductBloc extends Bloc<ProductEvent, ProductState> {final List<String> _cart = [];ProductBloc() : super(ProductInitial()) {on<AddToCart>((event, emit) {_cart.add(event.product);emit(ProductAdded(List.from(_cart)));});}
}void main() {runApp(BlocProvider(create: (context) => ProductBloc(),child: MyApp(),),);
}class MyApp extends StatelessWidget {Widget build(BuildContext context) {return MaterialApp(home: ProductListPage(),);}
}class ProductListPage extends StatelessWidget {final List<String> products = ["商品 1", "商品 2", "商品 3"];Widget build(BuildContext context) {final productBloc = BlocProvider.of<ProductBloc>(context);return Scaffold(appBar: AppBar(title: Text("商品列表"),actions: [IconButton(icon: Icon(Icons.shopping_cart),onPressed: () {Navigator.push(context,MaterialPageRoute(builder: (context) => CartPage()),);},),],),body: ListView.builder(itemCount: products.length,itemBuilder: (context, index) {final product = products[index];return ListTile(title: Text(product),trailing: ElevatedButton(onPressed: () {productBloc.add(AddToCart(product));},child: Text("添加到购物车"),),);},),);}
}class CartPage extends StatelessWidget {Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("购物车")),body: BlocBuilder<ProductBloc, ProductState>(builder: (context, state) {if (state is ProductAdded) {return ListView.builder(itemCount: state.cart.length,itemBuilder: (context, index) {return ListTile(title: Text(state.cart[index]),);},);}return Center(child: Text("购物车为空"));},),);}
}

5. 总结

5.1 Bloc 的优点

  1. 清晰的架构:事件和状态的分离使代码逻辑更加清晰。
  2. 高可测试性:业务逻辑独立于 UI,便于单元测试。
  3. 适合复杂项目:支持复杂的业务逻辑和状态管理。

5.2 实践建议

  1. 小型项目:使用 Cubit 简化代码。
  2. 中型项目:使用 Bloc 管理复杂的业务逻辑。
  3. 大型项目:结合 Bloc 和依赖注入,构建模块化的状态管理体系。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/70238.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用右侧值现象来处理一个word导入登记表的需求

需求也简单&#xff0c;导word文件用户登记表&#xff0c;有各部门的十几个版本&#xff08;为什么这么多&#xff1f;不知道&#xff09;。这里说下谈下我的一些代码做法&#xff1a; 需求分析&#xff1a; 如果能解决java字段和各项填的值怎么配对的问题&#xff0c;那么就…

Day48(补)【AI思考】-设计模式三大类型统一区分与记忆指南

文章目录 设计模式三大类型统一区分与记忆指南**一、创建型模式&#xff08;对象如何生&#xff1f;&#xff09;****二、结构型模式&#xff08;对象如何组&#xff1f;&#xff09;****三、行为型模式&#xff08;对象如何动&#xff1f;&#xff09;****1. 行为型类模式&…

Rook-ceph(1.92最新版)

安装前准备 #确认安装lvm2 yum install lvm2 -y #启用rbd模块 modprobe rbd cat > /etc/rc.sysinit << EOF #!/bin/bash for file in /etc/sysconfig/modules/*.modules do[ -x \$file ] && \$file done EOF cat > /etc/sysconfig/modules/rbd.modules &l…

Transformer技术报告:架构与原理

【深度学习】Transformer 技术报告&#xff1a;架构与原理 一、引言二、Transformer 的基本架构2.1 总体架构2.2 编码器&#xff08;Encoder&#xff09;2.3 解码器&#xff08;Decoder&#xff09;2.4 输入嵌入与位置编码 三、Transformer 的关键特性四、应用场景五、总结 一、…

电子制造企业数字化转型实战:基于Odoo构建MES平台的深度解决方案

作者背景 拥有8年乙方项目经理经验、8年甲方信息化管理经验&#xff0c;主导过12个Odoo制造业项目落地&#xff0c;服务客户涵盖消费电子、汽车电子、工业设备等领域。本文基于华东某电子企业&#xff08;以下简称"A公司"&#xff09;的实战案例&#xff0c;解析行业…

【实战】用飞书多维表格+AI DeepSeeker做股票量价分析

用2万元起步资金&#xff0c;进行A股实战模拟。&#xff08;量化分析无法知晓 消息面的事宜&#xff0c;是一个不足&#xff0c;但是可以代替 哪些一般水平的 股票分析师&#xff09; https://zk4wn8rhv2.feishu.cn/base/OABmbEBa4a4zgOsw5JlcrfIPnzh?tabletblMK2bDhPW5Am9b&a…

计算四个锚点TOA定位中GDOP的详细步骤和MATLAB例程

该MATLAB代码演示了在三维空间中,使用四个锚点的TOA(到达时间)定位技术计算几何精度衰减因子(GDOP)的过程。如需帮助,或有导航、定位滤波相关的代码定制需求,请联系作者 文章目录 DOP计算原理MATLAB例程运行结果示例关键点说明扩展方向另有文章: 多锚点Wi-Fi定位和基站…

Vue 记录用户进入页面的时间、离开页面的时间并计算时长

在 Vue 项目中&#xff0c;要记录用户进入页面的时间、离开页面的时间&#xff0c;并在用户离开时计算时长并调用后端接口&#xff0c;可以借助 Vue 的生命周期钩子和浏览器的一些事件来实现。以下是具体的实现步骤和示例代码&#xff1a; 实现思路 记录进入时间&#xff1a;…

蓝桥杯单片机基础部分——单片机介绍部分

前言 这个部分是额外的&#xff0c;我看我有的学弟学妹基础比较差&#xff0c;对板子上面的模块不太熟悉&#xff0c;这里简单的介绍一下 蓝桥杯单片机 这个就是蓝桥杯单片机的板子&#xff0c;它的主控芯片是&#xff08;IAP15F2K61S2&#xff09;&#xff0c;这里就对他常用…

场外个股期权下单后多久成交?场外个股期权对投资组合的影响

对普通老板们而言&#xff0c;它如同精密手术刀——用得好可精准优化投资组合&#xff0c;用不好则可能伤及本金。记住两个关键&#xff1a;一是永远用"亏得起的钱"参与&#xff0c;二是把合约条款当"药品说明书"逐字研读。 场外个股期权下单后多久成交&am…

sqlserver多表查询(内连接,外连接)

1.简单两表查询 2.内连接查询&#xff1a;两张表 3.内连接查询&#xff1a;三张表 4.外连接查询&#xff1a;左外联&#xff0c;右外联同理 5.全外联 6.自连接 一个表中某些数据有关联&#xff0c;相当于把一张表拆成了两张表

SQL Server 运算符优先级

在 SQL Server 中&#xff0c;运算符的优先级决定了在没有使用括号明确指定计算顺序时&#xff0c;运算符的执行顺序。 运算符优先级列表 括号 () 一元运算符 &#xff08;正号&#xff09;-&#xff08;负号&#xff09;~&#xff08;按位取反&#xff09; 乘法、除法和取模…

黑马JS教程笔记(JavaScript教程)——JS基础

黑马pink老师-JavaScript基础语法 黑马程序员前端JavaScript入门到精通全套视频教程&#xff0c;javascript核心进阶ES6语法、API、js高级等基础知识和实战教程 文章目录 ~~黑马pink老师-JavaScript基础语法~~001-计算机编程基础002-计算机编程基础编程语言和标记语言区别 00…

LabVIEW用CANopen的设备属性配置与心跳消息和PDO读取

本示例展示了如何通过SDO&#xff08;服务数据对象&#xff09;配置设备属性&#xff0c;以及如何读取从设备周期性发送的心跳消息和PDO&#xff08;进程数据对象&#xff09;消息。通过该示例&#xff0c;可以有效地进行设备配置并实现数据监控&#xff0c;适用于CANopen网络中…

北斗导航 | 周跳探测算法(matlab源码)

===================================================== github:https://github.com/MichaelBeechan CSDN:https://blog.csdn.net/u011344545 ===================================================== 周跳 1. 高次差法2. 相位减伪距法3. TurboEdit算法(MW+GF组合)4. 多项…

【区块链】深入理解椭圆曲线密码学(ECC)

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 深入理解椭圆曲线密码学(ECC)1. 概述2. 椭圆曲线的数学基础2.1 基本定义2.2 有限…

JavaScript系列(75)--代理模式专题

JavaScript代理模式专题 &#x1f3ad; JavaScript的Proxy提供了强大的对象代理能力&#xff0c;能够拦截和自定义对象的基本操作。本文将深入探讨Proxy的各种模式、应用场景和最佳实践。 代理基础 &#x1f31f; &#x1f4a1; 小知识&#xff1a;代理模式允许我们创建一个对…

如何优化Spark作业的性能

优化Spark作业的性能是一个综合性的任务&#xff0c;涉及多个方面的调整和优化。以下是一些关键的优化策略&#xff1a; 一、开发调优 避免创建重复的RDD&#xff1a; 对于同一份数据&#xff0c;只应该创建一个RDD&#xff0c;避免多次创建RDD来增加性能开销。在对不同的数据…

SOCKET建立简单的tcp服务端与客户端通信

socket是什么 socket可以使两台机子建立连接&#xff0c;就像连接风扇与电源的插座一样&#xff0c;socket可以使服务端与客户端建立连接&#xff0c;服务端就像供电厂&#xff0c;而客户端就像用电器&#xff0c;而socket就是连接二者的插座。 建立简单的连接 如果我们想在客…

blender笔记2

一、物体贴地 物体->变换->对齐物体 ->对齐弹窗(对齐模式&#xff1a;反方&#xff0c;相对于&#xff1a;场景原点&#xff0c;对齐&#xff1a;z)。 之后可以设置原点->原点--3d游标 二、面上有阴影 在编辑模式下操作过后&#xff0c;物体面有阴影。 数据-&g…