欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

flutter dio封装结合json_serializable创建网络请求

程序员文章站 2022-06-01 17:53:58
...

1.依赖

dio: ^3.0.9
fluttertoast: ^4.0.1
shared_preferences: ^0.5.7+3
build_runner: ^1.7.1
json_serializable: ^3.2.3

注意:序列化依赖是放在dev_dependencies里面

flutter dio封装结合json_serializable创建网络请求

2.目录结构

flutter dio封装结合json_serializable创建网络请求

3.开始封装dio网络请求

1.创建DioManage

  • 利用factory创建dio单列
  • 配置dio基本属性options
  • 设置拦截器
class DioManager{
  static const String BASE_URL = "http://172.16.3.52:8083/V1";
  static const int CONNECT_TIMEOUT = 10000;
  static const int RECEIVE_TIMEOUT = 10000;
 ///创建单例
  //1.私有静态实例对象
  static final DioManager _dioManager = DioManager._instance();

  //2.创建工厂方法
  factory DioManager() => _dioManager;

  //3.私有的命名式构造方法
  DioManager._instance() {

    if (_dio == null) {
      ///dio配置基本属性
      BaseOptions _options = BaseOptions(
          baseUrl: BASE_URL,
          contentType: Headers.jsonContentType,
          responseType: ResponseType.json,
          connectTimeout: CONNECT_TIMEOUT,
          receiveTimeout: RECEIVE_TIMEOUT,);
      _dio = Dio(_options);

      ///拦截器
      _dio.interceptors
        ..add(HeadersInterceptors())
        ..add(LogsInterceptor())
        ..add(ResponseInterceptors());
    }
  }
}

2.创建拦截器

  • HeadersInterceptors()头部拦截器,设置token和其他参数
  • LogsInterceptor()日志拦截器,控制台打印数据
  • ResponseInterceptors()相应拦截器,打印接口返回值

上代码

import 'dart:io';

import 'package:dio/dio.dart';
import 'package:shared_preferences/shared_preferences.dart';

class HeadersInterceptors extends InterceptorsWrapper{
  var encryptResult = '';
  @override
  Future onRequest(RequestOptions options) async{
    // TODO: implement onRequest
    SharedPreferences _sp = await SharedPreferences.getInstance();
    options.headers.addAll({
      HttpHeaders.contentTypeHeader: 'application/json',
      'antappid': 'appId',
      'antplatform': Platform.isIOS ? 'ios' : 'android',
      'authorization': 'token',
      'antciphertext': '加密',
    });

    return super.onRequest(options);
  }
}

根据每个公司不同的头部要求设置相应的头部参数

import 'package:dio/dio.dart';
import 'package:flutter_widget_project/dio_util/http_error.dart';

///日志拦截器
class LogsInterceptor extends InterceptorsWrapper {
  @override
  Future onError(DioError err) {
    print('请求异常信息: ' + HttpError(code: err.type.toString() ,message:  err.message).toString());
    return super.onError(err);
  }

  @override
  Future onRequest(RequestOptions options) {
    print("请求API:${options.baseUrl}${options.path}");
    print('请求头: ' + options.headers.toString());
    if (options.data != null) {
      print('请求参数: ' + options.data.toString());
    }
    return super.onRequest(options);
  }

  @override
  Future onResponse(Response response) {
    return super.onResponse(response);
  }
}

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_widget_project/dio_util/base_model.dart';
import 'package:fluttertoast/fluttertoast.dart';

class ResponseInterceptors extends InterceptorsWrapper {
  @override
  onResponse(Response response) {
    RequestOptions _options = response.request;
    String body = response.data.toString();
    print("${_options.contentType}");
    try{
      if(_options.contentType != null && _options.contentType == "text"){
        print("HTTP_RESPONSE_BODY::${response.data}");
      }
      if(response.statusCode == 200){
        if(body.length > 600){
          for(int i = 0 ; i< body.toString().length ; i+= 600){
            if(i+600 < body.toString().length){
              print("HTTP_RESPONSE_BODY::${body.toString().substring(i , i+600)}");
            }else{
              print("HTTP_RESPONSE_BODY::${body.toString().substring(i , body.toString().length)}");
            }
          }
        }else{
          print('HTTP_RESPONSE_BODY::$body');
        }
      }

    }catch(e){
      print('ERROR::$body');
    }
    return super.onResponse(response);
  }
}

