Flutter網絡請求與JSON解析


本文介紹如何在Flutter中創建HTTP網絡請求和對請求的json string進行類型解析.

  • 網絡請求

官方使用的是用dart io中的HttpClient發起的請求,但HttpClient本身功能較弱,很多常用功能都不支持。

建議使用dio 來發起網絡請求,它是一個強大易用的dart http請求庫,支持Restful API、FormData、攔截器、請求取消、Cookie管理、文件上傳/下載……

 

dart:io 

 

 發起HTTP請求

 

http支持位於dart:io,所以要創建一個HTTP client, 我們需要添加一個導入:

 

import 'dart:io'; var httpClient = new HttpClient();

 

該 client 支持常用的HTTP操作, such as GETPOSTPUTDELETE.

 

處理異步

 

注意,HTTP API 在返回值中使用了Dart Futures。 我們建議使用async/await語法來調用API。

 

網絡調用通常遵循如下步驟:

 

  1. 創建 client.
  2. 構造 Uri.
  3. 發起請求, 等待請求,同時您也可以配置請求headers、 body。
  4. 關閉請求, 等待響應.
  5. 解碼響應的內容.

 

Several of these steps use Future based APIs. Sample APIs calls for each step above are: 其中的幾個步驟使用基於Future的API。上面步驟的示例:

 

get() async {
  var httpClient = new HttpClient();
  var uri = new Uri.http(
      'example.com', '/path1/path2', {'param1': '42', 'param2': 'foo'});
  var request = await httpClient.getUrl(uri);
  var response = await request.close();
  var responseBody = await response.transform(UTF8.decoder).join();
}

dio

添加依賴

dependencies:
  dio: ^x.x.x // latest version

發起一個 GET 請求 :

Response response;
response=await dio.get("/test?id=12&name=wendu")
print(response.data.toString());
// 請求參數也可以通過對象傳遞,上面的代碼等同於:
response=await dio.get("/test",data:{"id":12,"name":"wendu"})
print(response.data.toString());

發起一個 POST 請求:

response=await dio.post("/test",data:{"id":12,"name":"wendu"})

發起多個並發請求:

response= await Future.wait([dio.post("/info"),dio.get("/token")]);

下載文件:

response=await dio.download("https://www.google.com/","./xx.html")

發送 FormData:

FormData formData = new FormData.from({
   "name": "wendux",
   "age": 25,
});
response = await dio.post("/info", data: formData)

 

通過FormData上傳多個文件:

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")
});
response = await dio.post("/info", data: formData)
  • JSON解析

使用 dart:convert手動序列化JSON

Flutter中基本的JSON序列化非常簡單。Flutter有一個內置dart:convert庫,其中包含一個簡單的JSON編碼器和解碼器。

以下是一個簡單的user model的示例JSON。

{ "name": "John Smith", "email": "john@example.com" }

有了dart:convert,我們可以用兩種方式來序列化這個JSON model。我們來看看這兩種方法:

內連序列化JSON

通過查看dart:轉換JSON文檔,我們發現可以通過調用JSON.decode方法來解碼JSON ,使用JSON字符串作為參數。

Map<String, dynamic> user = JSON.decode(json); print('Howdy, ${user['name']}!'); print('We sent the verification link to ${user['email']}.');

不幸的是,JSON.decode()僅返回一個Map<String, dynamic>,這意味着我們直到運行時才知道值的類型。 通過這種方法,我們失去了大部分靜態類型語言特性:類型安全、自動補全和最重要的編譯時異常。這樣一來,我們的代碼可能會變得非常容易出錯。

例如,當我們訪問nameemail字段時,我們輸入的很快,導致字段名打錯了。但由於這個JSON在map結構中,所以編譯器不知道這個錯誤的字段名(譯者語:所以編譯時不會報錯)。

在模型類中序列化JSON

我們可以通過引入一個簡單的模型類(model class)來解決前面提到的問題,我們稱之為User。在User類內部,我們有:

  • 一個User.fromJson 構造函數, 用於從一個map構造出一個 User實例 map structure
  • 一個toJson 方法, 將 User 實例轉化為一個map.

