上期回顾
在前面的文章中我们在Flutter中的本地存储,我们可以将用户的数据存储在移动设备上,但是当用户清空设备或者更换设置这些用户存储的信息就会面临丢失的问题。那么,我们就不得不考虑将用户的信息存储一个第三方的地方,没错就是服务器。
那么,今天我们就来看下Flutter中的网络操作。
Flutter中的网络操作
跟前面讲到的本地存储操作一样,Flutter给我们提供了第三发库的支持,同样的下面三个操作
打开项目的pubspec.yaml配置我文件在dependencies:节点下新增如下配置
http: ^0.11.3+16
点击开发工具提示的packages get按钮或者在命令行输入flutter packages get来同步第三方插件
在自己的Dart文件中引入插件即可正常使用了
import ‘package:http/http.dart’ as http
然后我们可以在我们代码中执行如如操作就可以完成http的请求
1 2 3 4 5 6 7
|
var url = "http://example.com/whatsit/create"; http.post(url, body: {"name": "doodle", "color": "blue"}) .then((response) { print("Response status: ${response.statusCode}"); print("Response body: ${response.body}"); });
|
当然它同样支持,get、delete、head、path、put、read等请求。
在上面的请求中我们直接使用http.post()方法便直接给我们返回了一个泛型为Response的Future对象。很好,这个库已经帮我们做好了网络请求的异步操作和异常捕获,所以说我们直接使用就可以了。
如果您向同一服务器发出多个请求,则可以通过使用客户端而不是发出一次性请求来保持打开持久连接。如果您这样做,请务必在完成后关闭客户端:
1 2 3 4 5 6 7 8
|
var client = new http.Client(); client.post( "http://example.com/whatsit/create", body: {"name": "doodle", "color": "blue"}) .then((response) => client.get(response.bodyFields['uri'])) .then((response) => print(response.body)) .whenComplete(client.close);
|
同样的这个库同样支持你自定义自己的HTTPClient
1 2 3 4 5 6 7 8 9 10 11
|
class UserAgentClient extends http.BaseClient { final String userAgent; final http.Client _inner;
UserAgentClient(this.userAgent, this._inner);
Future<StreamedResponse> send(BaseRequest request) { request.headers['user-agent'] = userAgent; return _inner.send(request); } }
|
好吧,接下来,我们来看个例子看看用起来在怎么样
看个例子
我们以github获取用户信息为例来看下http库的用法
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
|
import 'package:flutter/material.dart'; import 'package:http/http.dart' as http;
void main() { runApp(new MaterialApp(home: new MyApp(),)); }
class MyApp extends StatelessWidget { void httpGet() { print("请求开始"); http.get("https://api.github.com/users/flyou").then((response) { print("请求内容:"+response.body); }).catchError((error) { print("请求出错:" + error.toString()); }).whenComplete(() { print("请求完成"); }); }
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("网络操作"), ), body: Center( child: RaisedButton( onPressed: () {httpGet();}, child: Text("获取用户信息"), ), ), ); } }
|
上面的代码很简单,页面上只有一个很简单的RaisedButton,当用户点击时便会触发上,上面的httpGet方法。
httpGet方法里面会调用http的get请求,请求github api,使用then来接收正常的返回信息,使用catchError来接受异常的信息,当请求完成时会触发whenComplete
下面还是来看下效果吧:
点击按钮后控制台输出:
I/flutter (13613): 请求开始
I/flutter (13613): 请求结果:{“login”:”flyou”,”id”:6630762,”avatar_url”:”https://avatars1.githubusercontent.com/u/6630762?v=4","gravatar_id":"","url":"https://api.github.com/users/flyou"………}
I/flutter (13613): 请求完成
上面请求的返回结果比较长,就不全部贴出来了哈
现在我们断开手机的网络来重新试一下:
I/flutter (13613): 请求开始
I/flutter (13613): 请求出错:SocketException: Failed host lookup: ‘api.github.com’ (OS Error: No address associated with hostname, errno = 7)
I/flutter (13613): 请求完成
嗯,和我们的预期一样,现在它走了catchError的回调。
返回数据处理
现在我们使用的接口后台返回的一半都是Json的形式,所以我们也仅仅对json数据的处理做下介绍。
在Flutter中默认已经为我们提供了convert库来处理json字符串的转换
我们可以使用json.encode()或者json.decode()方法来序列化或者反序列化json字符。
好吧,还是来举个例子,还是跟上面的一样请求github api获取用户信息,但是这次我们根据用户输入的用户名来获取用户信息,并且把返回的用户信息合理的显示在屏幕上。
初始化的界面是这样样子的。
界面很简单,最上面一个image,下面是几个ListTitle,在下面就是一个TextField,最下面就是就是一个RaisedButton了。
当我们点击RaisedButton时就会获取TextField输入的内容并且去请求服务器并返回。
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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
|
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:test1/http/User.dart';
void main() { runApp(new MaterialApp( home: MyApp(), )); }
class MyApp extends StatefulWidget { @override State<StatefulWidget> createState() { return MyAppState(); } }
class MyAppState extends State<MyApp> { User user;
void getUserInfo(String userName) { http.get("https://api.github.com/users/$userName").then((respsone) { setState(() { final responseJson = json.decode(respsone.body); user = new User.fromJson(responseJson); print(user.avatar_url); }); }).catchError((error) { print(error.toString()); }).whenComplete(() { print("请求完成"); }); }
@override Widget build(BuildContext context) { TextEditingController controller = new TextEditingController(); return Scaffold( appBar: AppBar( title: Text("网络操作"), ), body: new SingleChildScrollView(child: Column( children: <Widget>[ new Container( child: Image.network(user != null ? user.avatar_url : "https://avatars1.githubusercontent.com/u/6630762?v=4"),width: 50.0,height: 50.0, ), new ListTile( leading: Icon(Icons.person), title: Text("name:" + (user != null &&user.name!=null ? user.name : "暂无")), ), new ListTile( leading: Icon(Icons.location_city), title: Text("location:" + (user != null &&user.location!=null? user.location : "暂无")), ), new ListTile( leading: Icon(Icons.web), title: Text("blog:" + (user != null &&user.blog!=null? user.blog : "暂无"))), new ListTile( leading: Icon(Icons.http), title: Text("html_url:" + (user != null&&user.html_url!=null ? user.html_url : "暂无")), ), new TextField( decoration: InputDecoration(labelText: "请输入你的github用户名"), controller: controller, ), RaisedButton( onPressed: () { getUserInfo(controller.text.toString()); }, child: Text("Get请求"), ), ], ),), ); } }
|
对了,在上面的代码中还用到了一个User对象,这是需要自己定义的用来存储接口返回信息的。
User.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
class User { final String name; final String location; final String blog; final String avatar_url; final String html_url;
User({this.name,this.location, this.blog, this.avatar_url, this.html_url});
factory User.fromJson(Map<String, dynamic> json) { return new User( name: json['name'], location: json['location'], blog: json['blog'], avatar_url: json['avatar_url'], html_url: json['html_url'], ); } }
|
可以发现User类里面很简单,只是定义了几个我们需要的属性,然后通过 定义fromJson方法把值赋值给相应的属性(对Json数据格式不熟悉的童鞋自己去google看下哈)
那么我们来试一下效果吧
当然在使用服务端反悔的数据的时候需要执行判空操作哦
当然,大家也可以在用户点击按钮时弹出dialog提示再请求完成时去除dialog显示,这个例子比较丑大家明白怎么使用就ok啦。
小结
试一试
大家在下面可以按照上面的提示做一下Post请求,或者给请求增加header