flutter之网络请求Dio

创建Dio单例类

创建全局的dio单例类,不需要每次请求都创建Dio对象,节省系统开支
创建NetManager类

class NetManager {
  static Dio _dio;
  static Dio getDio(String versionStr, String tokenStr) {
    if (_dio == null) {
      _dio = new Dio();
    }

//请求头
    Map<String, dynamic> headers = {
      "os": "ios",
      "Content-Type": "application/json;charset=UTF-8",
      "appname": "crm",
      "version": versionStr
    };
    if (tokenStr == null || tokenStr.length == 0) {
      headers.remove("x-token");
    } else {
      headers.putIfAbsent("x-token", () => tokenStr);
    }

//设置请求头、超时时间等参数
    _dio.options = BaseOptions(
      headers: headers, //application/x-www-form-urlencoded
      contentType: ContentType.parse("application/json;charset=UTF-8"),
      connectTimeout: 15000,
      receiveTimeout: 15000,
    );
    print("token=="+tokenStr.toString());
    print("version=====" + versionStr);

    return _dio;
  }


  /*获取当前版本号*/
  static Future<String> getVersion() async {
    try {
      PackageInfo packageInfo = await PackageInfo.fromPlatform();
      return packageInfo.version;
    } catch (e) {
      print("获取版本好错误=" + e.toString());
    }
  }

}

创建网络请求类HttpTools

//请求成功、请求失败的回调
typedef SuccCallback = Future<void> Function(
    Map<String, dynamic> responseDic, Response response);
typedef FailCallback = Future<void> Function(String errStr, DioError error);

/*区分网络请求类型get、post*/
enum HttpType {
  type_get,
  type_post,
}



/*图片上传类型区分类型*/
enum ImgFileType {
  type_data, //以二进制流形式上传List<int>
  type_file, //以文件形式上传
}

/*存放正在loading的context们*/
final List<BuildContext> contextList = List<BuildContext>();

class HttpTools {
  /*put、post网络请求*/
  static void http(
      BuildContext context,
      HttpType type,
      String url,
      Map<String, dynamic> paraDic,
      SuccCallback succ,
      FailCallback fail) async {
    String urlStr = baseUrl + url; //完整地址,参数是拼接好之后传过来的,不需要再拼接了

    if (paraDic == null) {
      paraDic = Map();
    }

    try {
      Response response;
      NetManager.getVersion().then((versionStr) {
        //获取当前版本号。判断是否需要更新
        Utils.getToken().then((tokenStr) async {
          //获取当前cookie。判断是否登录

          Dio dio = NetManager.getDio(versionStr, tokenStr);

          //根据不同的网络请求修改参数
          if (
              //url == xxx
          ) {
            //登录接口和评论接口都是使用formdata格式提交
            dio.options.contentType =
                ContentType.parse("application/x-www-form-urlencoded");
          }

          HttpTools.showLoading(context, true); //显示loading
          if (type == HttpType.type_get) {
            response = await dio.get(urlStr).catchError((err) {
              HttpTools.showLoading(context, false); //取消loading
              fail(netWrongMsg, err);
            });

            dataProcessing(
                context, dio, urlStr, response, paraDic, succ, fail); //数据处理
          } else if (type == HttpType.type_post) {
            response = await dio.post(urlStr, data: paraDic).catchError((err) {
              HttpTools.showLoading(context, false); //取消loading
              fail(netWrongMsg, err);
            });
            dataProcessing(
                context, dio,urlStr, response, paraDic, succ, fail); //数据处理
          }
        });
      });
    } catch (e) {
      print("url=" + urlStr);
      print("paraDic=" + paraDic.toString());
      print("报错了" + e.toString());
      fail(e.toString(), DioError());
    }
  }