這樣,調用代碼現在可以具有類型安全、自動補全字段(name和email)以及編譯時異常。如果我們將拼寫錯誤或字段視為int類型而不是String, 那么我們的應用程序就不會通過編譯,而不是在運行時崩潰。

user.dart

class User { final String name; final String email; User(this.name, this.email); User.fromJson(Map<String, dynamic> json) : name = json['name'], email = json['email']; Map<String, dynamic> toJson() => { 'name': name, 'email': email, }; } 

現在,序列化邏輯移到了模型本身內部。采用這種新方法,我們可以非常容易地反序列化user。

Map userMap = JSON.decode(json); var user = new User.fromJson(userMap); print('Howdy, ${user.name}!'); print('We sent the verification link to ${user.email}.');

要序列化一個user,我們只是將該User對象傳遞給該JSON.encode方法。我們不需要手動調用toJson這個方法,因為JSON.encode已經為我們做了。

String json = JSON.encode(user);

Json映射到對象

首先要借助一個工具jsonformat 工具下載地址

舉一個例子 json文件,來自玩安卓網站

這是一個相對很復雜的json文件

用jsonview打開查看,這個json文件包含一個data的數組和兩個變量,然后數組的每一項又包含一個數組和6個變量,然后下一級數組的每一項又包含一個數組和6個變量 
這里寫圖片描述

下面使用jsonformat 轉換成dart bean文件

打開下載的jsonformat ,將json文件copy進去點擊格式化 
這里寫圖片描述

右邊的紅色是我們要填寫的類名稱,對應關系像這樣,這里分別填寫 tree children children ,后兩個相同,點擊生成bean 
這里寫圖片描述

生成代碼如下

import 'dart:convert' show json; class tree { int errorCode; String errorMsg; List<children> data; tree(jsonStr) { var jsonRes = json.decode(jsonStr); errorCode = jsonRes['errorCode']; errorMsg = jsonRes['errorMsg']; data = []; for (var dataItem in jsonRes['data']){ data.add(new children(dataItem)); } } @override String toString() { return '{"errorCode": $errorCode,"errorMsg": ${errorMsg != null?'${json.encode(errorMsg)}':'null'},"data": $data}'; } } class children { int courseId; int id; int order; int parentChapterId; int visible; String name; List<children> children; children(jsonRes) { courseId = jsonRes['courseId']; id = jsonRes['id']; order = jsonRes['order']; parentChapterId = jsonRes['parentChapterId']; visible = jsonRes['visible']; name = jsonRes['name']; children = []; for (var childrenItem in jsonRes['children']){ children.add(new children(childrenItem)); } } @override String toString() { return '{"courseId": $courseId,"id": $id,"order": $order,"parentChapterId": $parentChapterId,"visible": $visible,"name": ${name != null?'${json.encode(name)}':'null'},"children": $children}'; } } class children { int courseId; int id; int order; int parentChapterId; int visible; String name; List<dynamic> children; children(jsonRes) { courseId = jsonRes['courseId']; id = jsonRes['id']; order = jsonRes['order']; parentChapterId = jsonRes['parentChapterId']; visible = jsonRes['visible']; name = jsonRes['name']; children = []; for (var childrenItem in jsonRes['children']){ children.add(childrenItem); } } @override String toString() { return '{"courseId": $courseId,"id": $id,"order": $order,"parentChapterId": $parentChapterId,"visible": $visible,"name": ${name != null?'${json.encode(name)}':'null'},"children": $children}'; } }

接下來就是在APP中請求網絡,將其轉換成dart bean

    ///封裝的get請求 _networkUtil,可以替換成自己的請求方式 ///發起get網絡請求並且轉換json Future<dynamic> requestGet(String url) { return http.get(url) .then((http.Response response) { final String res = response.body; final int statusCode = response.statusCode; if (statusCode < 200 || statusCode > 400 || json == null) { throw new Exception("Error while fetching data"); } ///有值 return _decoder.convert(res); }); } ///這里返回的就是 Future<Tree> 對象,(fillUrl(TREE_LIST)是請求的url Future<Tree> fetchTree() { return _networkUtil.requestGet(fillUrl(TREE_LIST)).then((dynamic res) { ///可以這樣取值 return new Tree.map(res); }); }
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM