Flutter操作结果管理插件operation_result的使用

Flutter操作结果管理插件operation_result的使用

本包起源于在API调用中指定预期错误并更方便地处理这些错误的需求。原始的解决方法是在函数签名中使用注释:

// Throws Unauthorized, InvalidFormField
Future<Profile> editProfile(EditProfile editProfile) async

但是,注释不会被编译器或运行时检查。因此,它们会变得过时且不可靠。

通过 operation_result 包,可以在以下方式中指定错误:

AsyncResult<Profile, Errors2<Unauthorized, InvalidFormField>> editProfile(EditProfile editProfile) async

使用示例

该示例展示了如何使用 operation_result 来处理API错误。

import 'package:operation_result/operation_result.dart';

// 全局变量仅为了简化示例。
late final HttpClient client;

AsyncResult<Response, Errors2<Unauthorized, ValidationError>> httpPost(String path, Object data) async {
  final response = await client.post(path, data);

  if (response.statusCode == 200) {
    return success2(response); // 成功时返回
  }

  if (response.statusCode == 401) {
    return failure2(Unauthorized()); // 401错误时返回
  }

  if (response.statusCode == 400) {
    final List<dynamic> errors = response.data['errors'];
    return failures2(errors.map((e) => ValidationError.fromMap(e))); // 400错误时返回多个错误
  }

  throw Exception('Unexpected status code: ${response.statusCode}');
}

AsyncResult<AuthToken, Errors2<InvalidCredentials, EmailNotConfirmed>> login(String login, String password) async {
  final response = await httpPost('/auth/login', {'login': login, 'password': password});
  return response.forward2(
      success: (r) => AuthToken.parse(r.data), // 成功时解析数据
      failure: (e) => switch (e) { // 处理失败情况
            (ValidationError e) when e.code == 'email-not-confirmed' => EmailNotConfirmed(), // 特定错误类型
            (Unauthorized _) => InvalidCredentials(), // 特定错误类型
            _ => e // 其他错误类型
          });
}

AsyncResult<Profile, Errors2<Unauthorized, InvalidFormField>> editProfile(Profile editProfile) async {
  final response = await httpPost('/profile/edit', editProfile.toMap());
  return response.forward2(
    success: (response) => Profile.fromMap(response.data), // 成功时解析数据
    failure: (e) => switch (e) { // 处理失败情况
      (Unauthorized e) => e, // 特定错误类型
      (ValidationError e) when e.code == 'incorrect-value' => InvalidFormField(fieldName: e.incorrectValue, message: e.message), // 特定错误类型
      _ => e // 其他错误类型
    },
  );
}

class LoginState {
  final Form form;
  final AuthStorage authStorage;
  final Router router;

  void onLoginPressed() async {
    final loginResult = await login('login', 'password');
    if (loginResult.hasError<InvalidCredentials>()) {
      form.setFailure(['Password or email are incorrect']);
      return;
    }

    if (loginResult.hasError<EmailNotConfirmed>()) {
      router.redirectToConfirmationPage();
      return;
    }

    authStorage.storeToken(loginResult.value);
    router.goToHomePage();
  }

  const LoginState({
    required this.form,
    required this.authStorage,
    required this.router,
  });
}

class EditProfileState {
  final Form form;
  final Router router;

  void onEditProfilePressed() async {
    final editResult = await editProfile(Profile(fistName: form.get('fistName'), lastName: form.get('lastName')));
    if (editResult.hasError<Unauthorized>()) {
      router.redirectToLoginPage();
      return;
    }

    final filedErrors = editResult.getErrors<InvalidFormField>();
    if (filedErrors.isNotEmpty) {
      for (final filedError in filedErrors) {
        form.setError(filedError.fieldName, filedError.message);
      }
      return;
    }

    if (editResult.failed) {
      form.setFailure(['Unhandled errors: ${editResult.errors}']);
      return;
    }

    form.setSuccess();
  }

  const EditProfileState({
    required this.form,
    required this.router,
  });
}

class AuthToken {
  final String token;

  AuthToken(this.token);

  factory AuthToken.parse(Map<String, dynamic> map) {
    return AuthToken(map['token']);
  }
}

class Profile {
  final String fistName;
  final String lastName;

  const Profile({
    required this.fistName,
    required this.lastName,
  });

  factory Profile.fromMap(Map<String, dynamic> map) {
    return Profile(
      fistName: map['firstName'],
      lastName: map['lastName'],
    );
  }

  Map<String, dynamic> toMap() {
    return {
      'firstName': fistName,
      'lastName': lastName,
    };
  }
}

