一、概要
- 数据本地化存储
- shared_preferences
- 网络请求dio
- 基础数据转换实体类
一、包相关
shared_preferences是官方推荐的包,目前主流。可以根据官网的教程直接使用,但是最好还是在本地做下单例,可根据自己需要添加相应的方法。
import 'package:shared_preferences/shared_preferences.dart';
class LocalStore{
static SharedPreferences? _prefs;
static getInstance(){
inti();
}
static Future inti() async{
if(_prefs==null){
// ignore: invalid_use_of_visible_for_testing_member
SharedPreferences.setMockInitialValues({});
_prefs = await SharedPreferences.getInstance();
}
}
factory LocalStore() => getInstance();
/// get string.
/// * key
/// * defValue 空时默认返回值
static String? getString(String key, {String? defValue = ''}) {
return _prefs?.getString(key) ?? defValue;
}
/// put string.
/// * key
/// * value
static Future<bool>? putString(String key, String value) {
return _prefs?.setString(key, value);
}
}
二、网络数据请求
Dio是目前Flutter最流行的网络数据请求库,本项目也是使用此库。在使用前,需要对Dio做单例,以及定义些全局请求变量,请求拦截以便处理token。
相关文件存放在:services
2.1请求拦截,添加token
存在token的时候,带上token发送请求
/// * 请求添加Authorization token
class AuthInterceptor extends Interceptor {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
final String accessToken = SpUtil.getString(Constant.refreshToken).nullSafe;
if (accessToken.isNotEmpty) {
options.headers['Authorization'] = 'token $accessToken';
}
super.onRequest(options, handler);
}
}
2.2响应拦截
发出去得响应,当后端返回token失效的时候,我们需要重新发起新的请求,同时锁足Dio实例暂停其他接口发送,直到获取到新的token后解锁,其他的请求将继续执行,本次请求需再继续补发第一次的请求。
/// * 响应token验证拦截
class TokenInterceptor extends Interceptor {
Dio? _tokenDio;
Future<String?> getToken() async {
final Map<String, String> params = <String, String>{};
params['refresh_token'] = SpUtil.getString(Constant.refreshToken).nullSafe;
try {
//重新创建一个实例,跳开token验证拦截器
_tokenDio ??= Dio();
_tokenDio!.options = Services.instance.dio.options;
final Response response = await _tokenDio!.post<dynamic>('lgn/refreshToken', data: params);
if (response.statusCode == ExceptionHandle.success) {
return (json.decode(response.data.toString()) as Map<String, dynamic>)['access_token'] as String;
}
} catch (e) {
// Log.e('刷新Token失败!');
}
return null;
}
@override
Future<void> onResponse(Response response, ResponseInterceptorHandler handler) async {
//401代表token过期
if (response.statusCode == ExceptionHandle.unauthorized) {
final Dio dio = Services.instance.dio;
dio.lock();
final String? accessToken = await getToken(); // 获取新的accessToken
SpUtil.putString(Constant.accessToken, accessToken.nullSafe);
dio.unlock();
if (accessToken != null) {
// 重新请求失败接口
final RequestOptions request = response.requestOptions;
request.headers['Authorization'] = 'Bearer $accessToken';
final Options options = Options(
headers: request.headers,
method: request.method,
);
try {
/// 避免重复执行拦截器,使用tokenDio
final Response response = await _tokenDio!.request<dynamic>(
request.path,
data: request.data,
queryParameters: request.queryParameters,
cancelToken: request.cancelToken,
options: options,
onReceiveProgress: request.onReceiveProgress,
);
return handler.next(response);
} on DioError catch (e) {
return handler.reject(e);
}
}
}
super.onResponse(response, handler);
}
}
三、数据实体类
对于接口回来的数据类型,外层的数据格式基本是一致。但是具体数据是不一样的。这个时候,我们需要借助泛型来处理请求到的数据。
import 'package:json_annotation/json_annotation.dart';
@JsonSerializable(
genericArgumentFactories: true, fieldRename: FieldRename.snake)
class BaseEntity<T,H> {
@JsonKey(name: 'code')
final int code;
@JsonKey(name: 'message')
final String message;
@JsonKey(name: 'model')
final T model;
@JsonKey(name: 'models')
final List<H> models;
BaseEntity(this.code, this.message, this.model, this.models);
factory BaseEntity.fromJson(
Map<String, dynamic> json,
T Function(Map<String, dynamic> json) fromJsonT,
H Function(Object json) fromJsonH
) =>_$BaseEntityFromJson(json, fromJsonT,fromJsonH);
Map<String, dynamic> toJson(
Map<String, dynamic> Function(T value) toJsonT,
H Function(H value) toJsonH
) =>
_$BaseEntityToJson(this, toJsonT, toJsonH);
}
BaseEntity<T,H> _$BaseEntityFromJson<T,H>(
Map<String, dynamic> json,
T Function(Map<String, dynamic> json) fromJsonT,
H Function(Object json) fromJsonH
) {
final models = <H>[];
json['models'].forEach((v) {
models.add(fromJsonH(v));
});
return BaseEntity<T,H>(
json['code'] as int,
json['message'] as String,
fromJsonT(json['model']),
models,
);
}
Map<String, dynamic> _$BaseEntityToJson<T,H>(
BaseEntity<T,H> instance,
Map<String, dynamic>? Function(T value) toJsonT,
H? Function(H value) toJsonH,
) {
return <String, dynamic>{
'code': instance.code,
'message': instance.message,
'model': toJsonT(instance.model),
'models': instance.models.map((v) => toJsonH(v)).toList()
};
}