Flutter便捷类型定义插件convenience_types的使用
Flutter便捷类型定义插件 convenience_types
的使用
概述
convenience_types
是由 Capyba 开发的包,旨在提供一些常用的便捷类型,以帮助开发者在 Flutter 项目中更安全、高效地处理异步任务、可选值、表单字段和请求状态等常见场景。
动机
在开发多个项目的过程中,Capyba 发现了一些有助于提高代码安全性、减少错误并提升生产力的类型。为了在不同项目之间共享这些类型,并可能启发其他开发者使用这些类型,他们创建了这个包。
目录
开始使用
要在 Flutter 项目中安装并使用该包,请运行以下命令:
flutter pub add convenience_types
如果你是在 Dart 项目中,请运行:
dart pub add convenience_types
类型
Result
Result
是一个泛型联合类型,用于表示异步任务的两种可能结果:成功(Success)或失败(Failure)。它提供了一种声明式的方式来处理异步任务的结果。
示例:
import 'package:convenience_types/types/result.dart';
Future<Result<String>> asyncTaskReturningStringResult() async {
// 模拟异步任务
await Future.delayed(Duration(seconds: 2));
return Success('Hello, World!');
}
void handleResult() async {
Result<String> result = await asyncTaskReturningStringResult();
result.handle(
onSuccess: (String data) {
print("Success: $data");
},
onFailure: (AppError error) {
print("Failure: ${error.slug}");
},
);
}
Maybe
Maybe
是一个泛型联合类型,用于安全地处理可选值。它可以表示两种状态:有值(Just)或无值(Nothing)。
示例:
import 'package:convenience_types/types/maybe.dart';
void handleMaybeValue() {
Maybe<String> maybeValue = Just('test');
final debugValue = maybeValue.map(
nothing: (_) => "No value",
just: (data) => data.value,
);
print(debugValue); // 输出: test
}
RequestStatus
RequestStatus
是一个泛型联合类型,用于表示请求的不同状态:空闲(Idle)、加载中(Loading)、成功(Succeeded)或失败(Failed)。
示例:
import 'package:convenience_types/types/request_status.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
[@override](/user/override)
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
RequestStatus<Number> numberRequestStatus = const Idle();
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: numberRequestStatus.when(
idle: () => const Text('Idle'),
loading: () => const CircularProgressIndicator(),
succeeded: (data) => Text('Succeeded: ${data.text}'),
failed: (error) => Text('Failed: ${error.slug}'),
),
),
);
}
}
FormField
FormField
是一个泛型类,用于表示表单中的字段。它结合了字段名称和字段数据(一个 Maybe
类型),并且提供了方便的方法来进行验证和序列化。
示例:
import 'package:convenience_types/types/form_field.dart' as form;
import 'package:convenience_types/util/form_utils.dart';
@freezed
class FormExample with _$FormExample, FormUtils {
const FormExample._();
const factory FormExample({
@Default(form.FormField(name: 'firstFieldJsonName')) form.FormField<String> firstField,
@Default(form.FormField(name: 'secondFieldJsonName')) form.FormField<String> secondField,
}) = _FormExample;
Result<String> get firstFieldValidation => validateField(
field: firstField.field,
validators: <String? Function(String)>[
// 列表验证器
],
);
Map<String, dynamic> toJson() => fieldsToJson([firstField, secondField]);
}
AppError
AppError
是一个抽象类,用于建模应用程序中的错误。它包含一些预设的具体错误实现,如 HttpError
、CacheError
等。
工具
FormUtils
FormUtils
是一个 Dart Mixin,用于方便地处理表单的验证和序列化。
SeedTestStateMixin
SeedTestStateMixin
是一个 Mixin,用于帮助在测试中播种状态。
示例:
class MyStateNotifier extends StateNotifier<MyState> with SeedTestStateMixin<MyState> {}
// 在测试中使用:
test('Test description', () {
myStateNotifier.setSeedState(mySeedState);
// 测试主体
});
完整示例 Demo
以下是一个完整的示例,展示了如何在 Flutter 应用中使用 convenience_types
包。
import 'package:flutter/material.dart';
import 'package:convenience_types/errors/errors.dart';
import 'package:convenience_types/types/form_field.dart' as form;
import 'package:convenience_types/types/maybe.dart';
import 'package:convenience_types/types/request_status.dart';
import 'package:convenience_types/types/result.dart';
import 'package:dio/dio.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Convenience Types Example'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
[@override](/user/override)
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
form.FormField<String> numberField = const form.FormField(name: 'number');
RequestStatus<Number> numberRequestStatus = const Idle();
Dio client = Dio(BaseOptions(baseUrl: 'http://numbersapi.com/'));
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Enter a number to search for a trivia:'),
TextField(
onChanged: _onFieldChanged,
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: _onButtonPressed,
child: const Text('Search'),
),
const SizedBox(height: 8),
numberRequestStatus.when(
idle: () => const SizedBox(),
loading: () => const LinearProgressIndicator(),
succeeded: (data) => Text('You\'ve just searched ${data.number}, here is a trivia for it: ${data.text}!'),
failed: (error) => Text('Something didn\'t go well! Sorry! hint: ${error.slug}'),
),
],
),
),
),
);
}
void _onFieldChanged(String value) {
numberField = numberField.copyWith(field: Just(value));
}
void _onButtonPressed() async {
setState(() {
numberRequestStatus = const Loading();
});
Result<Number> numberRes;
try {
numberRes = (await client.get('${numberField.field.getOrElse('')}?json')).mapSuccess(_mapRequestToNumber);
} catch (e) {
numberRes = Failure(AppUnknownError(slug: e.toString()));
}
setState(() {
numberRequestStatus = RequestStatus.fromResult(numberRes);
});
}
Result<Number> _mapRequestToNumber(data) {
try {
return Success(Number.fromJson(data));
} catch (e) {
return Failure(HttpUnknownError(slug: e.toString()));
}
}
}
class Number {
final String text;
final int number;
final String type;
Number(this.text, this.number, this.type);
factory Number.fromJson(Map<String, dynamic> json) {
try {
return Number(json['text']!, json['number']!, json['type']!);
} catch (e) {
throw Exception(e);
}
}
}
更多关于Flutter便捷类型定义插件convenience_types的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter便捷类型定义插件convenience_types的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用convenience_types
插件的示例代码。convenience_types
是一个方便的类型定义插件,可以简化一些常见的类型定义。
1. 添加依赖
首先,你需要在pubspec.yaml
文件中添加convenience_types
依赖:
dependencies:
flutter:
sdk: flutter
convenience_types: ^x.y.z # 请替换为最新版本号
然后运行flutter pub get
来安装依赖。
2. 导入插件
在你需要使用convenience_types
的Dart文件中导入插件:
import 'package:convenience_types/convenience_types.dart';
3. 使用便捷类型
以下是一些常见的使用示例:
使用NonEmptyString
NonEmptyString
是一个非空字符串类型,确保字符串不为空。
void printNonEmptyString(NonEmptyString nonEmptyString) {
print('The non-empty string is: $nonEmptyString');
}
void main() {
final NonEmptyString name = NonEmptyString('Alice');
printNonEmptyString(name); // 正常输出
// printNonEmptyString(NonEmptyString('')); // 这将导致编译时错误
}
使用PositiveInt
和 PositiveDouble
PositiveInt
和 PositiveDouble
确保数值为正数。
void printPositiveNumber(PositiveInt positiveInt, PositiveDouble positiveDouble) {
print('The positive integer is: $positiveInt');
print('The positive double is: $positiveDouble');
}
void main() {
final PositiveInt age = PositiveInt(30);
final PositiveDouble height = PositiveDouble(1.75);
printPositiveNumber(age, height); // 正常输出
// final PositiveInt invalidAge = PositiveInt(-5); // 这将导致编译时错误
// final PositiveDouble invalidHeight = PositiveDouble(-1.5); // 这将导致编译时错误
}
使用Nullable
和 NonNull
Nullable
和 NonNull
可以用来明确标识可空和不可空类型。
void printValues(Nullable<String> nullableString, NonNull<int> nonNullInt) {
print('The nullable string is: $nullableString');
print('The non-null integer is: $nonNullInt');
}
void main() {
final Nullable<String> maybeName = Nullable<String>('Bob');
final NonNull<int> id = NonNull<int>(123);
printValues(maybeName, id); // 正常输出
// printValues(Nullable<String>(null), id); // 允许为null,不会报错
// final NonNull<int> invalidId = NonNull<int>(null); // 这将导致编译时错误
}
总结
通过使用convenience_types
插件,你可以更清晰地定义和确保类型安全,减少运行时的类型错误。以上示例展示了如何使用一些常见的便捷类型定义,但插件可能包含更多类型,具体请参考官方文档以获取完整信息。