Flutter中使用retrofit和dio库实现网络请求

  1. 在pubspec.yaml文件中引用retrofit和dio框架后,点击Pub get。
  #网络
  retrofit: ^4.0.1
  dio: ^5.0.0
  1. 由于网络数据传输,需要给数据序列化,所以还需要用到json_annotation框架,以简化代码编写。
  #json注解
  json_annotation: ^4.8.0
  1. 在pubspec.yaml中引用声名,用于代码生成、JSON 序列化和 HTTP 客户端代码生成。
  build_runner: ^2.3.0
  json_serializable: ^6.6.1
  retrofit_generator: ^8.1.0

在这里插入图片描述

  1. 创建一个LoginByPasswordRequest类,用于登录请求体。
import 'package:json_annotation/json_annotation.dart';

part 'login_by_password_request.g.dart';

@JsonSerializable()
class LoginByPasswordRequest {
  String accountNumber;
  String password;
  String email = "";
  String phone = "";

  LoginByPasswordRequest({
    required this.accountNumber,
    required this.password,
    this.email = "",
    this.phone = "",
  });

  factory LoginByPasswordRequest.fromJson(Map<String, dynamic> json) => _$LoginByPasswordRequestFromJson(json);
  Map<String, dynamic> toJson() => _$LoginByPasswordRequestToJson(this);
}

其中@JsonSerializable()注解和part ‘login_by_password_request.g.dart’;都是必需的,此时打开Terminal窗口,运行flutter pub run build_runner build命令,即可生成对应的.g文件,.g文件中自动完成了序列化和反序列化。
在这里插入图片描述

  1. 创建一个BaseDio,用于配置一些默认的网络请求参数,此处设置了一些默认的Header参数,如果不需要,直接删除即可。
abstract class BaseDio {

  Dio createDio() {
    Dio dio = Dio();
    // _dio.options.baseUrl = ApiService.getBaseUrl();
    dio.options = getOptions();// 10 seconds
    dio.interceptors.add(getInterceptorsWrapper());
    dio.interceptors.add(CacheInterceptor());
    return dio;
  }

  Future<SharedPreferences> get prefs async {
    return await SharedPreferences.getInstance();
  }

  BaseOptions getOptions();

  InterceptorsWrapper getInterceptorsWrapper();
}

class CacheInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    if (kDebugMode) {
      print('Request[${options.method}] '
          '=> Path: ${options.baseUrl}${options.path} '
          '=> Headers: ${options.headers} '
          '=> Parameters: ${options.queryParameters} '
          '=> Body: ${options.data}');
    }
    super.onRequest(options, handler);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    if (kDebugMode) {
      print('Response[${response.statusCode}] => Data: ${response.data}');
    }
    super.onResponse(response, handler);
  }

  @override
  void onError(DioError err, ErrorInterceptorHandler handler) {
    if (kDebugMode) {
      print('onError: ${err.toString()}');
    }
  }
}

  1. 创建一个ApiDio继承BaseDio,实现一些必要的参数请求。
class ApiDio extends BaseDio{
  ApiDio._();
  static final ApiDio _instance = ApiDio._();
  static ApiDio get instance => _instance;

  @override
  InterceptorsWrapper getInterceptorsWrapper() {
    return InterceptorsWrapper(
      onRequest: (options, handler) async {
        options.headers['Authorization'] = await prefs.then((value) {
          value.getString(Sp.TOKEN);
        }) ?? '';
        var lang = 'en_US';
        if (isChinese()) {
          lang = 'zh_CN';
        }
        options.headers['lang'] = lang;
        return handler.next(options);
      },
      onResponse: (response, handler) {
        return handler.next(response);
      },
      onError: (DioError e, handler) {
        return handler.next(e);
      },
    );
  }

  @override
  BaseOptions getOptions() {
    BaseOptions baseOptions = BaseOptions();
    baseOptions.connectTimeout = const Duration(milliseconds: 10000);
    baseOptions.receiveTimeout = const Duration(milliseconds: 10000);
    baseOptions.sendTimeout = const Duration(milliseconds: 10000);
    return baseOptions;
  }

}
  1. 创建一个LoginResponse,用于接收登录接口返回的方法体,同上述4。
import 'package:json_annotation/json_annotation.dart';
import 'login_increase_team.dart';

part 'login_response.g.dart';

