> 技术文档 > Flutter 之 Bloc 手册:全面指南与实践_flutter bloc

Flutter 之 Bloc 手册:全面指南与实践_flutter bloc

目录

Flutter 之 Bloc 手册:全面指南与实践

一、Bloc 简介

事件(Event)

状态(State)

业务逻辑组件(Bloc)

二、在 Flutter 中使用 Bloc

引入依赖

在 Widget 中使用 Bloc

BlocProvider

BlocBuilder

BlocListener

三、Cubit 模式

定义 Cubit

在 Widget 中使用 Cubit

四、Bloc 的高级用法

多个 Bloc 的管理

Bloc 之间的通信

五、总结


在 Flutter 开发中,随着应用程序复杂度的增加,有效的状态管理变得至关重要。Bloc(Business Logic Component,业务逻辑组件)模式应运而生,它将业务逻辑从用户界面(UI)中分离出来,使得代码更具可维护性、可测试性和可扩展性。本文将深入探讨 Flutter 中的 Bloc 模式,通过丰富的代码示例帮助你全面掌握其使用方法。

一、Bloc 简介

Bloc 模式的核心思想是通过 “流(Stream)” 来管理状态变化。它主要包含三个关键部分:事件(Event)、状态(State)和业务逻辑组件(Bloc)本身。

事件(Event)

事件代表了应用程序中发生的各种动作或触发条件,比如用户点击按钮、网络请求完成等。在代码中,我们通常会为不同类型的事件定义相应的类。例如,在一个计数器应用中,可能有增加计数和减少计数两个事件:

// 定义抽象的计数器事件类abstract class CounterEvent {}// 增加计数事件class IncrementEvent extends CounterEvent {}// 减少计数事件class DecrementEvent extends CounterEvent {}

状态(State)

状态表示应用程序在某一时刻的数据状态,UI 会根据不同的状态进行相应的渲染。同样以计数器应用为例,状态类可以如下定义:

class CounterState { final int count; CounterState({required this.count});}

业务逻辑组件(Bloc)

Bloc 负责接收事件,根据事件的类型执行相应的业务逻辑,并生成新的状态。它是连接事件和状态的桥梁。在 Flutter 中,我们通过继承Bloc类来创建自定义的 Bloc。

import \'package:bloc/bloc.dart\';class CounterBloc extends Bloc { CounterBloc() : super(CounterState(count: 0)); @override Stream mapEventToState(CounterEvent event) async* { if (event is IncrementEvent) { yield CounterState(count: state.count + 1); } else if (event is DecrementEvent) { yield CounterState(count: state.count - 1); } }}

在上述代码中,CounterBloc继承自Bloc类,泛型参数分别为CounterEventCounterState,表示该 Bloc 处理的事件类型和状态类型。构造函数中初始化了初始状态CounterState(count: 0)mapEventToState方法是 Bloc 的核心逻辑,它根据传入的事件类型,生成并通过yield返回新的状态。

二、在 Flutter 中使用 Bloc

引入依赖

首先,在pubspec.yaml文件中添加flutter_blocbloc库的依赖:

dependencies: flutter: sdk: flutter flutter_bloc: ^8.1.3 bloc: ^8.1.2

然后运行flutter pub get获取依赖。

在 Widget 中使用 Bloc

BlocProvider

BlocProvider是一个重要的 Widget,用于在 Widget 树中提供 Bloc 实例,确保其子 Widget 可以访问到该 Bloc。例如,在应用的入口处提供CounterBloc

import \'package:flutter/material.dart\';import \'package:flutter_bloc/flutter_bloc.dart\';void main() { runApp( BlocProvider( create: (context) => CounterBloc(), child: const MyApp(), ), );}class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( home: const CounterPage(), ); }}

在上述代码中,BlocProvidercreate回调函数创建了CounterBloc的实例,并将其提供给MyApp及其子 Widget。

BlocBuilder

BlocBuilder用于监听 Bloc 的状态变化,并根据新的状态重建 Widget。在CounterPage中使用BlocBuilder来显示计数器的值:

