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? putString(String key, String value) {
return _prefs?.setString(key, value);
}
}
Dio是目前Flutter最流行的网络数据请求库,本项目也是使用此库。在使用前,需要对Dio做单例,以及定义些全局请求变量,请求拦截以便处理token。
相关文件存放在:services
存在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);
}
}
发出去得响应,当后端返回token失效的时候,我们需要重新发起新的请求,同时锁足Dio实例暂停其他接口发送,直到获取到新的token后解锁,其他的请求将继续执行,本次请求需再继续补发第一次的请求。
/// * 响应token验证拦截
class TokenInterceptor extends Interceptor {
Dio? _tokenDio;
Future getToken() async {
final Map params = {};
params['refresh_token'] = SpUtil.getString(Constant.refreshToken).nullSafe;
try {
//重新创建一个实例,跳开token验证拦截器
_tokenDio ??= Dio();
_tokenDio!.options = Services.instance.dio.options;
final Response response = await _tokenDio!.post('lgn/refreshToken', data: params);
if (response.statusCode == ExceptionHandle.success) {
return (json.decode(response.data.toString()) as Map)['access_token'] as String;
}
} catch (e) {
// Log.e('刷新Token失败!');
}
return null;
}
@override
Future 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(
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 {
@JsonKey(name: 'code')
final int code;
@JsonKey(name: 'message')
final String message;
@JsonKey(name: 'model')
final T model;
@JsonKey(name: 'models')
final List models;
BaseEntity(this.code, this.message, this.model, this.models);
factory BaseEntity.fromJson(
Map json,
T Function(Map json) fromJsonT,
H Function(Object json) fromJsonH
) =>_$BaseEntityFromJson(json, fromJsonT,fromJsonH);
Map toJson(
Map Function(T value) toJsonT,
H Function(H value) toJsonH
) =>
_$BaseEntityToJson(this, toJsonT, toJsonH);
}
BaseEntity _$BaseEntityFromJson(
Map json,
T Function(Map json) fromJsonT,
H Function(Object json) fromJsonH
) {
final models = [];
json['models'].forEach((v) {
models.add(fromJsonH(v));
});
return BaseEntity(
json['code'] as int,
json['message'] as String,
fromJsonT(json['model']),
models,
);
}
Map _$BaseEntityToJson(
BaseEntity instance,
Map? Function(T value) toJsonT,
H? Function(H value) toJsonH,
) {
return {
'code': instance.code,
'message': instance.message,
'model': toJsonT(instance.model),
'models': instance.models.map((v) => toJsonH(v)).toList()
};
}