***响应拦截将日志进行字符长度遍历,防止日志过长导致打印信息不全***

3.HttpError错误提示

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_widget_project/dio_util/constant.dart';
import 'package:flutter_widget_project/dio_util/error_page.dart';
import 'package:fluttertoast/fluttertoast.dart';

class HttpError{

  static const int UNAUTHORIZED = 401;
  static const int FORBIDDEN = 403;
  static const int NOT_FOUND = 404;
  static const int REQUEST_TIMEOUT = 408;
  static const int INTERNAL_SERVER_ERROR = 500;
  static const int BAD_GATEWAY = 502;
  static const int SERVICE_UNAVAILABLE = 503;
  static const int GATEWAY_TIMEOUT = 504;

  ///未知错误
  static const String UNKNOWN = "UNKNOWN";

  ///解析错误
  static const String PARSE_ERROR = "PARSE_ERROR";

  ///网络错误
  static const String NETWORK_ERROR = "NETWORK_ERROR";

  ///协议错误
  static const String HTTP_ERROR = "HTTP_ERROR";

  ///证书错误
  static const String SSL_ERROR = "SSL_ERROR";

  ///连接超时
  static const String CONNECT_TIMEOUT = "CONNECT_TIMEOUT";

  ///响应超时
  static const String RECEIVE_TIMEOUT = "RECEIVE_TIMEOUT";

  ///发送超时
  static const String SEND_TIMEOUT = "SEND_TIMEOUT";

  ///网络请求取消
  static const String CANCEL = "CANCEL";

  String code;

  String message;

  HttpError({this.code, this.message});

  HttpError.dioError(DioError error){

    message = error.message;
    switch(error.type){
      case DioErrorType.CONNECT_TIMEOUT:
        code = CONNECT_TIMEOUT;
        message = "网络连接超时,请检查网络设置!";
        break;
      case DioErrorType.RECEIVE_TIMEOUT:
        code = RECEIVE_TIMEOUT;
        message = "服务器异常,请稍后重试!";
        break;
      case DioErrorType.SEND_TIMEOUT:
        code = SEND_TIMEOUT;
        message = "网络连接超时,请检查网络设置!";
        break;
      case DioErrorType.RESPONSE:
        code = HTTP_ERROR;
        message = "服务器异常,请稍后重试!";
        break;
      case DioErrorType.CANCEL:
        code = CANCEL;
        message = "请求已被取消,请重新请求!";
        break;
      case DioErrorType.DEFAULT:
        code = UNKNOWN;
        message = "网络或服务器异常,请稍后重试!";
        break;
    }

    //跳转错误页面
    //push(ErrorPage(message: message,));
    Fluttertoast.showToast(msg: message);
  }

  @override
  String toString() {
    // TODO: implement toString
    return 'HttpError{code:$code , message:$message}';
  }

}

4.根据返回值结构,创建BaseModel , BaseListModel类

一般结构:data里面的数据才是我们需要的responsbody,所以我们要将拿的的返回值进行封装

{"code":0 ,
"message":"success",
"data":"{}"
}

数据序列化成json

我的数据结构

{
"Code":0,
"Message":"",
"Data":{
    "Token":"wewewwwewew",
    {
      "Id":"112" , 
      "Name":"ee"
    }
  }
}

创建返回值model

// ignore_for_file: non_constant_identifier_names,library_prefixes最顶部这句话可取消警告
part 'user_model.g.dart';创建.g文件不可少
下面这两句是模版
factory LoginModel.fromJson(Map<String, dynamic> json) =>
    _$LoginModelFromJson(json);