class CounterPage extends StatelessWidget { const CounterPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text(\'Counter\'), ), body: Center( child: BlocBuilder( builder: (context, state) { return Text(  \'Count: ${state.count}\',  style: const TextStyle(fontSize: 24), ); }, ), ), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton( onPressed: () => context.read().add(IncrementEvent()), child: const Icon(Icons.add), ), const SizedBox(height: 16), FloatingActionButton( onPressed: () => context.read().add(DecrementEvent()), child: const Icon(Icons.remove), ), ], ), ); }}

BlocBuilderbuilder回调函数中,根据CounterState中的count值来显示当前的计数值。当CounterBloc的状态发生变化时,BlocBuilder会自动重建,更新 UI 显示。

BlocListener

BlocListener用于监听 Bloc 的状态变化,并在状态变化时执行一些副作用操作,如导航、显示提示信息等,但不会触发 UI 重建(这一点与BlocBuilder不同)。例如,当计数器的值达到 10 时,显示一个提示信息:

class CounterPage extends StatelessWidget { const CounterPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text(\'Counter\'), ), body: BlocListener( listener: (context, state) { if (state.count == 10) { ScaffoldMessenger.of(context).showSnackBar(  const SnackBar(content: Text(\'Count reached 10!\')), ); } }, child: Center( child: BlocBuilder( builder: (context, state) {  return Text( \'Count: ${state.count}\', style: const TextStyle(fontSize: 24),  ); }, ), ), ), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton( onPressed: () => context.read().add(IncrementEvent()), child: const Icon(Icons.add), ), const SizedBox(height: 16), FloatingActionButton( onPressed: () => context.read().add(DecrementEvent()), child: const Icon(Icons.remove), ), ], ), ); }}

在上述代码中,BlocListenerlistener回调函数会在CounterBloc的状态变化时被调用。当state.count等于 10 时,通过ScaffoldMessenger显示一个SnackBar提示信息。

三、Cubit 模式

Cubit 是 Bloc 的一种简化形式,它没有事件的概念,而是通过直接调用方法来改变状态。在一些简单的业务逻辑场景中,Cubit 可以使代码更加简洁。

定义 Cubit

以计数器为例,定义一个CounterCubit

import \'package:bloc/bloc.dart\';class CounterCubit extends Cubit { CounterCubit() : super(0); void increment() => emit(state + 1); void decrement() => emit(state - 1);}

CounterCubit中,我们直接定义了incrementdecrement方法来改变状态,通过emit方法发出新的状态值。

在 Widget 中使用 Cubit

同样,使用BlocProvider来提供CounterCubit实例,并在 Widget 中使用BlocBuilder监听状态变化:

import \'package:flutter/material.dart\';import \'package:flutter_bloc/flutter_bloc.dart\';void main() { runApp( BlocProvider( create: (context) => CounterCubit(), child: const MyApp(), ), );}class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( home: const CounterPage(), ); }}class CounterPage extends StatelessWidget { const CounterPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text(\'Counter\'), ), body: Center( child: BlocBuilder( builder: (context, state) { return Text(  \'Count: $state\',  style: const TextStyle(fontSize: 24), ); }, ), ), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton( onPressed: () => context.read().increment(), child: const Icon(Icons.add), ), const SizedBox(height: 16), FloatingActionButton( onPressed: () => context.read().decrement(), child: const Icon(Icons.remove), ), ], ), ); }}

这里的使用方式与 Bloc 类似,只是在CounterCubit中没有事件类,直接通过方法调用改变状态。

四、Bloc 的高级用法

多个 Bloc 的管理

在复杂的应用中,可能会有多个 Bloc 同时存在。可以使用MultiBlocProvider来管理多个 Bloc 实例。例如,一个应用中同时有计数器 Bloc 和用户信息 Bloc:

import \'package:flutter/material.dart\';import \'package:flutter_bloc/flutter_bloc.dart\';class UserBloc extends Bloc { // 实现UserBloc的逻辑}class UserEvent {}class UserState {}void main() { runApp( MultiBlocProvider( providers: [ BlocProvider(create: (context) => CounterBloc()), BlocProvider(create: (context) => UserBloc()), ], child: const MyApp(), ), );}class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( home: const HomePage(), ); }}class HomePage extends StatelessWidget { const HomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( // 页面内容可以同时使用CounterBloc和UserBloc ); }}

MultiBlocProviderproviders列表中,可以添加多个BlocProvider,分别提供不同的 Bloc 实例,方便在整个应用中使用。

Bloc 之间的通信

有时候,不同的 Bloc 之间需要进行通信。一种常见的方式是通过事件总线(Event Bus)。可以创建一个全局的事件总线类,各个 Bloc 可以向事件总线发送事件,其他 Bloc 监听感兴趣的事件并做出响应。例如:

import \'package:event_bus/event_bus.dart\';final eventBus = EventBus();class UserLoggedInEvent {}class UserBloc extends Bloc { UserBloc() : super(InitialUserState()) { on((event, emit) { // 处理登录逻辑 eventBus.fire(UserLoggedInEvent()); }); }}class AnotherBloc extends Bloc { AnotherBloc() : super(InitialAnotherState()) { eventBus.on().listen((event) { // 根据用户登录事件更新AnotherBloc的状态 }); }}

在上述代码中,UserBloc在处理登录事件成功后,通过eventBus发送UserLoggedInEvent事件。AnotherBloc监听UserLoggedInEvent事件,并在事件触发时执行相应的逻辑。

五、总结

通过本文的介绍,我们全面了解了 Flutter 中的 Bloc 模式及其使用方法。Bloc 模式通过将业务逻辑与 UI 分离,使得代码结构更加清晰,易于维护和测试。无论是简单的计数器应用,还是复杂的大型项目,Bloc 都能为状态管理提供有效的解决方案。同时,我们还介绍了 Cubit 这种简化的模式以及 Bloc 的一些高级用法,希望能帮助你在 Flutter 开发中更好地运用 Bloc,打造出高质量的应用程序。在实际项目中,根据具体的业务需求选择合适的状态管理方式,并不断实践和优化,相信你会在 Flutter 开发中取得更好的成果。

美国云服务器