Flutter Dio网络请求封装

Flutter 3.24.5  

Dart 3.5.4

dio: ^5.3.4

1、新建一个类DioClient

import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:energy_manage/base/BaseModel.dart';
import 'package:energy_manage/constantkey/KeyString.dart';
import 'package:energy_manage/pages/login/pages/LoginPage.dart';
import '../base/BaseToast.dart';
import 'package:get/route_manager.dart';

// 请求方式
enum HTTPMethod {
  get,
  post,
  put,
  delete,
}

class DioClient {
  //创建单例
  static DioClient? _instance;

  // 确保DioClient 只有一个实例
  factory DioClient() => _instance ?? (_instance = DioClient._init());

  static Dio _dio = Dio();

  DioClient._init() {
    BaseOptions options = BaseOptions(
      connectTimeout: const Duration(seconds: 10),
      receiveTimeout: const Duration(seconds: 10),
    );
    _dio = Dio(options);
    _dio.options.baseUrl = KeyString.baseUrl;
    // 拦截器
    _dio.interceptors.add(ResponseInterceptor());
    // 日志打印
    _dio.interceptors.add(LogInterceptor(requestBody: true));
  }

  DioClient getInstance({String baseUrl = KeyString.baseUrl}) {
    _dio.options.baseUrl = baseUrl;
    return this;
  }

  doDio(
    path, {
    required method,
    queryParameters,
    data,
    options,
    cancelToken,
    onReceiveProgress,
    loading,
  }) async {
    if (loading == true) {
      BaseToast.show("请求中");
    }
    Response? response;
    switch (method) {
      case HTTPMethod.get:
        response = await _dio.get(path,
            queryParameters: queryParameters,
            data: data,
            options: options,
            cancelToken: cancelToken,
            onReceiveProgress: onReceiveProgress);
        break;
      case HTTPMethod.post:
        response = await _dio.post(path,
            queryParameters: queryParameters,
            data: data,
            options: options,
            cancelToken: cancelToken,
            onReceiveProgress: onReceiveProgress);
      case HTTPMethod.put:
        response = await _dio.put(path,
            queryParameters: queryParameters,
            data: data,
            options: options,
            cancelToken: cancelToken,
            onReceiveProgress: onReceiveProgress);
      case HTTPMethod.delete:
        response = await _dio.delete(path,
            queryParameters: queryParameters,
            data: data,
            options: options,
            cancelToken: cancelToken);
      default:
    }
    if (path == "api/auth/login") {
      return response;
    }
    return BaseModel.fromJson(response?.data);
  }
}

//拦截器
class ResponseInterceptor extends InterceptorsWrapper {
  // 请求时
  @override
  void onRequest(
      RequestOptions options, RequestInterceptorHandler handler) async {
    String token = SPUtils().getToken();
    if (options.path != "/login") {
      //请求头赋值
      options.headers["Authorization"] = token;
    }
    log("queryParameters:${options.queryParameters}");
    return handler.next(options);
  }

// 返回时
  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    BaseToast.cancel();
    //成功
    print(
        "返回数据:地址:${response.requestOptions.uri} \n内容:\n${prettyPrintJson(response.toString())}");
    return handler.resolve(response);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) async {
    BaseToast.cancel();
    print("请求返回报错:${err.requestOptions.uri} 错误信息:${err.response},${err.error}");
    if (err.error.toString().contains("Network is unreachable")) {
      BaseToast.showToast("网络连接失败,请检查网络");
      return handler
          .resolve(Response(requestOptions: err.requestOptions, data: {
        "code": -2,
        "message": "网络连接失败,请检查网络",
        "data": null,
      }));
    } else if (err.response == null) {
      return handler
          .resolve(Response(requestOptions: err.requestOptions, data: {
        "code": -1,
        "message": "请求报错",
        "data": null,
      }));
    } else {
      Map map = json.decode(err.response.toString());
      if (map["message"] != null) {
        BaseToast.showToast("${getErrorMessage(map["message"])}");
      }
      if (map["status"] == 401) {
        await Future.delayed(const Duration(milliseconds: 2000));
        if (Get.currentRoute == "/LoginPage" || Get.currentRoute == "/") {
          return;
        }
        Get.offAll(() => Loginpage());
        return handler
            .resolve(Response(requestOptions: err.requestOptions, data: {
          "code": 401,
          "message": "登录失效,请重新登录",
          "data": null,
        }));
      }
      return handler
          .resolve(Response(requestOptions: err.requestOptions, data: {
        "code": -1,
        "message": "请求报错",
        "data": null,
      }));
    }
  }

  ///错误信息
  getErrorMessage(var message) {
    String errMessage = "请求报错";
    switch (message) {
      case "Token has expired":
        errMessage = "登录过期,请重新登录";
        break;
      case "Invalid username":
        errMessage = "用户不存在";
        break;
    }
    return errMessage;
  }

    //格式化打印json数据
  String prettyPrintJson(String jsonStr) {
    var decodedJson = jsonDecode(jsonStr); // 解析 JSON 字符串
    var encoder = JsonEncoder.withIndent('  '); // 使用 2 个空格缩进格式化
    return encoder.convert(decodedJson);
  }
}

2、新建一个model基类


class BaseModel<T> {
  int? code;
  T? data;
  String? message;

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

  BaseModel.fromJson(Map<String, dynamic> json) {
    code = json["code"];
    data = json["data"];
    message = json["message"];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> map = <String, dynamic>{};
    map["code"] = code;
    map["data"] = data;
    map["message"] = message;
    return map;
  }
}

3、将网络请求都写在同一个类中


import 'package:energy_manage/http/DioClient.dart';

class APIService {
  // 登录
  static getLogin(Map map) async {
    return await DioClient().doDio("api/auth/login",
        method: HTTPMethod.post, data: map, loading: true);
  }
}

4、使用

Response 是根据封装的返回类型区分 如果返回Response类型,则使用Response类型接收,如果返回的是BaseModel类型,则使用BaseModel类型接收

在DioClient类中判断了login返回的是response 则这个接口使用Response类型接收

其余的使用BaseModel类型接收

if (path == "api/auth/login") {

return response;

}

return BaseModel.fromJson(response?.data);

两种不同类型的返回示例

  // 登录返回类型是Response
  login() async {
    Map<String, dynamic> map = {
      "phone": "13100000000",
      "password": "a00000000"
    };
    Response response = await APIService.getLogin(map);
    print("返回数据${response.data}");
  }

  // 登录返回类型是BaseModel
  login() async {
    Map<String, dynamic> map = {
      "phone": "13100000000",
      "password": "a00000000"
    };
    BaseModel response = await APIService.getLogin(map);
    print("返回数据${response.code}");
  }