LoginModel from(Map<String, dynamic> json) => _$LoginModelFromJson(json);
// ignore_for_file: non_constant_identifier_names,library_prefixes
import 'package:flutter_widget_project/dio_util/base_model.dart';
import 'package:json_annotation/json_annotation.dart';

part 'user_model.g.dart';


/*
* 登录接口请求成功的返回值
*
* */

@JsonSerializable(createToJson: false)
class LoginModel{
  String Token;
  UserModel Member;

  LoginModel({this.Token , this.Member});


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

  LoginModel from(Map<String, dynamic> json) => _$LoginModelFromJson(json);
}


@JsonSerializable(createToJson: false)
class UserModel {
  final String Id;


  UserModel({
    this.Id,

  });

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

  UserModel from(Map<String, dynamic> json) => _$UserModelFromJson(json);
}

创建完model后在控制台输入flutter packages pub run build_runner build 创建.g文件,如果不成功就使用flutter packages pub run build_runner build --delete-conflicting-outputs再次创建

创建EntityFactory申明model

import 'package:flutter_widget_project/model/entrust_search_model.dart';
import 'package:flutter_widget_project/model/user_model.dart';

class EntityFactory {
  static T generateOBJ<T>(json) {
    if (json == null) {
      return null;
    }
//可以在这里加入任何需要并且可以转换的类型,例如下面
//    else if (T.toString() == "LoginModel") {
//      return LoginModel.fromJson(json) as T;
//    }
//    else if (T.toString() == "EntrustModel") {
//      return EntrustModel.fromJson(json) as T;
//    }
//    else if (T.toString() == "EntrustListModel") {
//      return EntrustListModel.fromJson(json) as T;
//    }
    else {
      return json as T;
    }
  }
}

BaseModel

import 'entity_factory.dart';

///因为我的接口返回的是大写数据,所以json["Data"].

class BaseModel<T> {
///拿到服务器数据后,我们希望转换成自己方便使用的model结构
  int code;
  String message;
  String serverMessage;
  T data;

  BaseModel({this.code, this.message, this.data});

  factory BaseModel.fromJson(json) {
    ///获取接口数据
    if(json["Data"] != null){
      return BaseModel(
        code: json["Code"],
        message: json["Message"],
        // data值需要经过工厂转换为我们传进来的类型
        data: EntityFactory.generateOBJ<T>(json["Data"]),
      );
    }else{
      return BaseModel(
        code: json["Code"],
        message: json["Message"],
        data: null
      );
    }
  }
}

BaseListModel

import 'package:flutter_widget_project/dio_util/entity_factory.dart';
import 'package:flutter_widget_project/model/page_option_model.dart';

class BaseListModel<T>{
  int code;
  String message;
  dynamic pageResult;
  List<T> data;

  BaseListModel({this.code, this.message, this.data ,this.pageResult});

  factory BaseListModel.fromJson(json){
    List<T> mData = List();
    if (json["Data"] != null && json["Data"]["Data"] != null) {
      (json["Data"]["Data"] as List).forEach((element) {
        mData.add(EntityFactory.generateOBJ<T>(element));
      });
    }


    if(json["Data"] != null){
      return BaseListModel(
          code: json["Code"],
          message: json["Message"],
          data: mData,
          pageResult: EntityFactory.generateOBJ(json["Data"]["PagingResult"])
      );
    }else{
       return BaseListModel(
          code: json["Code"],
          message: json["Message"],
          data: null,
      );
    }


  }
}

