Flutter网络封装

依赖库

  • dio: dio 是一个强大的 HTTP 网络请求库
  • pretty_dio_logger: 基于拦截器的简明易读的请求日志打印
  • json_annotation: 用于生成 JSON 序列化与反序列化代码
  • retrofit: 对 dio 进一步封装

目录

1
2
3
4
5
6
7
8
9
.
├── api
│   └── example_api.dart
├── model
│   └── base_response.dart
├── services
│   └── api_service.dart
└── utils
└── request_helper.dart

返回数据基类base_response.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@JsonSerializable(genericArgumentFactories: true)
class BaseResponse<T> {
// 业务中 data 和 msg 都可能为 null
final T? data;
final int code;
final String? msg;

BaseResponse({
this.msg,
this.data,
required this.code,
});

factory BaseResponse.fromJson(
Map<String, dynamic> json,
T Function(dynamic json) fromJsonT,
) => _$BaseResponseFromJson<T>(json, fromJsonT);

Map<String, dynamic> toJson(Object? Function(T value) toJsonT) =>
_$BaseResponseToJson<T>(this, toJsonT);
}

接口定义类example_api.dart

1
2
3
4
5
6
7
8
@RestApi()
abstract class ExampleApi {
factory ExampleApi(Dio dio) = _ExampleApi;

// 使用BaseResponse包裹业务实体
@GET("/api/resources/{path}")
Future<BaseResponse<Data>?> fetchData(@Path() String path);
}

网络请求单例api_service.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class ApiService {
late Dio _dio;

late ExampleApi exampleApi;

factory ApiService() => _instance;

static final ApiService _instance = ApiService._internal();

ApiService._internal();

void initialize(String baseUrl) {
_dio = Dio(
BaseOptions(
baseUrl: baseUrl,
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
headers: {"Content-Type": "application/json"},
),
);
_dio.interceptors
..add(TokenInterceptor())
..add(UserAgentInterceptor())
..add(
PrettyDioLogger(
request: false,
requestHeader: true,
requestBody: false,
responseBody: true,
responseHeader: false,
error: true,
compact: true,
maxWidth: 90
),
);
exampleApi = ExampleApi(_dio);
}
}

工具方法request_helper.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Future<Result<T?, Exception>> requestSafety<T>(
Future<BaseResponse<T>?> Function() block,
) async {
try {
final response = await block();
if (response is BaseResponse<T>) {
if (response.code ~/ 100 == 2) {
return Success(response.data);
} else if (response.code == 401) {
// TODO 处理401
return Failure(Exception(response.msg));
} else if (response.code >= 1000) {
// TODO 处理业务code
return Failure(Exception(response.msg));
}
}
return Failure(Exception('response is null'));
} catch (e) {
return Failure(Exception(e.toString()));
}
}

具体使用

  1. 添加业务实体,使用JsonSerializable注解生成Json序列化代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @JsonSerializable()
    class Data{
    final String name;

    const Data({
    required this.name,
    });

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

    Map<String, dynamic> toJson() => _$DataToJson(this);
    }
  2. 添加业务接口,注意返回值为Future<BaseResponse<实体>?>

    1
    2
    @POST("/api/resources")
    Future<BaseResponse<Data>?> fetchData();
  3. 调用接口

    1
    2
    3
    4
    5
    final result = await requestSafety(() => _apiService.resourcesApi.fetchData());
    final data = result.success;
    if (data != null) {
    debugPrint(data.name);
    }
Author

xiaoyu

Posted on

2024-05-14

Updated on

2024-05-14

Licensed under

You need to set install_url to use ShareThis. Please set it in _config.yml.
You forgot to set the business or currency_code for Paypal. Please set it in _config.yml.

Kommentare

You forgot to set the shortname for Disqus. Please set it in _config.yml.