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插件来定义和执行一个获取用户信息的用例。你可以根据自己的需求进一步扩展和修改这个示例。
 
        
       
             
             
            

