EbloX
一个简单的 Flutter 状态管理库。它类似于 Bloc,但它使用大量的注解,并通过 Action 和 State 的概念将业务逻辑与 UI 分离开。
更简单、更可靠、更易于测试!
示例
一个常见的计数器示例。
添加依赖
dependencies:
eblox:
eblox_annotation:
dev_dependencies:
build_runner:
eblox_generator:
新的 counter_view_model.dart
import 'package:eblox/eblox.dart';
import 'package:eblox_annotation/eblox_annotation.dart';
import 'package:flutter/cupertino.dart';
part 'counter_view_model.g.dart';
@bloX
class _CounterVModel extends Blox{
@StateX(name:'CounterState')
int _counter = 0;
@ActionX(bind: 'CounterState')
void _add() async{
_counter ++;
}
@ActionX(bind: 'CounterState')
void _sub(){
_counter--;
}
@override
void dispose() {
super.dispose();
debugPrint('CounterVModel dispose...');
}
}
执行 flutter pub run build_runner watch --delete-conflicting-outputs 命令将在当前目录中生成 counter_view_model.g.dart 文件。
它将自动为我们生成 Action 和 State。接下来,编写 UI 并使用这些 Actions。
import 'package:eblox/blox.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'blox/counter_view_model.dart';
class CounterPage extends StatelessWidget {
const CounterPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child:BloxBuilder<CounterVModel,CounterState>(
create:()=>CounterVModel(),
builder: (count) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text("$count"),
ElevatedButton(
onPressed: () {
AddAction().to<CounterVModel>();
},
child: const Text("+"),
),
ElevatedButton(
onPressed: () {
SubAction().to<CounterVModel>();
},
child: const Text("-"),
),
],
),
);
}),
),
);
}
}
如此简单!
用法
派生一个 Blox 类,我们在其中编写业务逻辑。这是 MVVM 中的 ViewModel。请注意,为了方便代码生成注解,类名必须以 _ 前缀开头。最后,我们在该类上使用了 @bloX 注解。
@StateX 用于修饰我们需要的 state,它会自动生成一个指定名称的 State 类来封装修改后的数据。
// **************************************************************************
// BloxStateGenerator
// **************************************************************************
class CounterState<T> extends BloxSingleState<T> {
CounterState(data) : super(data);
}
如果您不指定名称,状态类将根据默认规则生成。例如
@StateX()
Color _color = Colors.white;
将生成 ColorState。
@ActionX 用于生成 Action 类,也可以指定名称。bind 用于指定要将此 Action 关联到哪个 State 类。此外,它还将修饰的方法与生成的 Action 关联起来,并在发送 Action 时调用此方法。
目前还提供了一个 @AsyncX 注解来修饰异步状态。
part 'search_view_model.g.dart';
@bloX
class _SearchVModel extends Blox{
@AsyncX(name: 'SongListState')
SongListModel _songModel = SongListModel();
@bindAsync
@ActionX(bind: 'SongListState')
BloxAsyncTask<SongListModel> _search(String name){
return (){
return SearchService.search(name);
};
}
}
@ActionX 修饰的方法也可以声明参数,生成的类将自动包含这些参数。
// **************************************************************************
// BloxActionGenerator
// **************************************************************************
class SearchAction extends BloxAction {
SearchAction(String name) : super.argsByPosition([name]);
}
要将 Action 方法与异步状态关联,您需要添加另一个注解 @bindAsync。被 @bindAsync 注解的方法必须返回 BloxAsyncTask<T> 类型,其中泛型 T 是我们需要异步加载的数据类型。
在 UI 中,您可以使用 BloxView 来处理异步状态。
class SearchPage extends StatelessWidget {
SearchPage({Key? key}) : super(key: key);
final TextEditingController _controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Song search'),),
body: SafeArea(
child: Column(
children: [
TextField(
controller: _controller,
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
suffix: IconButton(
icon: const Icon(Icons.search_rounded),
onPressed: (){
if(_controller.text.isNotEmpty) {
SearchAction(_controller.text).to<SearchVModel>();
}
},
)),
),
Flexible(
child: BloxView<SearchVModel, SongListState<SongListModel>>(
create: () => SearchVModel(),
onLoading: () => const Center(child: CircularProgressIndicator()),
onEmpty: ()=> const Center(child: Text("Empty")),
builder: (state) {
return ListView.builder(
itemCount: state.data.songs.length,
itemBuilder: (ctx, i) {
return Container(
alignment: Alignment.center,
height: 40,
child: Text(state.data.songs[i],style: const TextStyle(color: Colors.blueGrey,fontSize: 20),),
);
});
},
)),
],
),
),
);
}
}
BloxView 提供了 onLoading、onEmpty、onError 和 builder 来处理加载期间和加载后的 UI 显示。
请注意,如果您希望 onEmpty 生效,则自定义数据类型应 mixin BloxData。
class SongListModel with BloxData{
SongListModel({UnmodifiableListView<String>? songs}){
if(songs !=null) this.songs = songs;
}
UnmodifiableListView<String> songs = UnmodifiableListView([]);
@override
bool get isEmpty => songs.isEmpty;
}
有关详细示例,请在此 处 查看。

