mydomain
No ADS
No ADS

FlutterArtist Dio

  1. Cấu hình FlutterArtist Dio
  2. Ví dụ sử dụng FlutterArtist Dio
  3. FlutterArtistDio
FlutterArtist DIO là một thư viện, một trình bao bọc (wrapper) của gói thư viện DIO giúp bạn dễ dàng thực hiện các cuộc gọi Rest và ghi lại nhật ký. FlutterArtist DIO là một phần của dự án FlutterArtist nhưng bạn cũng có thể sử dụng thư viện này một cách độc lập.
A log record generated by FlutterArtist DIO includes the following information:
  • Data source URL
  • Method (GET, POST,...)
  • Request-Headers and parameters
  • Authorization
  • Response-Headers, Response Time, Response Status, Error Info,...
  • Response-Body
The methods of FlutterArtist DIO typically return the Future<ApiResult<DATA>> data type:
  • If an error occurs: You can access the error information via the ApiResult.error property (FlutterArtist DIO automatically extracts the error to assign to ApiError.errorMessage and ApiError.errorDetails using a provided or default ErrorExtractor).
  • If successful: You obtain the data through the ApiResult.data property (FlutterArtist DIO automatically converts the data into a Dart Object using a provided converter function).
class ApiResult<D> {
  final D? data;
  final int? statusCode;
  final String? statusMessage;
  final ApiError? error;
}

class ApiError extends AppError {
  ApiErrorType? errorType;
  int? statusCode;
  String? statusMessage;
  String? originErrorText;
  dynamic Function(Map<String, dynamic> value)? usedConverter;

  ApiError({
    this.statusCode,
    this.statusMessage,
    this.errorType,
    this.originErrorText,
    required super.errorMessage,
    super.errorDetails,
    this.usedConverter,
  });
}
No ADS
The real strength of FlutterArtist DIO lies in its ability to standardize server responses. In the real world, each API might return a different error structure, but through ErrorExtractor, we unify them into a single ApiError format.
Rest Debug Viewer
You can view FlutterArtist DIO's logs through the built-in Rest Debug Viewer tool.
  • FlutterArtist Rest Debug Viewer
  • Giới thiệu về FlutterArtist
  • Download FlutterArtist Demo

1. Cấu hình FlutterArtist Dio

Khai báo các thư viện cần thiết trong file pubspec.yaml:
pubspec.yaml (*)
dependencies: 
  dio:  ^5.7.0  
  flutter_artist_dio:   
  json_serializable: ^6.11.1
  json_annotation: ^4.9.0
  fresh_dio: ^0.6.0
FlutterArtist Dio được tạo ra như một phần của dự án FlutterArtist vì vậy cách cấu hình và sử dụng nó rất đơn giản. Bạn có thể tìm thấy mã đầy đủ trong "FlutterArtist Demo":
Cấu hình appBaseURL trong một file riêng biệt.
rest_config.dart
// =============================================================================
//  APP BASE URL: **************************************************************
// =============================================================================

///
/// Prefix URL for Rest API.
///

const appBaseURL = "http://localhost:8080";

// =============================================================================
// STATIC RESOURCE BASE URL: ***************************************************
// =============================================================================

///
/// Prefix URL for Static Resources (Profile Image, Icon,..)
///
const staticResourceBaseURL = "http://localhost:8080";

// =============================================================================
//
// =============================================================================