  /*文件上传*/
  static void upload(
      BuildContext context,
      String url,
      String name,
      String fileName,
      String primaryType,
      String subType,
      ImgFileType fileType,
      List<int> data,
      File imgFile,
      Map<String, dynamic> paraDic,
      SuccCallback succ,
      FailCallback fail) async {
    String urlStr = baseUrl + url; //完整地址,参数是拼接好之后传过来的,不需要再拼接了

    if (paraDic == null) {
      paraDic = Map();
    }


    if(fileType==ImgFileType.type_data){//以二进制流形式上传
      paraDic.putIfAbsent(
          name,
              () => new UploadFileInfo.fromBytes(data, fileName,
              contentType: ContentType(primaryType, subType))
      );
    }else if(fileType==ImgFileType.type_file){//以文件形式上传
      paraDic.putIfAbsent(name, ()=>UploadFileInfo(imgFile, fileName));
    }




    FormData formData = new FormData.from(paraDic);
    try {
      Response response;
      NetManager.getVersion().then((versionStr) {
        //获取当前版本号。判断是否需要更新
        Utils.getToken().then((tokenStr) async {

          Dio dio = NetManager.getDio(versionStr, tokenStr);
          
          
          dio.options.contentType = ContentType.parse("multipart/form-data");

          HttpTools.showLoading(context, true); //显示loading
          //获取当前cookie。判断是否登录
          response = await dio
              .post(urlStr, data: formData)
              .catchError((err) {
            HttpTools.showLoading(context, false);
            fail(netWrongMsg, err);
          });
          dataProcessing(context, dio, urlStr, response, paraDic, succ, fail); //数据处理
        });
      });
    } catch (e) {
      print("url=" + urlStr);
      print("paraDic=" + paraDic.toString());
      print("报错了" + e.toString());
      debugPrint('debugPrint');
      fail(e.toString(), DioError());
    }
  }

  /*get和post请求获取到的数据处理*/
  static dataProcessing(BuildContext context,Dio dio, String urlStr, Response response,
      Map<String, dynamic> paraDic, SuccCallback succ, FailCallback fail) {
    //尽量保证url和返回结果一块打印出来
    print("header===");
    print(dio.options.headers);
    print("url=" + urlStr);
    print("paraDic=" + paraDic.toString());
    print("response=" + response.toString());

    HttpTools.showLoading(context, false); //取消loading






    if (response != null && response.statusCode == 200 ) {

      //网络请求成功
      if (response.data.isNotEmpty && response.data is Map<String, dynamic>) {
        //获取Map<String,dynamic>类型的返回数据,我们统一处理
        String errcode = response.data["code"].toString();
        String message = response.data["message"].toString();
        var entity = response.data["data"];
//        bool success = response.data["success"];
        bool success = errcode.isNotEmpty && errcode == "200" ? true : false;

        if (success == true) {
          //请求成功
          if (entity is Map<String, dynamic>) {
            //data是字典
            succ(entity, response);
          } else if (entity is List) {
            //data是数组
            succ({kUndefineKey: entity}, response);
          } else if (entity is String) {
            //data是字符串
            succ({kUndefineKey: entity}, response);
          } else if (entity is num) {
            //data是数值类型
            succ({kUndefineKey: entity}, response);
          } else {
            //未知情况按照成功处理
            succ({}, entity);
          }
        } else {
          if (errcode == "401") {
            //token超时
            Fluttertoast.showToast(msg: "登录超时,请重新登录");
            LoginModel.requestLogoutFun(context); //退出登录的接口,通知后台我退出登录了
            LoginModel.logoutSucc().then((_){//清除本地token
//              RouteHelper.pushWidget(context,MyHomePage(0), replaceRoot: true);
            Navigator.of(context).pushAndRemoveUntil(new MaterialPageRoute(builder: (ctx){
              return new LoginPage();
            }), (Route route)=>false);
            });

          }
//          else if (errcode == "600") {
//            print("版本更新");
//          }
          else {
            //
            if (message.isEmpty || message.length == 0) {
              fail(netWrongMsg, DioError());
            } else {
              //未知状态吗按照错误情况处理
              fail(message, DioError());
            }
          }
        }
      } else {
        //返回数据不是Map<String,dynamic>类型的,我们不统一处理,但是也按照成功的情况处理
//        succ(Map(), response);
        fail(netWrongMsg, DioError());
      }
    } else {
      //此次网络请求不通
      fail(netWrongMsg, DioError());
    }
  }