class Unauthorized {
  const Unauthorized();
}

class EmailNotConfirmed {
  const EmailNotConfirmed();
}

class InvalidCredentials {
  const InvalidCredentials();
}

class InvalidFormField {
  final String fieldName;
  final String message;

  const InvalidFormField({
    required this.fieldName,
    required this.message,
  });
}

class ValidationError {
  final String code;
  final String incorrectValue;
  final String message;

  const ValidationError({
    required this.code,
    required this.incorrectValue,
    required this.message,
  });

  factory ValidationError.fromMap(Map<String, dynamic> map) {
    return ValidationError(
      code: map['code'],
      incorrectValue: map['incorrectValue'],
      message: map['message'],
    );
  }
}

class Response {
  final int statusCode;
  final dynamic data;

  Response(this.statusCode, this.data);
}

abstract class HttpClient {
  Future<Response> post(String url, Object? data);
}

abstract class Router {
  void redirectToConfirmationPage();

  void goToHomePage();

  void redirectToLoginPage();
}

abstract class AuthStorage {
  void storeToken(AuthToken token);
}

abstract class Form {
  void setFailure(List<String> errors);

  void setError(String fieldName, String message);

  void setSuccess();

  String get(String fieldName);
}

更多关于Flutter操作结果管理插件operation_result的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter操作结果管理插件operation_result的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter开发中,operation_result 插件用于管理操作结果,可以帮助开发者更方便地处理异步操作的成功与失败情况。以下是如何在Flutter项目中使用 operation_result 插件的一个示例代码案例。

首先,确保你已经在 pubspec.yaml 文件中添加了 operation_result 依赖:

dependencies:
  flutter:
    sdk: flutter
  operation_result: ^最新版本号  # 请替换为实际的最新版本号

然后,运行 flutter pub get 来获取依赖。

接下来,我们编写一个简单的示例,展示如何使用 OperationResult 来管理一个异步操作的结果。

示例代码

  1. 创建一个 Dart 文件 operation_result_example.dart,定义操作逻辑
import 'package:flutter/material.dart';
import 'package:operation_result/operation_result.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: OperationResultExample(),
    );
  }
}

class OperationResultExample extends StatefulWidget {
  @override
  _OperationResultExampleState createState() => _OperationResultExampleState();
}

class _OperationResultExampleState extends State<OperationResultExample> {
  OperationResult<String, String> _operationResult =
      OperationResult.idle(); // 初始化 OperationResult

  void _performOperation() async {
    setState(() {
      _operationResult = OperationResult.loading('Loading...');
    });

    // 模拟一个异步操作,比如网络请求
    await Future.delayed(Duration(seconds: 2));

    // 根据操作结果更新 OperationResult
    if (true) { // 这里可以替换为实际的成功或失败条件
      setState(() {
        _operationResult = OperationResult.success('Operation successful!');
      });
    } else {
      setState(() {
        _operationResult = OperationResult.error('Operation failed.');
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('OperationResult Example'),
      ),
      body: Center(
        child: OperationResultBuilder<String, String>(
          operationResult: _operationResult,
          builder: (context, result, loading, error) {
            if (loading != null) {
              return CircularProgressIndicator();
            } else if (result != null) {
              return Text(
                result,
                style: TextStyle(color: Colors.green),
              );
            } else if (error != null) {
              return Text(
                error,
                style: TextStyle(color: Colors.red),
              );
            } else {
              return ElevatedButton(
                onPressed: _performOperation,
                child: Text('Perform Operation'),
              );
            }
          },
        ),
      ),
    );
  }
}

解释

  1. 初始化 OperationResult:在 _OperationResultExampleState 类中,我们初始化了一个 OperationResult<String, String> 对象,初始状态为 idle

  2. 执行异步操作:在 _performOperation 方法中,我们首先将 OperationResult 的状态设置为 loading,并显示加载提示。然后模拟一个异步操作(使用 Future.delayed),根据操作的成功或失败更新 OperationResult 的状态。

  3. 构建 UI:使用 OperationResultBuilder 小部件根据 OperationResult 的当前状态构建相应的 UI。如果处于加载状态,显示 CircularProgressIndicator;如果操作成功,显示成功信息;如果操作失败,显示错误信息;如果处于空闲状态,显示一个按钮来触发操作。

通过这种方式,operation_result 插件使得管理异步操作的结果变得更加简单和直观。希望这个示例代码能帮助你更好地理解和使用 operation_result 插件。

回到顶部