String? getStaticResourceURL(String? path) {
  if (path == null || path.isEmpty) {
    return null;
  }
  if (path.startsWith("http://") || path.startsWith("https://")) {
    return path;
  }
  return "$staticResourceBaseURL$path";
}
TokenStorage
Khi bạn gửi một yêu cầu tới một Rest API bạn cần phải đính kèm một Token vào headers. Thông tin này sẽ được kiểm tra tại máy chủ Rest để xem bạn có được phép tiếp cận nguồn dữ liệu này hay không, nếu Token không hợp lệ hoặc hết hạn yêu cầu của bạn sẽ bị từ chối.
  • TokenStorage là một interface triển khai phương thức readAccessToken() để đọc một Token đã được lưu trữ ở đâu đó trong ứng dụng sau khi người dùng đã đăng nhập thành công.
  • Nếu một Token đã hết hạn được đính kèm trong headers của yêu cầu gửi đến máy chủ Rest, yêu cầu này sẽ bị từ chối, FlutterArtist Dio sẽ tự động gửi một yêu cầu khác để làm mới lại Token, và gửi lại yêu cầu đã thất bại trước đó với Token mới. Phương thức saveAccessToken() sẽ được gọi tự động bởi FlutterArtist Dio để lưu trữ Token mà bạn vừa nhận được. Phương thức clearTokens() sẽ được gọi tự động bởi FlutterArtist Dio nếu việc làm mới Token thất bại, các Token(s) hết hạn cần phải được xoá khỏi khu vực lưu trữ của ứng dụng và người dùng cần phải đăng nhập lại mới có thể gọi Rest API.
token_storage_impl.dart
class TokenStorageImpl implements TokenStorage {
  @override
  Future<void> clearTokens() async {
    AppUserData? user = FlutterArtist.loggedInUser as AppUserData?;
    if (user != null) {
      user.accessToken = null;
      await FlutterArtist.setOrUpdateLoggedInUser(user);
    }
  }

  @override
  Future<String?> readAccessToken() async {
    AppUserData? user = FlutterArtist.loggedInUser as AppUserData?;
    return user?.accessToken;
  }

  @override
  Future<void> saveAccessToken(String newAccessToken) async {
    AppUserData? user = FlutterArtist.loggedInUser as AppUserData?;
    if (user != null) {
      user.accessToken = newAccessToken;
      await FlutterArtist.setOrUpdateLoggedInUser(user);
    }
  }
}
  • FlutterArtist Login
FlutterArtistDio
FlutterArtistDio Constructor
FlutterArtistDio({
  required BaseOptions? baseOptions,
  required TokenStorage tokenStorage,
  required WriteTokenToHeaders writeTokenToHeaders,
  required ReadTokenFromHeaders readTokenFromHeaders,
})


typedef WriteTokenToHeaders =
    Function(Map<String, dynamic> headers, String accessToken);

typedef ReadTokenFromHeaders = 
    String? Function(Map<String, dynamic> headers);    
Để tạo một đối tượng FlutterArtistDio bạn cần cung cấp cách Token được đính kèm vào headers của yêu cầu và cách lấy ra Token từ headers.
fa_dio.dart
const String _bearer = "Bearer";

final flutterArtistDio = FlutterArtistDio(
  baseOptions: BaseOptions(
    baseUrl: appBaseURL,
  ),
  tokenStorage: TokenStorageImpl(),
  writeTokenToHeaders: (Map<String, dynamic> headers, String accessToken) {
    headers['Authorization'] = '$_bearer $accessToken';
  },
  readTokenFromHeaders: (Map<String, dynamic> headers) {
    String? auth = headers['Authorization'];
    if (auth == null) {
      return null;
    }
    if (auth.startsWith("$_bearer ")) {
      return auth.substring(_bearer.length + 1);
    }
    return auth;
  },
);

///
/// To get BINARY.
/// See Demo: [17a].
///
final flutterArtistDio4Download = FlutterArtistDio(
  baseOptions: BaseOptions(
    baseUrl: appBaseURL,
    responseType: ResponseType.bytes, // FIX Error! (For download File only).
  ),
  tokenStorage: TokenStorageImpl(),
  writeTokenToHeaders: (Map<String, dynamic> headers, String accessToken) {
    headers['Authorization'] = accessToken;
  },
  readTokenFromHeaders: (Map<String, dynamic> headers) {
    String? auth = headers['Authorization'];
    if (auth == null) {
      return null;
    }
    return auth;
  },
);

2. Ví dụ sử dụng FlutterArtist Dio