  /*loading*/
  static void showLoading(BuildContext context, bool isShow) {
    if (isShow == true) {
      //a7c07
      bool isContain = false;
      contextList.forEach((c) {
        if (c.widget.toString() == context.widget.toString()) {
          isContain = true;
        }
      });
      if (!isContain) {
        print("显示loading");
        contextList.add(context);
//        print("数组长度"+contextList.length.toString());
//        contextList.forEach((c){
//          print("数组元素=="+c.widget.toString());
//        });
        showDialog(
            context: context,
            barrierDismissible: false,
            builder: (BuildContext c) {
              return SpinKitFadingCircle(color: Colors.white);
            });
      }
    } else {
      List<BuildContext> removeList = new List();

      contextList.forEach((c) {
        if (context.widget.toString() == c.widget.toString()) {
          print("取消loading");
          removeList.add(c);
          if (Navigator.canPop(c)) {
            Navigator.pop(c);
          }
        }
      });

      removeList.forEach((rc) {
        contextList.remove(rc);
      });
    }
  }
}

关联实体类文件

创建model

import 'package:json_annotation/json_annotation.dart';

part 'messageItemModel.g.dart';

@JsonSerializable()
class MessageItemModel{
  String id = "";//这条消息的id
  String toUserid = "";//这条消息是针对谁的用户id
  String from = ""; //这条消息是谁发起的用户id
  String infoDesc = "";//描述信息
  String createBy = "";//创建人
  String createDate = "";//创建时间
  String read = "";//是否已读 0未读

  String delFlag = "";

  MessageItemModel(this.id, this.toUserid, this.from, this.infoDesc,
      this.createBy, this.createDate, this.read, this.delFlag);



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

  Map<String, dynamic> toJson() => _$MessageItemModelToJson(this);

}

使用

    HttpTools.http(context, HttpType.type_post, requestGetMyInfosAction, {"userId":userId,"PageNo":PageNo,"PageSize":PageSize}, (dic,response){

      if(dic!=null && dic["data"]!=null){
        MineMessageModel model = MineMessageModel.fromJson(dic["data"]);
        model.message = dic["message"];
        succ(model);
      }else{
        fail(netWrongMsg);
      }
    }, (errStr,error){
      fail(errStr);
    });

此时下面代码报错,没关系,先这样写

part 'messageItemModel.g.dart';

在终端的项目根目录执行

flutter packages pub run build_runner build

此时生成了一个messageItemModel.g.dart文件,报错解决了,这个类里边的代码是自动生成的,不要手动修改,否则运行错误

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'messageItemModel.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

MessageItemModel _$MessageItemModelFromJson(Map<String, dynamic> json) {
  return MessageItemModel(
      json['id'] as String,
      json['toUserid'] as String,
      json['from'] as String,
      json['infoDesc'] as String,
      json['createBy'] as String,
      json['createDate'] as String,
      json['read'] as String,
      json['delFlag'] as String);
}

Map<String, dynamic> _$MessageItemModelToJson(MessageItemModel instance) =>
    <String, dynamic>{
      'id': instance.id,
      'toUserid': instance.toUserid,
      'from': instance.from,
      'infoDesc': instance.infoDesc,
      'createBy': instance.createBy,
      'createDate': instance.createDate,
      'read': instance.read,
      'delFlag': instance.delFlag
    };

用到了json_annotation和json_serializable
(json_annotation)[https://pub.dev/packages/json_annotation]
(json_serializable)[https://pub.dev/packages/json_serializable]

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335