Flutter通用用例插件generic_usecase的使用
Flutter通用用例插件 generic_usecase
的使用
简介
在使用Clean Architecture的过程中,我们经常发现自己重复编写用例的通用代码。generic_usecase
插件提供了一个小组件,用于封装应用程序的场景逻辑。通过这些类,你可以将你的逻辑封装在一个原子元素中,并在整个应用程序中注入和使用。
特性
- ✅ 简单易用的API
- ✅ 完全测试(100%覆盖率)
- ✅ 完全文档化
可用的用例类型:
Usecase<Input, Output>
NoParamsUsecase<Output>
StreamUsecase<Input, Output>
NoParamsStreamUsecase<Output>
使用示例
简单用例
假设你想将两个数字相加,可以创建一个用例如下:
class AdditionUsecase extends Usecase<int, int> {
const AdditionUsecase();
[@override](/user/override)
FutureOr<int> execute(int params) async => params + params;
}
void main() async {
final addition = AdditionUsecase();
await addition(2).then(print, onError: print); // 输出:4
}
execute
方法会在调用用例的 call
方法时执行。
使用流用例
你可以使用流用例返回一个 Stream
而不是原始值:
class GeneratorUsecase extends NoParamsStreamUsecase<int> {
const GeneratorUsecase();
[@override](/user/override)
Stream<int> execute() async* {
for (int i = 0; i < 10; i++) {
await Future<void>.delayed(const Duration(seconds: 1));
yield i;
}
}
}
void main() {
final generator = GeneratorUsecase();
final stream = generator();
stream.listen(
print,
onError: print,
onDone: () => print('Done'),
);
}
检查前提条件和后置条件
你可以在用例中添加前提条件检查,它将在 execute
方法之前执行:
class DivisionUsecase extends Usecase<(int, int), double> {
const DivisionUsecase();
[@override](/user/override)
FutureOr<ConditionsResult> checkPreconditions((int, int)? params) {
if (params == null) {
return ConditionsResult(isValid: false, message: 'Params is null');
}
if (params.$2 == 0) {
return ConditionsResult(isValid: false, message: 'Cannot divide by 0');
}
return ConditionsResult(isValid: true);
}
[@override](/user/override)
FutureOr<double> execute((int, int) params) async => params.$1 / params.$2;
}
你也可以添加后置条件检查,在 execute
方法之后执行:
class AdditionUsecase extends Usecase<int, int> {
const AdditionUsecase();
[@override](/user/override)
FutureOr<int> execute(int params) async => params + params;
[@override](/user/override)
FutureOr<ConditionsResult> checkPostconditions(int? result) {
if (result == null) {
return ConditionsResult(isValid: false, message: 'Result is null');
}
if (result < 0) {
return ConditionsResult(isValid: false, message: 'Result is negative');
}
return ConditionsResult(isValid: true);
}
}
捕获异常
你可以通过覆盖 onException
方法来捕获由用例抛出的异常:
class AdditionUsecase extends Usecase<int, int> {
const AdditionUsecase();
[@override](/user/override)
FutureOr<int> execute(int params) async => params + params;
[@override](/user/override)
FutureOr<int> onException(Object e) {
print(e); // 打印异常信息
return super.onException(e);
}
}
这个方法会在 execute
方法抛出异常时被调用,也会在前提条件或后置条件检查失败时被调用。
使用 Result
对象
通过组合前面的例子,你可以创建一个返回 Result
对象的用例。通过捕获异常和检查前提条件与后置条件,你可以返回一个 Result
对象,该对象要么是 Success
,要么是 Failure
。
class DivisionResultUsecase extends Usecase<(int, int), Result<double, Failure>> {
const DivisionResultUsecase();
[@override](/user/override)
FutureOr<ConditionsResult> checkPreconditions((int, int)? params) {
if (params == null) {
return ConditionsResult(isValid: false, message: 'Params is null');
}
if (params.$2 == 0) {
return ConditionsResult(isValid: false, message: 'Cannot divide by 0');
}
return ConditionsResult(isValid: true);
}
[@override](/user/override)
FutureOr<Result<double, Failure>> execute((int, int) params) async =>
Result.success(params.$1 / params.$2);
[@override](/user/override)
FutureOr<Result<double, Failure>> onException(Object e) {
if (e case UsecaseException _) {
return Result.failure(Failure(e.message ?? ''));
}
if (e case Exception || Error) {
return Result.failure(Failure(e.toString()));
}
return Result.failure(Failure(''));
}
}
示例代码
下面是一个完整的示例代码,展示了如何使用 generic_usecase
插件:
import 'dart:async';
import 'package:generic_usecase/generic_usecase.dart';
Future<void> main() async {
print('AdditionUsecase');
const AdditionUsecase addition = AdditionUsecase();
print('> addition(2)');
await display(() => addition(2));
print('> addition(null)');
await display(() => addition(null));
print('> addition(-2)');
await display(() => addition(-2));
print('DivisionUsecase');
const DivisionUsecase division = DivisionUsecase();
print('> division(null)');
await display(() => division(null));
print('> division((2, 0))');
await display(() => division((2, 0)));
print('> division((4, 2))');
await display(() => division((4, 2)));
print('GeneratorUsecase');
const GeneratorUsecase generator = GeneratorUsecase();
print('> generator()');
generator().listen(
print,
onError: print,
onDone: () => print('Done'),
);
}
class AdditionUsecase extends Usecase<int, int> {
const AdditionUsecase();
[@override](/user/override)
FutureOr<int> execute(int params) async => params + params;
[@override](/user/override)
FutureOr<ConditionsResult> checkPostconditions(int? result) {
if (result == null) {
return ConditionsResult(isValid: false, message: 'Result is null');
}
if (result < 0) {
return ConditionsResult(isValid: false, message: 'Result is negative');
}
return ConditionsResult(isValid: true);
}
}
class DivisionUsecase extends Usecase<(int, int), double> {
const DivisionUsecase();
[@override](/user/override)
FutureOr<ConditionsResult> checkPreconditions((int, int)? params) {
if (params == null) {
return ConditionsResult(isValid: false, message: 'Params is null');
}
if (params.$2 == 0) {
return ConditionsResult(isValid: false, message: 'Cannot divide by 0');
}
return ConditionsResult(isValid: true);
}
[@override](/user/override)
FutureOr<double> execute((int, int) params) async => params.$1 / params.$2;
}
class GeneratorUsecase extends NoParamsStreamUsecase<int> {
const GeneratorUsecase();
[@override](/user/override)
Stream<int> execute() async* {
for (int i = 0; i < 5; i++) {
await Future<void>.delayed(const Duration(seconds: 1));
yield i;
}
}
}
更多关于Flutter通用用例插件generic_usecase的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter通用用例插件generic_usecase的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用generic_usecase
插件的一个示例。这个插件通常用于实现业务逻辑层的通用用例模式,使得代码更加模块化和可测试。
首先,确保你已经在pubspec.yaml
文件中添加了generic_usecase
依赖:
dependencies:
flutter:
sdk: flutter
generic_usecase: ^最新版本号 # 请替换为实际的最新版本号
然后运行flutter pub get
来安装依赖。
定义用例和数据模型
假设我们有一个简单的用户数据模型和一个获取用户信息的用例。
用户数据模型
class User {
final String id;
final String name;
User({required this.id, required this.name});
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as String,
name: json['name'] as String,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
};
}
}
定义获取用户信息的用例
我们需要定义一个获取用户信息的用例。这通常包括一个输入(请求参数)和一个输出(响应数据或错误)。
import 'package:dartz/dartz.dart';
import 'package:generic_usecase/generic_usecase.dart';
class GetUserInput {
final String userId;
GetUserInput({required this.userId});
}
class GetUserOutput {
final Either<String, User> result;
GetUserOutput({required this.result});
}
class GetUserUseCase implements UseCase<GetUserOutput, GetUserInput> {
final UserRepository userRepository;
GetUserUseCase({required this.userRepository});
@override
Future<GetUserOutput> execute(GetUserInput input) async {
try {
User user = await userRepository.getUser(input.userId);
return GetUserOutput(result: Right(user));
} catch (e) {
return GetUserOutput(result: Left(e.toString()));
}
}
}
定义数据仓库
数据仓库通常用于处理数据的获取和存储。在这个例子中,我们假设有一个UserRepository
。
import 'dart:convert';
import 'package:http/http.dart' as http;
class UserRepository {
final String apiUrl = 'https://api.example.com/users/';
Future<User> getUser(String userId) async {
final response = await http.get(Uri.parse('$apiUrl$userId'));
if (response.statusCode == 200) {
Map<String, dynamic> body = jsonDecode(response.body);
return User.fromJson(body);
} else {
throw Exception('Failed to load user');
}
}
}
使用用例
现在,我们可以在Flutter组件中使用这个用例来获取用户信息。
import 'package:flutter/material.dart';
import 'package:generic_usecase/generic_usecase.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: UserProfilePage(),
);
}
}
class UserProfilePage extends StatefulWidget {
@override
_UserProfilePageState createState() => _UserProfilePageState();
}
class _UserProfilePageState extends State<UserProfilePage> {
late GetUserUseCase _getUserUseCase;
late Future<GetUserOutput> _futureUserOutput;
@override
void initState() {
super.initState();
UserRepository userRepository = UserRepository();
_getUserUseCase = GetUserUseCase(userRepository: userRepository);
_futureUserOutput = _getUserUseCase.execute(GetUserInput(userId: '1'));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('User Profile'),
),
body: FutureBuilder<GetUserOutput>(
future: _futureUserOutput,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
GetUserOutput output = snapshot.data!;
return output.result.fold(
(error) => Text('Error: $error'),
(user) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('ID: ${user.id}'),
Text('Name: ${user.name}'),
],
),
);
} else {
return CircularProgressIndicator();
}
},
),
);
}
}
这个示例展示了如何使用generic_usecase
插件来定义和执行一个获取用户信息的用例。你可以根据自己的需求进一步扩展和修改这个示例。