Flutter状态管理插件ajwah_bloc的使用
Flutter状态管理插件ajwah_bloc的使用
ajwah_bloc
ajwah_bloc
是一个用于实现 BLoC 模式的 Dart 包。每个 StateController
都具有以下功能:
- 分发操作
- 过滤操作
- 添加效果
- 控制器之间的通信
- 完整的 RxDart 功能
请查看 示例 。示例包含 counter
和 todos
页面,这些页面演示了所有功能。
CounterState
import 'package:ajwah_bloc/ajwah_bloc.dart';
import 'package:example/widgets/StreamConsumer.dart';
import 'package:rxdart/rxdart.dart';
class CounterState extends StateController<int> {
CounterState() : super(0);
@override
void onInit() {
// 当接收到 'asyncInc' 类型的操作时,延迟 1 秒后将状态增加 1
mapActionToState([
action$
.whereType('asyncInc')
.delay(const Duration(seconds: 1))
.map((event) => state + 1),
]);
}
// 增加状态值
void inc() => emit(state + 1);
// 减少状态值
void dec() => emit(state - 1);
// 返回当前状态值
Stream<SCResponse> get count$ =>
Rx.merge([
action$.whereType('asyncInc').mapTo(SCLoading()),
stream$.map((data) => data > 10
? SCError('Counter is out of the range.')
: SCData('$data')),
]);
}
TodoState
import 'package:ajwah_bloc/ajwah_bloc.dart';
import 'package:rxdart/rxdart.dart';
import '../api/todoApi.dart';
import './searchCategory.dart';
class TodoState extends StateController<List<Todo>> {
TodoState() : super([]);
@override
void onInit() {
loadTodos();
/**
* 对于 todo 搜索输入的效果。每次按键都会从 AddTodo 小部件分发 SearchInputAction。
* 但效果会将其节流到 320 毫秒以收集后续操作,并最终分发 SearchTodoAction。
*/
registerEffects([
action$
.isA<SearchInputAction>()
.debounceTime(const Duration(milliseconds: 320))
.map((action) => SearchTodoAction(action.searchText))
]);
}
// 加载待办事项
void loadTodos() {
getTodos().listen((todos) {
emit(todos);
});
}
// 添加新的待办事项
void add(String description) {
addTodo(Todo(description: description))
.listen((todo) => emit([...state, todo]));
}
// 更新待办事项
void update(Todo todo) {
updateTodo(todo).listen(
(todo) => emit([
for (var item in state)
if (item.id == todo.id) todo else item,
]), onError: (error) {
dispatch(TodoErrorAction(error));
});
}
// 删除待办事项
void remove(Todo todo) {
removeTodo(todo).listen(
(todo) => emit(state.where((item) => item.id != todo.id).toList()));
}
// 获取活动待办事项信息
Stream<String> get activeTodosInfo$ =>
stream$
.map((todos) => todos.where((todo) => !todo.completed).toList())
.map((todos) => '${todos.length} items left');
// 结合多个控制器(TodoState, SearchCategoryState)并返回单个待办事项流
Stream<List<Todo>> get todo$ =>
Rx.combineLatest3<List<Todo>, SearchCategory, String, List<Todo>>(
stream$,
remoteStream<SearchCategoryState, SearchCategory>(),
action$
.isA<SearchTodoAction>()
.map<String>((action) => action.searchText)
.doOnData((event) {
print('searchText: ' + event);
}).startWith(''), (todos, category, searchText) {
if (searchText.isNotEmpty)
todos = todos
.where((todo) => todo.description
.toLowerCase()
.contains(searchText.toLowerCase()))
.toList();
switch (category) {
case SearchCategory.Active:
return todos.where((todo) => !todo.completed).toList();
case SearchCategory.Completed:
return todos.where((todo) => todo.completed).toList();
default:
return todos;
}
});
}
// 待办事项错误动作
class TodoErrorAction extends Action {
final dynamic error;
TodoErrorAction(this.error);
}
// 搜索待办事项动作
class SearchTodoAction extends Action {
final String searchText;
SearchTodoAction(this.searchText);
}
// 搜索输入动作
class SearchInputAction extends Action {
final String searchText;
SearchInputAction(this.searchText);
}
SearchCategoryState
import 'package:ajwah_bloc/ajwah_bloc.dart';
enum SearchCategory { All, Active, Completed }
class SearchCategoryState extends StateController<SearchCategory> {
SearchCategoryState() : super(SearchCategory.All);
// 设置搜索类别
void setCategory(SearchCategory category) => emit(category);
}
Consuming State
final controller = CounterStateController();
class CounterWidget extends StatelessWidget {
const CounterWidget({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text('inc'),
onPressed: controller.inc,
),
ElevatedButton(
child: Text('dec'),
onPressed: controller.dec,
),
ElevatedButton(
child: Text('async(+)'),
onPressed: controller.asyncInc,
),
StreamBuilder(
stream: controller.count$,
initialData: '',
builder: (context, snapshot) => Text(snapshot.data.toString())
),
],
),
);
}
}
Testing
void main() {
CounterStateController? controller;
setUp(() {
controller = CounterStateController();
});
tearDown(() {
controller?.dispose();
});
ajwahTest<int>(
'Initial state',
build: () => controller!.stream$,
expect: [isA<int>()],
verify: (state) {
expect(state[0], 0);
},
);
ajwahTest<int>(
'increment',
build: () => controller!.stream$,
act: () => controller?.increment(),
skip: 1,
expect: [isA<int>()],
verify: (state) {
expect(state[0], 1);
},
);
ajwahTest<int>(
'decrement',
build: () => controller!.stream$,
act: () => controller?.decrement(),
skip: 1,
expect: [isA<int>()],
verify: (state) {
expect(state[0], -1);
},
);
}
API
Actions get action$ // 获取操作流
void dispatch(Action action) // 分发操作
void onAction(Action action) // 处理操作
void onInit() // 初始化方法
S get state // 获取当前状态
Stream<S> get stream$ // 获取状态流
Stream<T> select<T>(T Function(S state) mapCallback) // 选择状态
void emit(S newState) // 发射新状态
void registerEffects(Iterable<Stream<Action>> callbackList) // 注册效果
void importState(S state) // 导入状态
Stream<Controller> remoteController<Controller>() // 远程控制器
Future<State> remoteState<Controller, State>() // 远程状态
Stream<S> remoteStream<Controller, S>() // 远程状态流
void mapActionToState(Iterable<Stream<S>> callbackList) // 映射操作到状态
void dispose() // 销毁
示例代码
import 'package:example/states/counter.dart';
import 'package:flutter/material.dart';
import 'package:get/instance_manager.dart';
import 'package:get/route_manager.dart';
import './states/todo.dart';
import './pages/counterPage.dart';
import './pages/todoPage.dart';
import './states/searchCategory.dart';
void main() {
runApp(GetMaterialApp(
smartManagement: SmartManagement.keepFactory,
debugShowCheckedModeBanner: false,
getPages: [
GetPage(
name: '',
page: () => CounterPage(),
binding: BindingsBuilder(() {
Get.lazyPut(() => CounterState());
})),
GetPage(
name: '/todo',
page: () => TodoPage(),
transition: Transition.zoom,
binding: BindingsBuilder(() {
Get.put<TodoState>(TodoState(), permanent: true);
Get.put<SearchCategoryState>(SearchCategoryState(), permanent: true);
}))
],
initialRoute: '',
///home: CounterPage(),
));
}
更多关于Flutter状态管理插件ajwah_bloc的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter状态管理插件ajwah_bloc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,关于Flutter状态管理插件ajwah_bloc
的使用,下面是一个简单的代码案例来展示如何集成和使用它。ajwah_bloc
是一个基于BLoC(Business Logic Component)模式的状态管理库,它允许你以响应式的方式管理应用的状态。
首先,确保你已经在pubspec.yaml
文件中添加了ajwah_bloc
依赖:
dependencies:
flutter:
sdk: flutter
ajwah_bloc: ^latest_version # 替换为最新版本号
然后,运行flutter pub get
来安装依赖。
接下来,我们将创建一个简单的计数器应用来演示如何使用ajwah_bloc
。
1. 定义事件(Events)
首先,我们需要定义可能触发状态变化的事件。例如,增加计数器和减少计数器的事件。
// counter_events.dart
import 'package:ajwah_bloc/ajwah_bloc.dart';
class IncrementEvent extends Event {}
class DecrementEvent extends Event {}
2. 定义状态(States)
状态表示应用当前的视图或数据。在这个例子中,我们将有一个包含计数器的状态。
// counter_states.dart
import 'package:ajwah_bloc/ajwah_bloc.dart';
class CounterState extends State {
final int count;
CounterState({required this.count});
@override
List<Object?> get props => [count];
}
3. 定义BLoC
BLoC负责处理事件并发出新的状态。
// counter_bloc.dart
import 'package:ajwah_bloc/ajwah_bloc.dart';
import 'counter_events.dart';
import 'counter_states.dart';
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(CounterState(count: 0));
@override
Stream<CounterState> mapEventToState(CounterEvent event) async* {
if (event is IncrementEvent) {
yield state.copyWith(count: state.count + 1);
} else if (event is DecrementEvent) {
yield state.copyWith(count: state.count - 1);
}
}
}
4. 创建UI层
在UI层,我们将使用BlocProvider
来提供BLoC,并使用BlocBuilder
来监听状态变化。
// main.dart
import 'package:flutter/material.dart';
import 'package:ajwah_bloc/ajwah_bloc.dart';
import 'counter_bloc.dart';
import 'counter_events.dart';
void main() {
runApp(BlocProvider<CounterBloc>(
create: (_) => CounterBloc(),
child: MyApp(),
));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Counter App')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
return Text(
'${state.count}',
style: Theme.of(context).textTheme.headline4,
);
},
),
],
),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
onPressed: () {
context.read<CounterBloc>().add(IncrementEvent());
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
SizedBox(height: 10),
FloatingActionButton(
onPressed: () {
context.read<CounterBloc>().add(DecrementEvent());
},
tooltip: 'Decrement',
child: Icon(Icons.remove),
),
],
),
),
);
}
}
在这个示例中,我们创建了一个简单的计数器应用,使用ajwah_bloc
进行状态管理。BLoC处理增加和减少事件,并相应地更新状态。UI层监听这些状态变化并更新界面。
希望这个示例能够帮助你理解如何在Flutter项目中使用ajwah_bloc
进行状态管理。如果有更多问题,欢迎继续提问!