网络请求 Dio 官方https://github.com/flutterchina/dio
添加依赖
1 2 dependencies: dio: ^3.0.9
导入并创建dio实例:
1 2 import 'package:dio/dio.dart' ;Dio dio = Dio();
设置配置参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var dio = Dio(BaseOptions( baseUrl: "http://httpbin.org/" , connectTimeout: 5000 , receiveTimeout: 100000 , headers: { HttpHeaders.userAgentHeader: "dio" , "api" : "1.0.0" , }, contentType: Headers.jsonContentType, responseType: ResponseType.plain, ));
示例
发起 GET 请求 :
1 2 3 Response response; response=await dio.get ("/test?id=12&name=wendu" ) print (response.data.toString());
对于GET请求我们可以将query参数通过对象来传递,上面的代码等同于:
1 2 response=await dio.get ("/test" ,queryParameters:{"id" :12 ,"name" :"wendu" }) print (response);
发起一个 POST 请求:
1 response=await dio.post("/test" ,data:{"id" :12 ,"name" :"wendu" })
发起多个并发请求:
1 response= await Future.wait([dio.post("/info" ),dio.get ("/token" )]);
下载文件:
1 response=await dio.download("https://www.google.com/" ,_savePath);
发送 FormData:
1 2 3 4 5 FormData formData = new FormData.from({ "name" : "wendux" , "age" : 25 , }); response = await dio.post("/info" , data: formData)
如果发送的数据是FormData,则dio会将请求header的contentType设为“multipart/form-data”。
通过FormData上传多个文件:
1 2 3 4 5 6 7 8 9 10 11 12 FormData formData = new FormData.from({ "name" : "wendux" , "age" : 25 , "file1" : new UploadFileInfo(new File("./upload.txt" ), "upload1.txt" ), "file2" : new UploadFileInfo(new File("./upload.txt" ), "upload2.txt" ), "files" : [ new UploadFileInfo(new File("./example/upload.txt" ), "upload.txt" ), new UploadFileInfo(new File("./example/upload.txt" ), "upload.txt" ) ] }); response = await dio.post("/info" , data: formData)
值得一提的是,dio内部仍然使用HttpClient发起的请求,所以代理、请求认证、证书校验等和HttpClient是相同的,我们可以在onHttpClientCreate回调中设置,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) { client.findProxy = (uri) { return "PROXY 192.168.1.2:8888" ; }; httpClient.badCertificateCallback=(X509Certificate cert, String host, int port){ if (cert.pem==PEM){ return true ; } return false ; }; };
注意,onHttpClientCreate会在当前dio实例内部需要创建HttpClient时调用,所以通过此回调配置HttpClient会对整个dio实例生效,如果你想针对某个应用请求单独的代理或证书校验策略,可以创建一个新的dio实例即可。
Retrofit For Dart 官网地址https://pub.flutter-io.cn/packages/retrofit
添加依赖
1 2 3 4 5 6 7 8 dependencies: retrofit: ^1.3.4 logger: ^0.9.1 dev_dependencies: retrofit_generator: ^1.3.4+2 build_runner: ^1.10.0 json_serializable: ^3.2.0
注意
json_serializable必须要添加 否则实体类不会自动生成
一般步骤为:
(1) 引入json_annotation ,即插入
1 import 'package:json_annotation/json_annotation.dart' ;
(2) 指定此类的生成代码,
(3) 添加序列化标注,注意括号
(4) 新建类和属性(字段)以及带参构造方法 ,注意属性类型。
(5) 添加反序列化和序列化方法fromJson 和toJson
即
1 2 factory 类名.fromJson(Map <String , dynamic > json) =>_$类名FromJson(json);Map <String , dynamic > toJson() => _$类名ToJson(this );
(6) 生成文件
1 flutter pub run build_runner build
Mac上已生成文件无法覆盖,可以按如下命令操作
1 flutter pub run build_runner build --delete-conflicting-outputs
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import 'package:json_annotation/json_annotation.dart' ;part 'ResultVo.g.dart' ;@JsonSerializable ()class ResultVo { int code; String msg; dynamic obj; ResultVo({this .code, this .msg, this .obj}); factory ResultVo.fromJson(Map <String , dynamic > json) => _$ResultVoFromJson(json); Map <String , dynamic > toJson() => _$ResultVoToJson(this ); }
接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import 'package:retrofit/retrofit.dart' ;import 'package:dio/dio.dart' ;import 'package:qggj_android/model/ResultVo.dart' ;part 'RestClient.g.dart' ;@RestApi (baseUrl: "http://192.168.6.200:31199/" )abstract class RestClient { factory RestClient(Dio dio, {String baseUrl}) = _RestClient; @POST ("/teacherSign/login" ) Future<ResultVo> teacher_login(@Field () String account,@Field () String password); } final dio = Dio(); final client = RestClient(dio);final logger = Logger();
调用
1 2 3 4 5 6 7 8 9 client .teacher_login(this .username, this .userpwd) .then((value) => {login(value)}) .catchError((err) => { ToastHelper.showToast(context, "请求失败!" ) }) .whenComplete( () => LoadingHelper.hideLoading(context)); },
官方示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @GET ("/tasks/{id}" )Future<Task> getTask(@Path ("id" ) String id); @GET ('/demo' )Future<String > queries(@Queries () Map <String , dynamic > queries); @GET ("https://httpbin.org/get" )Future<String > namedExample( @Query ("apikey" ) String apiKey, @Query ("scope" ) String scope, @Query ("type" ) String type, @Query ("from" ) int from ); @POST ("/tasks" )Future<Task> createTask(@Body () Task task); @POST ("/teacherSign/login" )Future<ResultVo> teacher_login(@Field () String account,@Field () String password); @POST ("http://httpbin.org/post" )Future<void > createNewTaskFromFile(@Part () File file); @POST ("http://httpbin.org/post" )@FormUrlEncoded ()Future<String > postUrlEncodedFormData(@Field () String hello); @PATCH ("/tasks/{id}" )Future<Task> updateTaskPart(@Path () String id, @Body () Map <String , dynamic > map); @PUT ("/tasks/{id}" )Future<Task> updateTask(@Path () String id, @Body () Task task); @DELETE ("/tasks/{id}" )Future<void > deleteTask(@Path () String id);
JSON解析 Dart不支持带有泛型的实体类直接转换。
读取本地JSON文件 比如要读取项目根目录下的assets/person.json
首先要在 pubspec.yaml 中做如下配置:
1 2 3 4 5 flutter: uses-material-design: true assets: - assets/person.json
导入如下几个依赖库:
1 2 3 4 5 6 import 'package:flutter/services.dart' ; import 'dart:convert' ; import 'dart:async' ;
实体类
1 2 3 4 5 6 7 8 9 10 11 class Person { String name; int age; double height; Person({this .name, this .age, this .height}); factory Person.fromJson(Map <String , dynamic > json) { return Person(name: json['name' ], age: json['age' ], height: json['height' ]); } }
读取转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import 'package:flutter/services.dart' ;import 'dart:convert' ;import 'dart:async' ;import '../models/person.dart' ;Future<String > _loadPersonJson() async { return await rootBundle.loadString('assets/person.json' ); } Future<Person> decodePerson() async { String personJson = await _loadPersonJson(); final jsonMap = json.decode(personJson); print ('jsonMap runType is ${jsonMap.runtimeType} ' ); Person person = Person.fromJson(jsonMap); print ( 'person name is ${person.name} , age is ${person.age} , height is ${person.height} ' ); return person; }
JSON和Map互转 添加引用
JSON字符串转Map
1 2 3 4 Map <String , dynamic > user = JSON.decode(json);print ('Howdy, ${user['name' ]} !' );print ('We sent the verification link to ${user['email' ]} .' );
对象转JSON字符串
1 String json = JSON.encode(user);
JSON字符串转Model类 简单对象转换 json
1 2 3 4 5 { "name" : "jack" , "age" : 18 , "height" : 175.0 }
实体类
1 2 3 4 5 6 7 8 9 10 11 class Person { String name; int age; double height; Person({this .name, this .age, this .height}); factory Person.fromJson(Map <String , dynamic > json) { return Person(name: json['name' ], age: json['age' ], height: json['height' ]); } }
调用
1 2 3 final jsonMap = json.decode(personJson);print ('jsonMap runType is ${jsonMap.runtimeType} ' );Person person = Person.fromJson(jsonMap);
输出如下
1 jsonMap runType is _InternalLinkedHashMap<String, dynamic>
可以看出 json.decode(personJson) 方法返回的类型为 _InternalLinkedHashMap ,意思就是这个 Map 的 key 为 String 类型,而 value 的类型为 dynamic 的,也就是动态的
数组的转换 1 2 3 4 5 6 7 8 9 10 [ { "id" : 1 , "name" : "Jack" } , { "id" : 2 , "name" : "Rose" } ]
实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class MemberList { List <Member> memberList; MemberList({this .memberList}); factory MemberList.fromJson(List <dynamic > listJson) { List <Member> memberList = listJson.map((value) => Member.fromJson(value)).toList(); return MemberList(memberList: memberList); } } class Member { int id; String name; Member({this .id, this .name}); factory Member.fromJson(Map <String , dynamic > json) { return Member(id: json['id' ], name: json['name' ]); } }
调用
1 2 3 4 List <dynamic > list = json.decode(memberListJson);MemberList memberList = MemberList.fromJson(list); memberList.memberList .forEach((member) => print ('member name is ${member.name} ' ));
带有数组的对象转换 1 2 3 4 5 6 7 { "name" : "China" , "cities" : [ "Beijing" , "Shanghai" ] }
实体类
1 2 3 4 5 6 7 8 9 10 11 12 class Country { String name; List <String > cities; Country({this .name, this .cities}); factory Country.fromJson(Map <String , dynamic > json) { var originList = json['cities' ]; List <String > cityList = new List <String >.from(originList); return Country(name: json['name' ], cities: cityList); } }
调用
1 2 Map <String , dynamic > jsonMap = json.decode(countryJson);Country country = Country.fromJson(jsonMap);
复杂的对象数组嵌套 json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 { "id" : "0302" , "class_name" : "三年二班" , "students" : [ { "name" : "叶湘伦" , "sex" : "男" } , { "name" : "路小雨" , "sex" : "女" } ] }
实体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class ClassInfo { String id; String name; List <Student> studentList; ClassInfo({this .id, this .name, this .studentList}); factory ClassInfo.fromJson(Map <String , dynamic > json) { final originList = json['students' ] as List ; List <Student> studentList = originList.map((value) => Student.fromJson(value)).toList(); return ClassInfo(id: json['id' ], name: json['class_name' ], studentList: studentList); } } class Student { String name; String sex; Student({this .name, this .sex}); factory Student.fromJson(Map <String , dynamic > json) { return Student(name: json['name' ], sex: json['sex' ]); } }
调用
1 2 3 4 5 Map <String , dynamic > jsonMap = json.decode(classInfoJson);ClassInfo classInfo = ClassInfo.fromJson(jsonMap); classInfo.studentList .forEach((student) => print ('student name is ${student.name} ' ));
使用插件生成实体类 FlutterJsonBeanFactory插件
除了上面的方式外,我们还可以使用FlutterJsonBeanFactory插件来辅助生成Bean类。 安装FlutterJsonBeanFactory插件很简单,
以Android Studio为例,
依次选择【Android Studio】->【Settings】->【Plugins】,然后搜索FlutterJsonBeanFactory插件安装即可
使用方式
在要生成文件的文件夹上右键New -> dart bean class File from JSON
该插件转换要求JSON的最外层为对象,不能为数组,为数组时无法转换。
在线生成实体类 https://www.psvmc.cn/zjtools/json_dart/index.html
无法抓包 设置Http代理
默认Flutter网络请求是无法通过抓包软件抓包的,如果需要抓包按如下配置
1 2 3 4 5 6 7 8 9 10 11 12 import 'package:dio/dio.dart' ;import 'package:dio/adapter.dart' ;... (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) { client.findProxy = (uri) { return "PROXY localhost:8888" ; }; };