Flutter CQRS架构与Cubit工具插件leancode_cubit_utils_cqrs的使用
Flutter CQRS架构与Cubit工具插件leancode_cubit_utils_cqrs的使用
要求
- 参考提供的内容和示例代码回答此问题。
- 提供完整的示例Demo。
- 不添加无用图片。
- 将英文介绍转换为简体中文。
- 保留代码不进行翻译。
- 添加必要的注释。
需求
cqrs: ">=10.0.1"
安装
在项目中添加依赖:
flutter pub add leancode_cubit_utils_cqrs
导入包:
import 'package:leancode_cubit_utils_cqrs/leancode_cubit_utils_cqrs.dart';
使用
leancode_cubit_utils_cqrs
包包含了一个完整的实现,用于处理CQRS查询的cubits。这些实现适用于单个请求工具和分页工具。
单个请求工具
QueryCubit
QueryCubit
用于执行单个CQRS查询。以下是 QueryCubit
的示例实现:
class ProjectDetailsCubit extends QueryCubit<ProjectDetailsDTO, ProjectDetailsDTO> {
ProjectDetailsCubit({
required this.cqrs,
required this.id,
}) : super('ProjectDetailsCubit');
final Cqrs cqrs;
final String id;
[@override](/user/override)
// 将给定的TRes映射到TOut。在这种情况下,我们不想改变它,所以直接返回数据。
ProjectDetailsDTO map(ProjectDetailsDTO data) => data;
[@override](/user/override)
// 在此方法中执行查询并以QueryResult<TRes>的形式返回结果。QueryResult<TRes>由QueryCubit内部处理。
Future<QueryResult<ProjectDetailsDTO>> request() {
return cqrs.get(ProjectDetails(id: id));
}
}
ArgsQueryCubit
ArgsQueryCubit<TArgs, TRes, TOut>
是一个接受参数的 QueryCubit
版本。TArgs
确定了请求方法接受的参数类型。TRes
和 TOut
的作用与 QueryCubit
相同。
使用QueryCubit和UseArgsQueryCubit
有时,没有必要对查询响应进行任何映射。这种情况下,没有必要实现扩展 QueryCubit
或 ArgsQueryCubit
的cubit。相反,可以使用提供的钩子之一 useQueryCubit
或 useArgsQueryCubit
。只需提供要执行的请求,然后就可以像通过 RequestCubitBuilder
传递它一样使用该cubit。
final queryCubit = useQueryCubit(
() => cqrs.get(ProjectDetails(id: id)),
);
final argsQueryCubit = useArgsQueryCubit(
(args) => cqrs.get(AllProjects(sortByNameDescending: args.isDescending)),
);
可以通过传递可选参数来配置 requestMode
和 loggerTag
。在 useQueryCubit
中,还可以通过传递 callOnCreate
标志来定义是否立即调用请求。
分页工具
分页工具是为了简化创建主元素为分页列表的页面而设计的。
PaginatedQueryCubit
PaginatedQueryCubit<TData, TRes, TItem>
是一种用于CQRS的分页cubit实现。它用于处理检索分页列表下一页的逻辑。它有三个泛型参数:
TData
表示我们希望存储和处理的附加数据。TRes
表示从API返回项目的结构。TItem
对应于计划在页面上显示的单个列表项模型(经过潜在转换)。
以下是 PaginatedQueryCubit
的示例实现:
class IdentitiesCubit extends PaginatedQueryCubit<void, PaginatedResult<KratosIdentityDTO>, KratosIdentityDTO> {
IdentitiesCubit({
super.config,
required this.cqrs,
}) : super(loggerTag: 'IdentitiesCubit');
final Cqrs cqrs;
[@override](/user/override)
Future<QueryResult<PaginatedResult<KratosIdentityDTO>>> requestPage(
PaginatedArgs args,
) {
return cqrs.get(
// 查询获取下一页
SearchIdentities(
pageSize: args.pageSize,
pageNumber: args.pageNumber,
emailPattern: args.searchQuery,
),
);
}
[@override](/user/override)
PaginatedResponse<void, KratosIdentityDTO> onPageResult(
PaginatedResult<KratosIdentityDTO> page,
) {
// 使用cubit方法计算是否有下一页
final args = state.args;
final hasNextPage = calculateHasNextPage(
pageNumber: args.pageNumber,
totalCount: page.totalCount,
);
// 返回带有下一页的响应
return PaginatedResponse.append(
items: page.items,
hasNextPage: hasNextPage,
);
}
}
需要实现两个方法的主体:requestPage
和 onPageResult
。在第一个方法中执行请求并返回其结果。在第二个方法中,需要处理结果并以 PaginatedResponse
的形式返回它。
预请求
预请求允许你在请求第一页之前执行操作。例如,这可以用于获取可用过滤器。
QueryPreRequest
QueryPreRequest
是一个专为CQRS设计的预请求实现类。要利用此功能提供的预请求功能,创建一个扩展 QueryPreRequest
的类。
class FiltersPreRequest extends QueryPreRequest<List<Filter>, List<Filter>, User> {
FiltersPreRequest({required this.cqrs});
final Cqrs cqrs;
[@override](/user/override)
Future<QueryResult<List<Filter>>> request(PaginatedState<List<Filter>, User> state) {
return api.getFilters();
}
[@override](/user/override)
List<Filter> map(
List<Filter> res,
PaginatedState<List<Filter>, User> state,
) {
return res;
}
}
然后需要在 PaginatedCubit
构造函数中创建定义的 FiltersPreRequest
实例。
class IdentitiesCubit extends PaginatedQueryCubit<List<Filter>, PaginatedResult<KratosIdentityDTO>, KratosIdentityDTO> {
IdentitiesCubit({
super.config,
preRequest: FiltersPreRequest(cqrs: cqrs), // 这里
required this.cqrs,
}) : super(loggerTag: 'IdentitiesCubit');
/* IdentitiesCubit 的其余实现 */
}
示例
以下是一个完整的示例,展示了如何使用 leancode_cubit_utils_cqrs
包:
import 'dart:developer';
import 'package:cqrs/cqrs.dart';
import 'package:example/cqrs/cqrs.dart';
import 'package:example/pages/home_page.dart';
import 'package:example/pages/paginated/paginated_cubit_page.dart';
import 'package:example/pages/query/query_page.dart';
import 'package:flutter/material.dart';
import 'package:leancode_cubit_utils_cqrs/leancode_cubit_utils_cqrs.dart';
import 'package:logging/logging.dart';
import 'package:provider/provider.dart';
class Routes {
static const home = '/';
static const simpleQuery = '/simple-query';
static const simpleQueryHook = '/simple-query-hook';
static const paginatedCubit = '/paginated-cubit';
}
void _setupLogger() {
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen(
(record) => log(
record.message,
time: record.time,
sequenceNumber: record.sequenceNumber,
level: record.level.value,
name: record.loggerName,
zone: record.zone,
error: record.error,
stackTrace: record.stackTrace,
),
);
FlutterError.onError = (details) {
FlutterError.dumpErrorToConsole(details);
};
}
void main() {
final cqrs = createMockedCqrs();
_setupLogger();
runApp(
Provider<Cqrs>.value(
value: cqrs,
child: PaginatedLayoutConfigProvider(
onFirstPageLoading: (context) => const Center(
child: CircularProgressIndicator(),
),
onFirstPageError: (context, error, retry) => Error(
retry: retry,
error: error,
),
onNextPageLoading: (context) => const Center(
child: CircularProgressIndicator(),
),
onNextPageError: (context, error, retry) => Error(
retry: retry,
error: error,
),
onEmptyState: (context) => const Center(child: Text('No items')),
child: RequestLayoutConfigProvider(
requestMode: RequestMode.replace,
onLoading: (BuildContext context) =>
const CircularProgressIndicator(),
onError: (
BuildContext context,
RequestErrorState<dynamic, dynamic> error,
VoidCallback? onErrorCallback,
) =>
const Text(
'Error',
style: TextStyle(color: Colors.red),
),
child: const MainApp(),
),
),
),
);
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) => MaterialApp(
routes: <String, WidgetBuilder>{
Routes.home: (_) => const HomePage(),
Routes.simpleQuery: (_) => const QueryScreen(),
Routes.simpleQueryHook: (_) => const QueryHookScreen(),
Routes.paginatedCubit: (_) => const PaginatedCubitScreen(),
},
);
}
class Error extends StatelessWidget {
const Error({
super.key,
this.retry,
required this.error,
});
final VoidCallback? retry;
final Object? error;
[@override](/user/override)
Widget build(BuildContext context) => retry != null
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(error.toString()),
ElevatedButton(onPressed: retry, child: const Text('Retry')),
],
)
: Text(error.toString());
}
更多关于Flutter CQRS架构与Cubit工具插件leancode_cubit_utils_cqrs的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter CQRS架构与Cubit工具插件leancode_cubit_utils_cqrs的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
在Flutter应用中实现CQRS(Command Query Responsibility Segregation,命令查询责任分离)架构,结合Cubit工具插件leancode_cubit_utils_cqrs
,可以帮助我们更好地管理应用的状态和逻辑。以下是一个简单的代码案例,展示如何使用这些工具来构建一个基本的CQRS架构。
1. 添加依赖
首先,在你的pubspec.yaml
文件中添加leancode_cubit_utils_cqrs
依赖:
dependencies:
flutter:
sdk: flutter
flutter_bloc: ^8.0.0
leancode_cubit_utils_cqrs: ^latest_version # 请替换为实际最新版本号
2. 定义命令和事件
在CQRS架构中,命令是用来触发状态改变的请求,而事件则是状态改变的结果。
// commands.dart
class CreateTodoCommand {
final String title;
final String description;
CreateTodoCommand({required this.title, required this.description});
}
// events.dart
class TodoCreatedEvent {
final String id;
final String title;
final String description;
TodoCreatedEvent({required this.id, required this.title, required this.description});
}
3. 定义状态
状态表示应用当前的数据。
// states.dart
import 'package:flutter/material.dart';
class TodoState {
final List<Todo> todos;
TodoState({required this.todos});
TodoState copyWith({List<Todo>? todos}) {
return TodoState(
todos: todos ?? this.todos,
);
}
}
class Todo {
final String id;
final String title;
final String description;
bool isCompleted;
Todo({required this.id, required this.title, required this.description, this.isCompleted = false});
}
4. 定义Cubit和Repository
使用leancode_cubit_utils_cqrs
中的工具来管理状态和命令处理。
// todo_cubit.dart
import 'package:bloc/bloc.dart';
import 'package:leancode_cubit_utils_cqrs/leancode_cubit_utils_cqrs.dart';
import 'commands.dart';
import 'events.dart';
import 'states.dart';
class TodoCubit extends CubitCqrsWithSimpleRepository<TodoState> {
TodoCubit() : super(TodoState(todos: []));
@override
void onCommand(Command command) {
if (command is CreateTodoCommand) {
// 生成一个唯一的ID(实际应用中可能需要更复杂的逻辑)
final id = DateTime.now().millisecondsSinceEpoch.toString();
emitEvent(TodoCreatedEvent(id: id, title: command.title, description: command.description));
}
}
@override
TodoState applyEvent(TodoState state, Event event) {
if (event is TodoCreatedEvent) {
return state.copyWith(
todos: [...state.todos, Todo(id: event.id, title: event.title, description: event.description)],
);
}
return state;
}
}
5. 使用Cubit
在UI中使用TodoCubit
来管理状态和响应用户输入。
// main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'todo_cubit.dart';
import 'states.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Todo App')),
body: BlocProvider<TodoCubit>(
create: (_) => TodoCubit(),
child: TodoList(),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
final context = BlocProvider.of<TodoCubit>(context);
context.emitCommand(CreateTodoCommand(title: 'New Todo', description: 'Description'));
},
tooltip: 'Add',
child: Icon(Icons.add),
),
),
);
}
}
class TodoList extends StatelessWidget {
@override
Widget build(BuildContext context) {
final todoCubit = BlocProvider.of<TodoCubit>(context);
return BlocBuilder<TodoCubit, TodoState>(
builder: (context, state) {
return ListView.builder(
itemCount: state.todos.length,
itemBuilder: (_, index) {
final todo = state.todos[index];
return ListTile(
title: Text(todo.title),
subtitle: Text(todo.description),
trailing: Checkbox(
value: todo.isCompleted,
onChanged: (value) {
// 这里可以添加逻辑来处理todo的完成状态改变,但在这个简单示例中忽略
},
),
);
},
);
},
);
}
}
这个示例展示了如何使用leancode_cubit_utils_cqrs
和Flutter的Bloc库来实现一个简单的CQRS架构。注意,这只是一个基本的实现,实际应用中可能需要更多的功能和更复杂的逻辑,如错误处理、持久化存储等。