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);
    }

Flutter三方库

网络请求

包名 简介 地址
dio dio 是一个强大的 HTTP 网络请求库 https://pub.dev/packages/dio
pretty_dio_logger 基于拦截器的简明易读的请求日志打印 https://pub-web.flutter-io.cn/packages/pretty_dio_logger
native_dio_adapter 使用 cupertino_http 和 cronet_http 以适配器代理实现的原生网络请求功能 https://pub-web.flutter-io.cn/packages/native_dio_adapter
json_annotation 用于生成JSON序列化与反序列化代码 https://pub.dev/packages/json_annotation
retrofit 对dio进一步封装 https://pub.dev/packages/retrofit

图片

包名 简介 地址
cached_network_image 展示并缓存网络图片 https://pub.dev/packages/cached_network_image

弹窗

包名 简介 地址
flutter_smart_dialog 展示弹窗、加载弹窗、Toast https://pub.dev/packages/flutter_smart_dialog

时间日期(选取日期)

包名 简介 地址
dart_date dart_date provides the most comprehensive, yet simple and consistent toolset for manipulating Dart dates. https://pub.dev/packages/dart_date
calendar_date_picker A lightweight and customizable calendar picker https://pub.dev/packages/calendar_date_picker2
flutter_calendar_carousel Calendar widget for flutter that is swipeable horizontally. https://pub.dev/packages/flutter_calendar_carousel

下拉刷新

包名 简介 地址
easy_refresh EasyRefresh can easily implement pull-down refresh and pull-up load on Flutter applications. https://pub.dev/packages/easy_refresh

Flutter修改状态栏颜色

修改AppBar中状态栏颜色

AppBar控件中添加systemOverlayStyle参数,SystemUiOverlayStyle主要包含三个参数:

  1. (only for Android) statusBarColor:状态栏背景颜色;
  2. (only for Android)statusBarIconBrightness:状态栏字体和图标颜色,设置为Brightness.dark时为黑色字体和图标,Brightness.light时为白色字体和图标;
  3. (only for iOS)statusBarBrightness:状态栏字体和图标颜色,设置为Brightness.dark时为字白色字体和图标,Brightness.light时为黑色字体和图标;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
AppBar(
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Colors.green, // <-- SEE HERE
statusBarIconBrightness: Brightness.dark, //<-- For Android SEE HERE (dark icons)
statusBarBrightness: Brightness.light, //<-- For iOS SEE HERE (dark icons)
),
centerTitle: true,
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'assets/images/logo.png',
scale: 12,
),
const SizedBox(
width: 10,
),
const Text(
'Flutter',
style: TextStyle(color: Colors.black),
),
],
),
)

全局修改状态栏颜色

MaterialApp控件中指定theme参数,可以设置全局主题,theme参数类型为ThemeData,其中再传入appBarTheme参数用于指定标题栏主题。

AppBarTheme中指定systemOverlayStyle参数来设置状态栏颜色。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
appBarTheme: AppBarTheme(
iconTheme: IconThemeData(color: Colors.black),
color: Colors.deepPurpleAccent,
foregroundColor: Colors.black,
systemOverlayStyle: SystemUiOverlayStyle( //<-- SEE HERE
// Status bar color
statusBarColor: Colors.green,
statusBarIconBrightness: Brightness.dark,
statusBarBrightness: Brightness.light,
),
),
),
home: ChangeStatusBarColorDemo(),
);

没有AppBar时修改状态栏颜色

当没有AppBar控件又需要改变状态栏颜色时,可以使用AnnotatedRegion控件。

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
@override
Widget build(BuildContext context) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.dark, //<-- SEE HERE
child: Scaffold(
body: Center(
child: Column(
children: [
SizedBox(
height: 50,
),
Container(
width: 200,
height: 200,
color: Colors.red,
),
ElevatedButton(
onPressed: () {},
child: const Text(
'Elevated Button 1',
style: TextStyle(fontSize: 24),
),
),
],
),
),
),
);
}