- 在pubspec.yaml文件中引用retrofit和dio框架后,点击Pub get。
#网络
retrofit: ^4.0.1
dio: ^5.0.0
- 由于网络数据传输,需要给数据序列化,所以还需要用到json_annotation框架,以简化代码编写。
#json注解
json_annotation: ^4.8.0
- 在pubspec.yaml中引用声名,用于代码生成、JSON 序列化和 HTTP 客户端代码生成。
build_runner: ^2.3.0
json_serializable: ^6.6.1
retrofit_generator: ^8.1.0
- 创建一个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文件中自动完成了序列化和反序列化。
- 创建一个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()}');
}
}
}
- 创建一个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;
}
}
- 创建一个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);
}
- 创建一个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);
}
- 创建一个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);
}
- 在需要的地方直接调用即可。
// 执行登录逻辑
var request = LoginByPasswordRequest(accountNumber: _username, password: _password);
mainService.loginByPassword(request).then((value) {
var result = value;
});
其中result即为最终的返回数据,数据格式如下所示。
- 如果对返回的数据有一定要求,可以创建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));
});
}
- 在合适的地方直接调用即可。
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);
},
);