Flutter 从0搭建一个完整的项目(一、网络请求)

一、核心理念

化繁为简,最终使用时,简单易用,扩展性强即可。

二、网络请求,基于Dio的封装

dio 官网地址

先看最终使用事例(1.定义返回的实体类 2.发起网络请求)

// 1. 返回实体类 UserBean 的定义(根据Json 用开发工具生成即可)
class UserBean implements BaseHttpBean {
  UserBean({
    this.token,
    this.userId,
  });
  
  String? token;
  String? userId;
  
  //  解析数据
  UserBean.fromJson(dynamic json) {
    token = json['token'];
    user_id = json['userId']?.toString();
  }

  // 这个方法是 实现父类BaseHttpBean 的方法,下面会详解
  @override
  UserBean fromJson(dynamic json) {
    return UserBean.fromJson(json);
  }
}
// 2. 登录网络请求--事例
void login(){
    AppRequest.login("account","password", cancelToken).then((UserBean bean) {
      // 成功处理 ...
    }).catchError((e) {
      // 异常处理 ...
    });
}

三、网络封装具体过程

1.接口定义
/// 网络请求 最常用的定义 实际项目自己添加或者修改
class BaseHttp {
  /// BaseUrl
  String baseUrl() {
    return "";
  }

  /// 固定的请求头(所有接口只走一次)
  Map<String, dynamic>? baseHeaders() {
    return null;
  }

  /// 特有的请求头(每个接口都会走)
  Map<String, dynamic>? headers() {
    return null;
  }

  /// 超时时间
  Duration timeout() {
    return const Duration(seconds: 45);
  }

  /// 连接超时时间
  Duration connectTimeout() {
    return const Duration(seconds: 3);
  }

  /// 数据转换 - 扩展用(比如数据解密)
  dynamic convert(dynamic json) {
    return json;
  }
}
2. DIO 实现基本的网络请求
/// 根据Dio的基本封装
class BaseDio extends BaseHttp {
  late Dio _dio;
  // 配置信息
  late BaseOptions baseOptions;
  // 是否初始化
  bool _isInit = false;

  /// 初始化构造
  BaseDio.init() {
    if (_isInit) {
      return;
    }
    baseOptions = BaseOptions(
      baseUrl: baseUrl(),
      headers: baseHeaders(),
      connectTimeout: connectTimeout(),
    );
    _dio = Dio(baseOptions);
    _isInit = true;
  }

  /// 设置BaseURL 根据实际情况修改 如果固定 就复写 baseUrl()
  void setBaseUrl(String url) {
    if (_isInit) {
      baseOptions.baseUrl = url;
    }
  }

  /// 配置信息,子类可以根据实际覆写
  Options options() {
    final options = Options(
      headers: headers(),
    );
    return options;
  }

  /// 核心方法:网络请求统一实现
  Future<T> request<T>(
    String path, {
    String method = "GET",
    Map<String, dynamic>? params,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) async {
    // 参数配置
    var option = options();
    if (method == "FORM") {
      option.method = "POST";
    } else {
      option.method = method;
    }
    // 请求参数
    Map<String, dynamic>? queryParameters;
    // 表单请求
    Object? data;
    if (method == "GET") {
      queryParameters = params;
    } else {
      // POST FORM
      if (params != null) {
        if (method == "POST") {
          data = params;
        } else {
          data = FormData.fromMap(params);
        }
      }
    }
    // dio 发起网络请求
    var response = await _dio.request(
      path,
      data: data,
      queryParameters: queryParameters,
      cancelToken: cancelToken,
      options: option,
      onSendProgress: onSendProgress,
      onReceiveProgress: onReceiveProgress,
    );
    if (response.statusCode == 200) {
      // 返回请求成功的结果
      return Future.value(response.data);
    }
    throw Exception("网络请求出错:${response.statusCode}");
  }
}
3.基本数据类的定义
/// 1.网络请求 数据解析定义
abstract class BaseHttpBean {
  dynamic fromJson(dynamic json);
}

/// 2. 网络请求处理 根据实际项目 自己统一封装的类
class HttpBean<T extends BaseHttpBean, K> {
  HttpBean({
    this.code,
    this.msg,
    this.data,
  });

  int? code; // 响应码
  String? msg; // 响应的信息
  K? data; // 响应的数据 可能是:{} 或者 []

  /// 是否成功
  bool success() {
    return code == 0 || code == 1;
  }

  HttpBean.fromJson(dynamic json, T type) {
    code = json['code'] as int?;
    msg = json['msg'];
    // 解析data
    var result = json['data'];
    // List 处理
    if (result is List) {
      this.data = List<T>.from(result.map((item) => type.fromJson(item)).toList()) as K?;
    } else {
      this.data = type.fromJson(result);
    }
  }

  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{};
    map['code'] = code;
    map['msg'] = msg;
    map['data'] = data;
    return map;
  }
}
4.网络请求,实际项目客户端请求封装(Get Post Form),上传下载这些可以自己扩展
/// 网络请求,实际项目客户端请求封装(Get Post Form)
class BaseClient extends BaseDio {
  BaseClient.init() : super.init();