5.再回到DioManager创建请求方法

 Future request<T>(
    BuildContext context,
    NetMethod method,
    String path, {
    bool isLoading,
    Map param,
    Map formData,
    Function(T) onSuccess,
    Function(HttpError) onError,
  }) async {
    try {
      ///开始做网络请求
      Response response = await _dio.request(path,
          queryParameters: param,
          data: formData,
          options: Options(method: NetMethodValues[method]));
      if (response != null) {
        ///返回数据转化成对应的model实例
        BaseModel entity = BaseModel<T>.fromJson(response.data);

        ///根据状态码处理相应的状态
        if (entity.code == 0) {
          onSuccess(entity.data);
        } else {
          onError(HttpError(code: entity.code.toString(), message: entity.message));
        }
      } else {
        onError(HttpError(code: "-1", message: "未知错误"));
      }
    } on DioError catch (e) {
      ///异常拦截
      onError(HttpError.dioError(e));
    }
  }
///请求列表
  Future requestList<T>(
    BuildContext context,
    NetMethod method,
    String path, {
    bool isLoading,
    Map param,
    Map formData,
    Function(List , dynamic) onSuccess,
    Function(HttpError) onError,
  }) async {
    try {
      Response response = await _dio.request(path,
          queryParameters: param,
          data: formData,
          options: Options(method: NetMethodValues[method]));
      if (response != null) {
        BaseListModel entity = BaseListModel<T>.fromJson(response.data);
        if (entity.code == 0) {
          onSuccess(entity.data , entity.pageResult);
        } else {
          onError(
              HttpError(code: entity.code.toString(), message: entity.message));
        }
      } else {
        onError(HttpError(code: "-1", message: "未知错误"));
      }
    } on DioError catch (error) {
      onError(HttpError.dioError(error));
    }
  }
}

枚举请求方式


enum NetMethod { GET, POST, DELETE, PUT }

const NetMethodValues = {
  NetMethod.GET: "get",
  NetMethod.POST: "post",
  NetMethod.DELETE: "delete",
  NetMethod.PUT: "put"
};

使用 

request() {
    DioManager().request<LoginModel>(
      context,
      NetMethod.POST,
      "/MemberAuth/Login",
      formData: {"Account": "zws1234", "Password": "123456"},
      onSuccess: (data) async {
        LoginModel dataModel = data;
        SharedPreferences sp = await SharedPreferences.getInstance();
        sp.setString("accessToken" ,dataModel.Token);
      },
      onError: (error) {
        Fluttertoast.showToast(msg: error.message);
        print("error code = ${error.code}, massage = ${error.message}");
      },
    );
  }

分页列表请求

1.先设置监听是否滑动到最底部或者最顶部

import 'package:flutter/material.dart';
import 'package:flutter_widget_project/model/page_option_model.dart';

class PageMini {
  ScrollController pageScrollController = new ScrollController();
  bool isPageLoading = false;

  PageResultModel pageResultModel = new PageResultModel.empty();
  PageOptionModel pageOptionModel = new PageOptionModel.empty();

  initPage() {
    if(pageScrollController != null){
      pageScrollController.addListener(() {
        if (pageScrollController.position.pixels >=
            pageScrollController.position.maxScrollExtent  && !isPageLoading) {
          isPageLoading = true;
          loadMoreData();
        }
        if (pageScrollController.position.pixels ==
            pageScrollController.position.minScrollExtent && !isPageLoading) {
          isPageLoading = true;
          refreshData();
        }
      });
    }
  }


  @protected
  refreshData() {}

  @protected
  loadMoreData() {}
}

在页面混入,并设置滚动controller

flutter dio封装结合json_serializable创建网络请求

请求

 requestList({int goPage = 1}) {
    setState(() {
      isLoading = true;
    });
     goPage = pageOptionModel.GotoPageNumber;
    DioManager().requestList<EntrustModel>(
      context,
      NetMethod.POST,
      "/Entrust/Search",
      formData: {"EntrustType": 1 , "PagingOption":{"GotoPageNumber":"$goPage" , "PageSize":"15"}},
      onSuccess: (response , pageResult){

        if(goPage == 1){
          dataList.clear();
        }
        dataList = response;
        setState(() {
          isLoading = false;
          isPageLoading = false;
        });
      },
      onError: (error){
        Fluttertoast.showToast(msg: error.message);
        setState(() {
          isLoading = false;
        });
      }
    );
  }

 github链接dio_util