Flutter插件alt_bloc的使用_alt_bloc是一个用于实现 BLoC 设计模式的库
Flutter插件alt_bloc的使用_alt_bloc是一个用于实现 BLoC 设计模式的库
alt_bloc
概述
alt_bloc
是一个用于实现 BLoC 设计模式的库。它由 DashDevs LLC 和 Ingenio LLC 开发。
作者
- Yegor Logachev
<yegor.logachev@dashdevs.com>
(DashDevs LLC) - Olivier Brand
<obrand@ingenio.com>
(Ingenio LLC)
引言
为什么我们决定创建这个解决方案?
我们非常兴奋地推出我们版本的 BLoC 模式。从 Flutter 发布之初,我们就一直在实现 Flutter 应用,并积累了丰富的框架经验。我们的主要目标是提供一种简单易懂且快速采用的状态管理解决方案,以构建强大的应用程序。我们还希望避免依赖复杂的“魔法”框架(如 Rx),或者不得不使用多个框架来达到相同的效果。
现有的解决方案,如 bloc
和 flutter_bloc
,在设计上存在一些限制:
bloc
库只能处理单一状态和事件类。因此,开发者必须创建大型的继承层次结构,这导致状态和事件类的数量随着块的数量呈非线性增长。bloc
库使得在其他 Bloc 类中创建可重用状态变得困难。例如,简单的 UX 元素如进度指示器会导致每个 Bloc 都包含一个状态类。bloc
和flutter_bloc
库依赖于复杂的库和包,如 rxdart 和 provider,这会影响构建时间和项目大小,并增加调试复杂性。
主要特性
alt_bloc
的公共接口与流行的库(如Provider
和BLoC
)类似,所以你无需花费大量时间学习如何设置该库。- 支持多状态。你可以在每个
Bloc
中创建几个状态,并且不需要创建继承自单一父状态的状态层次结构。 - 支持
Stream
和Future
对象作为状态提供者。 - 提供独立的管道来处理导航操作。
- 包含接收导航结果的方法。
- 轻量级解决方案。整个包非常小,不包含任何第三方库。
组件
Bloc
业务逻辑组件。
该类实现了核心业务逻辑,并应放置在 UI 和数据源之间。BLoC 接受来自 widget 的 UI 动作,并通知 widget 关于 UI 状态的变化或发起导航操作。
该类实现了一些方法,以简化向 widget 交付业务逻辑状态变化和导航操作的过程。
当前解决方案包含注册要提供给 widget 的状态的方法 (registerState()
)、发送这些状态的方法 (addState()
, addStateSource()
, addStatesSource()
) 和导航操作的方法 (addNavigation()
, addNavigationSource()
).
class CounterBloc extends Bloc {
int value = 0;
CounterBloc() {
registerState<int>(initialState: value);
}
void increment() => addState(++value);
}
widget 应使用 getStateStream()
和 navigationStream
属性来订阅 Bloc
事件。
abstract class Bloc {
void registerState<S>({bool isBroadcast = false, S initialState});
S initialState<S>();
bool containsState<S>();
bool get isClosed;
bool addState<S>(S uiState);
StreamSubscription<S> addStateSource<S>(Future<S> source, {void Function(S data) onData, void Function() onDone, void Function(dynamic error) onError});
StreamSubscription<S> addStatesSource<S>(Stream<S> source, {void Function(S data) onData, void Function() onDone, void Function(dynamic error) onError});
Future<Result> addNavigation<Result>({String routeName, dynamic arguments});
StreamSubscription<RouteData> addNavigationSource(Stream<RouteData> source, {void Function(RouteData data) onData, void Function() onDone, void Function(dynamic error) onError});
Stream<S> getStateStream<S>();
Stream<RouteData> get navigationStream;
void close();
}
RouteData
包含所有有关导航的信息的类。
ResultConsumer
用于将导航结果返回到 Bloc
的回调签名。
BlocProvider
负责通过 create
函数创建 Bloc
的 Widget
。接受任何 Widget
作为子节点,并允许子节点获取 Bloc
。
函数 shouldNotify
定义了继承自此 widget 的 widget 是否应在 Bloc
更改时重建。
class CounterScreen extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return BlocProvider<CounterBloc>(
routerPrecondition: (prevRouteData, routeData) => (routeData.settings.arguments as int) % 5 == 0,
create: () => CounterBloc(),
child: CounterLayout(title: 'Bloc Demo Home Page'),
router: (context, name, args) {
return showDialog(
context: context,
builder: (_) {
return WillPopScope(
child: AlertDialog(
title: Text('Congratulations! You clicked $args times'),
),
onWillPop: () async {
Navigator.of(context).pop('Dialog with $args clicks has been closed');
return false;
}
);
}
);
},
);
}
}
Provider
封装在 BlocProvider
中的 InheritedWidget
,允许 BlocProvider.child
widget 树获取 Bloc
对象。
static B of<B extends Bloc>(BuildContext context, {bool listen = false})
BlocBuilder
接受类型为 B
的 Bloc
并订阅类型为 S
的状态流的 Widget
。如果未提供 Bloc
,则默认使用 Provider.of
。
BlocBuilder<CounterBloc, int>(
bloc: CounterBloc(),
precondition: (prevCount, count) => count % 2 == 0,
builder: (_, count) => Text('$count');
)
BlocConsumer
接受类型为 B
的 Bloc
并订阅类型为 S
的状态流的 Widget
。如果未提供 Bloc
,则默认使用 Provider.of
。
RouteListener
订阅 Bloc.navigationStream
,监听并处理导航操作的 Widget
。如果未提供 Bloc
,则默认使用 Provider.of
。
RouteListener<CounterBloc>(
bloc: CounterBloc(),
child: CounterLayout(title: 'Bloc Demo Home Page'),
precondition: (prevData, data) => (data.settings.arguments as int) % 5 == 0,
router: (context, name, args) {
return showDialog(
context: context,
builder: (_) {
return WillPopScope(
child: AlertDialog(
title: Text('Congratulations! You clicked $args times'),
),
onWillPop: () async {
// Argument that Navigator.pop() function will be returned to the Bloc
Navigator.of(context).pop('Dialog with $args clicks has been closed');
return false;
}
);
}
);
},
)
示例
以下是一个简单的 Bloc
实现示例。
你需要使用 registerState<T>()
方法注册 Bloc 将使用的所有状态。为了通知 Widget
关于更改,你应该调用 addState<T>(T_object);
。
class CounterBloc extends Bloc {
var _counter = 0;
CounterBloc() {
registerState<int>(initialState: 10);
registerState<bool>(initialState: false);
}
void increment() {
addState<bool>(true);
Future.delayed(const Duration(milliseconds: 500), () {
addState<bool>(false);
addState<int>(++_counter);
if ((_counter % 10) == 0) {
addNavigation(arguments: _counter);
}
});
}
}
如果你有一些包含状态对象的 Future
或 Stream
,你可以使用 addStatesSource()
和 addStateSource()
函数将其作为状态源传递。
class IncrementRepo {
int _counter = 0;
Future<int> increment() => Future.delayed(const Duration(seconds: 1), () => ++_counter);
Future<int> decrement() => Future.delayed(const Duration(seconds: 5), () => --_counter);
}
class CounterBloc extends Bloc {
final repo = IncrementRepo();
CounterBloc() {
registerState<int>(initialState: 0);
registerState<bool>(initialState: false);
}
void increment() {
addState<bool>(true);
addStateSource<int>(repo.increment(),
onData: (count) async {
print('Dialog result: ${await addNavigation(arguments: count)}');
},
onDone: () => addState<bool>(false));
}
}
BlocProvider
create
函数负责 Bloc 的创建。
class CounterScreen extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return BlocProvider<CounterBloc>(
create: () => CounterBloc(),
routerPrecondition: (_, routeData) => (routeData.settings.arguments as int) % 5 == 0,
child: CounterLayout(title: 'Bloc Demo Home Page'),
router: (context, name, args) {
return showDialog(
context: context,
builder: (_) => WillPopScope(
child: AlertDialog(
title: Text('Congratulations! You clicked $args times'),
),
onWillPop: () async {
Navigator.of(context).pop(args);
return false;
}
)
);
},
);
}
}
导航处理
你可以使用 router
如上所示来处理导航。或者你可以使用 RouteListener
。
class CounterScreen extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return BlocProvider<CounterBloc>(
create: () => CounterBloc(),
child: RouteListener<CounterBloc>(
child: CounterLayout(title: 'Bloc Demo Home Page'),
router: (context, name, args) {
return showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text('Congratulations! You clicked $args times'),
),
);
},
),
);
}
}
在 UI 上提供和观察 Bloc
class CounterLayout extends StatelessWidget {
CounterLayout({Key key, this.title}) : super(key: key);
final String title;
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Stack(
children: <Widget>[
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
BlocBuilder<CounterBloc, int>(
builder: (_, count) {
return Text(
'$count',
style: Theme.of(context).textTheme.display1,
);
}),
],
),
),
Center(
child: BlocBuilder<CounterBloc, bool>(
builder: (_, inProgress) {
return inProgress ? CircularProgressIndicator() : Container();
}
),
)
],
),
floatingActionButton: FloatingActionButton(
onPressed: Provider.of<CounterBloc>(context).increment,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
更多关于Flutter插件alt_bloc的使用_alt_bloc是一个用于实现 BLoC 设计模式的库的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter插件alt_bloc的使用_alt_bloc是一个用于实现 BLoC 设计模式的库的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
在Flutter中,alt_bloc
作为一个“未知功能”的插件(假设其功能未明确定义),我们可以从基本的插件集成和使用方法来探讨其可能的实现方式。由于具体功能未知,这里将提供一个假设性的集成代码示例,展示如何在Flutter项目中引入并使用一个假设的第三方插件。
首先,假设alt_bloc
已经发布在pub.dev
上,我们可以通过pubspec.yaml
文件添加依赖:
dependencies:
flutter:
sdk: flutter
alt_bloc: ^x.y.z # 假设的版本号
然后运行flutter pub get
来安装依赖。
接下来,我们将展示如何在Flutter应用中使用这个假设的alt_bloc
插件。由于具体功能未知,我们将创建一个假设性的使用场景,比如初始化插件、监听事件或状态变化等。
import 'package:flutter/material.dart';
import 'package:alt_bloc/alt_bloc.dart'; // 假设的导入路径
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Alt Bloc Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: AltBlocExampleScreen(),
);
}
}
class AltBlocExampleScreen extends StatefulWidget {
@override
_AltBlocExampleScreenState createState() => _AltBlocExampleScreenState();
}
class _AltBlocExampleScreenState extends State<AltBlocExampleScreen> {
// 假设AltBloc是插件提供的一个Bloc类
AltBloc? _altBloc;
@override
void initState() {
super.initState();
// 初始化AltBloc实例
_altBloc = AltBloc();
// 假设监听某个事件或状态变化
_altBloc!.someStream.listen((event) {
// 处理事件或状态变化
print('Received event: $event');
});
}
@override
void dispose() {
// 释放资源
_altBloc?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Alt Bloc Example'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
// 假设触发某个动作
_altBloc?.someAction();
},
child: Text('Trigger Action'),
),
),
);
}
}
// 假设AltBloc类的定义(这部分通常会在alt_bloc插件中提供)
class AltBloc {
// 假设的Stream用于监听事件或状态变化
final StreamController<String> _controller = StreamController<String>();
Stream<String> get someStream => _controller.stream;
// 假设的动作方法
void someAction() {
// 触发某个事件或状态变化
_controller.add('Action Triggered');
}
// 释放资源
void dispose() {
_controller.close();
}
}
注意:上述代码中的AltBloc
类及其方法(如someStream
和someAction
)完全是假设性的,用于展示如何在Flutter中集成和使用一个第三方插件。在实际使用中,你需要根据alt_bloc
插件的文档来了解其提供的具体API和功能。
由于alt_bloc
的具体功能未知,上述代码仅作为一个集成和使用第三方插件的模板示例。在实际项目中,你应该查阅alt_bloc
的官方文档或源代码来了解其真实的功能和使用方法。