  /// 核心方法 -- 数据统一处理
  Future<K> _requestData<T extends BaseHttpBean, K>(
    String path, // 路径
    T type, // 实际解析类
    {
    String method = "GET", // 请求方式
    Map<String, dynamic>? params, // 请求参数
    CancelToken? cancelToken, // 取消的Token
    ProgressCallback? onSendProgress, // 扩展操作用
    ProgressCallback? onReceiveProgress, // 扩展操作用
  }) async {
    // 1. 请求数据
    var data = await request(
      path,
      method: method,
      params: params,
      cancelToken: cancelToken,
      onSendProgress: onSendProgress,
      onReceiveProgress: onReceiveProgress,
    );
    // 2. 根据实际 转换数据,默认就是返回 data原有数据
    var json = convert(data);
    // 测试模式 打印LOG
    if (kDebugMode) {
      print("url==>${baseOptions.baseUrl}$path");
      print("header==>${jsonEncode(params)}");
      print("result==>${jsonEncode(json)}");
    }
    // 3. 解析数据
    var bean = HttpBean.fromJson(json, type);
    if (bean.success()) {
      // 4. 返回最终结果
      return Future.value(bean.data as K);
    }
    throw Exception("数据解析出错 请检查实体Bean");
  }

  /// Get 请求
  Future<K> get<T extends BaseHttpBean, K>(
    String path,
    T type, {
    Map<String, dynamic>? params,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) async {
    return _requestData(
      path,
      type,
      method: "GET",
      params: params,
      cancelToken: cancelToken,
      onSendProgress: onSendProgress,
      onReceiveProgress: onReceiveProgress,
    );
  }

  /// Post请求
  Future<K> post<T extends BaseHttpBean, K>(
    String path,
    T type, {
    Map<String, dynamic>? params,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) async {
    return _requestData(
      path,
      type,
      method: "POST",
      params: params,
      cancelToken: cancelToken,
      onSendProgress: onSendProgress,
      onReceiveProgress: onReceiveProgress,
    );
  }

  /// 表单请求
  Future<K> form<T extends BaseHttpBean, K>(
    String path,
    T type, {
    Map<String, dynamic>? params,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) async {
    return _requestData(
      path,
      type,
      method: "FORM",
      params: params,
      cancelToken: cancelToken,
      onSendProgress: onSendProgress,
      onReceiveProgress: onReceiveProgress,
    );
  }
}
5.项目最终使用
5.1 项目网络的最终封装
/// 客户端网络请求
class AppClient extends BaseClient {
  AppClient.init() : super.init();

  /// 请求头
  @override
  Map<String, dynamic>? headers() {
    Map<String, dynamic> header = {};
    header["deviceid"] = "AAABBBCCC123456XXX";
    // ...
    return header;
  }

  /// 根据实际情况处理数据,比如解密操作
  @override
  dynamic convert(json) {
    return json;
  }
}
5.2 接口统一管理类(项目接口统一定义的地方)
/// 1. 接口统一管理类
class AppRequest {
  static final _client = AppClient.init();

  /// 设置BaseUrl
  static void setBaseUrl(String url) {
    _client.setBaseUrl(url);
  }

  /// 登录接口 (UserBean在下面最终使用里会看到)
  static Future<UserBean> login(var account,var password, {CancelToken? cancelToken}) {
    var path = "/login";
    Map<String, dynamic> params = {};
    params["account"] = account;
    params["password"] = password;
    return _client.post(path, UserBean(),
        cancelToken: cancelToken, params: params);
  }

  /// 系统配置接口
  static Future<SystemBean> systemConfig({CancelToken? cancelToken}) {
    var path = "/system/config";
    return _client.post(path, SystemBean(), cancelToken: cancelToken);
  }
}
5.3 最终使用事例(1.定义返回的实体类 2.发起网络请求)
// 1. 返回实体类 UserBean 的定义(根据Json 用开发工具生成即可)
class UserBean implements BaseHttpBean {
  UserBean({
    this.token,
    this.userId,
  });
  
  String? token;
  String? userId;
  
  //  解析数据
  UserBean.fromJson(dynamic json) {
    token = json['token'];
    user_id = json['userId']?.toString();
  }

  // 这个方法是 实现父类BaseHttpBean 的方法,下面会详解
  @override
  UserBean fromJson(dynamic json) {
    return UserBean.fromJson(json);
  }
}
// 2. 登录网络请求--事例
void login(){
    AppRequest.login("account","password", cancelToken).then((UserBean bean) {
      // 成功处理 ...
    }).catchError((e) {
      // 异常处理 ...
    });
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容