@JsonSerializable()
class LoginResponse {
  final int? userId;
  final String? token;
  final List<LoginIncreaseTeam>? increaseTeamList;
  final String? name;
  final String? phone;
  final String? email;

  LoginResponse({
    this.userId,
    this.token,
    this.increaseTeamList,
    this.name,
    this.phone,
    this.email,
  });

  factory LoginResponse.fromJson(Map<String, dynamic> json) => _$LoginResponseFromJson(json);
  Map<String, dynamic> toJson() => _$LoginResponseToJson(this);
}
  1. 创建一个ApiResponse,用于解析返回的数据。
import 'package:json_annotation/json_annotation.dart';
import 'package:partner/net/response/error_data.dart';

part 'api_response.g.dart';
@JsonSerializable(genericArgumentFactories: true)
class ApiResponse<T>{
  String? code;
  String? msg;
  ErrorData? error;
  T? data;
  ApiResponse({required this.code, this.msg, this.data});

  bool isSuccess() => this.code == "200";

  @override
  String toString() {
    return 'Response{code: $code,error:$error, msg: $msg, data: $data}';
  }

  factory ApiResponse.fromJson(
      Map<String, dynamic> json, T Function(Object?) fromJsonT) =>
      _$ApiResponseFromJson(json, fromJsonT);

  Map<String, dynamic> toJson(Object? Function(T) toJsonT) =>
      _$ApiResponseToJson(this, toJsonT);
}
  1. 创建一个ApiMainService,用于登录接口请求,其中包括url和post请求等。
import 'package:dio/dio.dart';
import 'package:partner/net/network_api.dart';
import 'package:partner/net/request/login_by_password_request.dart';
import 'package:partner/net/response/api_response.dart';
import 'package:partner/net/response/distribution_response.dart';
import 'package:partner/net/response/login_increase_team.dart';
import 'package:partner/net/response/login_response.dart';
import 'package:partner/net/response/user.dart';
import 'package:retrofit/retrofit.dart';

import 'api_dio.dart';

part 'api_main_service.g.dart';

@RestApi(baseUrl: 'https://api-partner.test.cn')
abstract class ApiMainService {
  factory ApiMainService({Dio? dio, String? baseUrl}) {
    dio ??= ApiDio.instance.createDio();
    return _ApiMainService(dio, baseUrl: baseUrl);
  }

  @POST("/partnerApp/loginByPassword")
  Future<ApiResponse<LoginResponse>> loginByPassword(
      @Body() LoginByPasswordRequest request);
}


  1. 在需要的地方直接调用即可。
    // 执行登录逻辑
    var request = LoginByPasswordRequest(accountNumber: _username, password: _password);
    mainService.loginByPassword(request).then((value) {
      var result = value;
    });

其中result即为最终的返回数据,数据格式如下所示。
在这里插入图片描述

  1. 如果对返回的数据有一定要求,可以创建Future方法去处理网络请求。
Future<void> request<T>(
    Future<BaseResponse<T>> Function() block,
    Function(T) success,
    Function(AppException) error,
    { bool isShowDialog = true,
      String loadingMessage = ''}
    ) async {
  // 如果需要弹窗,通知视图显示加载对话框
  if (isShowDialog) {
    LoadingDialog.show(loadingMessage);
  }
  // 请求体
  block().then((value) async {
    try {
      // 网络请求成功,关闭弹窗
      if (isShowDialog) {
        LoadingDialog.hide();
      }

      // 校验请求结果码是否正确,不正确会抛出异常
      await executeResponse(value, success);
    } catch (e) {
      // 网络请求异常,关闭弹窗
      if (isShowDialog) {
        LoadingDialog.hide();
      }

      // 打印错误消息
      logError(e.toString());
      // 失败回调
      error(ExceptionHandle.handleException(e));
    }
  }).catchError((e) {
    // 网络请求异常,关闭弹窗
    if (isShowDialog) {
      LoadingDialog.hide();
    }

    // 打印错误消息
    logError(e.toString());
    // 失败回调
    error(ExceptionHandle.handleException(e));
  });
}
  1. 在合适的地方直接调用即可。
    request<LoginResponse>(
          () => mainService.loginByPassword(LoginByPasswordRequest(
          accountNumber: _username,
          password: _password)),
      (LoginResponse response) {
        // 登录成功
      },
      (AppException e) {
        logDebug("error: ${e.errorMsg}");
        SnackBarManager.instance.showTitle(strings().login_fail, message: e.errorMsg);
      },
    );