Trong ví dụ này chúng ta sẽ sử dụng FlutterArtist Dio để gọi một Rest API và nhận được một văn bản JSON, sau đó chuyển đổi nó thành một đối tượng Dart.
{
   "id": "USD",
   "name": "US Dollar",
   "symbol": "$", 
   "description": "The USD (United States dollar) ..."
}
Chú ý: Để chuyển đổi một đối tượng Dart sang JSON và ngược lại bạn nên sử dụng gói thư viện json_serializable hoặc dart_json_mapper.
  • Dart json serializable
currency_data.dart
@JsonSerializable()
class CurrencyData implements Identifiable<String> {
  @override
  @JsonKey(name: 'id')
  String id;

  @JsonKey(name: 'symbol')
  String symbol;

  @JsonKey(name: 'name')
  String name;

  @JsonKey(name: 'description')
  String? description;

  CurrencyData(this.id, this.symbol, this.name);

  CurrencyData.named({
    required this.id,
    required this.symbol,
    required this.name,
    this.description,
  });

  CurrencyInfo toCurrencyInfo() {
    return CurrencyInfo.named(id: id, name: name, symbol: symbol);
  }

  factory CurrencyData.fromJson(Map<String, dynamic> json) =>
      _$CurrencyDataFromJson(json);

  Map<String, dynamic> toJson() => _$CurrencyDataToJson(this);

  @override
  String toString() {
    return "${getClassName(this)}($id, $name)";
  }
}
Lớp CurrencyRestProvider bao gồm các phương thức gọi tới các Rest API có liên quan tới dữ liệu Currency.
class CurrencyRestProvider { 

  // /rest/currency/VND
  Future<ApiResult<CurrencyData>> find({
    required String currencyId,
  }) async {
    Map<String, dynamic>? queryParameters = {};

    // /rest/currency/VND
    ApiResult<CurrencyData> result = await flutterArtistDio.jsonGet(
      "/rest/currency/$currencyId",
      queryParameters: queryParameters,
      converter: CurrencyData.fromMap,
    );
    return result;
  }

  ...
}
Sau đó bạn có thể sử dụng các phương thức này ở một nơi nào đó trong ứng dụng của bạn.
final  currencyRestProvider = CurrencyRestProvider();

ApiResult<CurrencyData> result = await currencyRestProvider.find(currencyId: "VND");

if(result.isError())  {
  print("Error Message: ${result.errorMessage}");
  print("Error Details: ${result.errorDetails}");
} else  {
  CurrencyData? data = result.data;
  print("Currency Data: $data");
}
  • FlutterArtist ApiResult

3. FlutterArtistDio

Lớp FlutterArtistDio cũng có duy nhất một constructor, đối tượng được tạo ra là một trình bao bọc của một đối tượng Dio.
  • Chú ý: Các tham số của constructor đã được giải thích ở phần trên của bài viết.
FlutterArtistDio({
    required BaseOptions? baseOptions,
    required TokenStorage tokenStorage,
    required WriteTokenToHeaders writeTokenToHeaders,
    required ReadTokenFromHeaders readTokenFromHeaders,
  }) 
Các phương thức của lớp FlutterArtistDio:
Future<ApiResult<D>> jsonGet<D>(
    String path, { 
    required Converter<D>? converter,
    bool showDebug = false,
    //
    Object? data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onReceiveProgress, 
});

Future<ApiResult<D>> jsonPost<D>(
    String path, { 
    required Converter<D>? converter,
    bool showDebug = false,
    //
    Object? data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
});

Future<ApiResult<D>> jsonPut<D>(
    String path, { 
    required Converter<D>? converter,
    bool showDebug = false,
    //
    Object? data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
}); 

Future<ApiResult<D>> jsonDelete<D>(
    String path, { 
    required Converter<D>? converter,
    bool showDebug = false,
    //
    Object? data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
})

Future<ApiResult<List<int>?>> binaryGet(
  String path, {
  bool showDebug = false,
  //
  ProgressCallback? onReceiveProgress,
  Map<String, dynamic>? queryParameters,
  CancelToken? cancelToken,
  Object? data,
  Options? options,
})
No ADS
No ADS