*
flutter之从零开始搭建(一)之 BottomNavigationBar
<https://juejin.im/post/5b3ee0b66fb9a04f8a21678c>

*
flutter之从零开始搭建(二)之 Navigator路由
<https://juejin.im/post/5b3ee117f265da0fb0184db4>

*
flutter之从零开始搭建(二)之 网络请求 <https://juejin.im/post/5b3f0c406fb9a04faa7984b5>

项目还是在原来的基础上搭建,具体的可以看上面的连接

这次,我们来介绍下网络请求,并且将请求到的数据设置到ListView列表中。老规矩,先来看下效果图



页面看起来不错吧,在动手之前还是得说一下,首页数据来自wanandroid <http://www.wanandroid.com/>
提供,毕竟用了别人的东西就得标明。

实战

flutter请求网络有两种,一种是http请求,一种是HttpClient请求,下面来分别来使用一下。

http方式

在使用http方式请求网络时,需要导入http包
//导入网络请求相关的包 import 'package:http/http.dart' as http; void _pullNet() { http.
get("http://www.wanandroid.com/project/list/1/json?cid=1") .then((http.Response
response) {var convertDataToJson = JSON.decode(response.body);
convertDataToJson = convertDataToJson["data"]["datas"]; //打印请求的结果
print(convertDataToJson);//更新数据 setState(() { data = convertDataToJson; }); });
}
httpClient方式

需要导入httpClient包
import 'dart:io'; void _httpClient() async { var responseBody; var httpClient =
new HttpClient(); var request = await httpClient.getUrl( Uri.parse(
"http://www.wanandroid.com/project/list/1/json?cid=1")); var response = await
request.close();//判断是否请求成功 if (response.statusCode == 200) { //拿到请求的数据
responseBody =await response.transform(utf8.decoder).join();
//解析json,拿到对应的jsonArray数据 var convertDataToJson = jsonDecode(responseBody)[
"data"]["datas"]; //更新数据 setState(() { data = convertDataToJson; }); } else {
print("error"); } }
知道了网络请求的概念,那么,我们先来写下界面

ListView界面布局

打开HomePage,
import 'package:flutter/material.dart'; class HomePage extends StatefulWidget {
@override State<StatefulWidget> createState() { // TODO: implement createState
return new HomeState(); } } @override Widget build(BuildContext context) { //
TODO: implement build return new Scaffold( body: new ListView( children:
<Widget>[ _getItem2(), _getItem2() ]), ); } Widget _getItem2() {return new
Card(child:new Padding( padding: const EdgeInsets.all(10.0), child:
_getRowWidget2(),), elevation:3.0, margin: const EdgeInsets.all(10.0),); }
Widget _getRowWidget2() {return new Row(children: <Widget>[ new Flexible( flex:
1, fit: FlexFit.tight, //和android的weight=1效果一样 child: new Stack(children:
<Widget>[new Column(children: <Widget>[ new Text("title".trim(), style: new
TextStyle(color: Colors.black, fontSize:20.0,), textAlign: TextAlign.left), new
Text("desc", maxLines: 3,) ],) ],) ), new ClipRect(child: new
FadeInImage.assetNetwork( placeholder:"images/ic_shop_normal.png", image:
"images/ic_shop_normal.png", width: 50.0, height: 50.0, fit:
BoxFit.fitWidth,),), ],); }
效果如下



ListView感觉看起来像是Android中的ScrollView+LineaLayout.vertical。


flutter的布局其实是个特别头疼的问题,widget特别多,没有android那么方便,也没有react-native的flex布局方便,迷之缩进更让人想删除widget都变得特别的困难,所以,在做布局这部分,我们尽可能的将widget做成分割出来,做成一个个的方法widget,然后组合起来。

数据填充


我们看到ListView接收的是一个widget数组,后台返回给我们jsonArray数据的时候,我们完全可以使用map来遍历数据,然后返回widget给Listview的children。

flutter是有生命周期的,大致生命周期可以分为
- initState : 初始化widget的时候调用,只会调用一次。
- build : 初始化之后开始绘制界面,当setState触发的时候会再次被调用
- didUpdateWidget : 当触发setState时,会被调用
- dispose : 页面销毁的时候调用

如果把他们和react-native进行分类看的话,下面是个对比

flutter react native
initState Mount等函数
didUpdateWidget update等函数
dispose Unmount等函数
所以,我们在请求网络的时候,将数据请求放在initState进行处理,下面贴出代码。
import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'
;import 'package:http/http.dart' as http; //导入网络请求相关的包 class HomePage extends
StatefulWidget { @override State<StatefulWidget> createState() { // TODO:
implement createState return new HomeState(); } } class HomeState extends State<
HomePage> { //数据源 List data; @override void initState() { // TODO: implement
initState super.initState(); _pullNet(); } void _pullNet() async { await
http.get("http://www.wanandroid.com/project/list/1/json?cid=1")
.then((http.Response response) {var convertDataToJson =
JSON.decode(response.body); convertDataToJson = convertDataToJson["data"][
"datas"]; print(convertDataToJson); setState(() { data = convertDataToJson; });
}); }@override Widget build(BuildContext context) { // TODO: implement build
return new Scaffold( body: new ListView( children: _getItem() ), ); }
List<Widget> _getItem() {return data.map((item) { return new Card(child: new
Padding( padding: const EdgeInsets.all(10.0), child: _getRowWidget(item),),
elevation:3.0, margin: const EdgeInsets.all(10.0),); }).toList(); } Widget
_getRowWidget(item) {return new Row(children: <Widget>[ new Flexible( flex: 1,
fit: FlexFit.tight,//和android的weight=1效果一样 child: new Stack(children: <Widget>[
new Column(children: <Widget>[ new Text("${item["title"]}".trim(), style: new
TextStyle(color: Colors.black, fontSize:20.0,), textAlign: TextAlign.left), new
Text("${item["desc"]}", maxLines: 3,) ],) ],) ), new ClipRect(child: new
FadeInImage.assetNetwork( placeholder:"images/ic_shop_normal.png", image:
"${item['envelopePic']}", width: 50.0, height: 50.0, fit: BoxFit.fitWidth,),),
],); }
然后我们来看下效果图



相信大家看到了一闪而过的红色报警图,虽然不影响最后的显示效果,但是,我们必须得去处理。

在控制台中,我看到了这样的一句异常
The method 'map' was called on null
看到map我们这才焕然大悟,因为网络请求是异步的,当前界面因为要执行build界面的绘制,导致我们在_getItem
中map遍历data数据时是个空值,然后再异步请求成功后,setState又重新给data赋了值,然后触发了界面重新绘制,这时候,map遍历是有值,然后就出现了一下会出现异常,然后又好了的原因。


我们得想个办法,并且能优雅的去解决这个问题,对了,我们只需要在ListView中对data进行判空处理不就行了吗,如果为空的话,我们给他设置一个预加载页面,如果不为空的话,直接就当前现在的这套流程。

ok,思路有了,开始干吧

直接看build方法进行更改
@override Widget build(BuildContext context) { // TODO: implement build return
new Scaffold( body: new ListView( children: data != null ? _getItem() :
_loading()), ); }//预加载布局 List<Widget> _loading() { return <Widget>[ new
Container( height:300.0, child: new Center(child: new Column(
mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[new
CircularProgressIndicator( strokeWidth:1.0,), new Text("正在加载"), ],)),) ]; }
然后我们再来看看效果图



gay gay gayhub <https://github.com/MRwangqi/flutter_dev>

下次再见咯!

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信