Flutter实战项目-第三篇 数据本存储、网络请求Dio、基础类转换

一、概要

  • 数据本地化存储
  • 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()
  };
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值