当咱们搭建好了整个APP的页面框架,如今我往Tab页里加点东西:各类分类的新闻列表。也能够参考个人Git,上面有要点注释。html
因为须要请求外部数据,所以引入一个较为方便的http库。官方示例的httpClient也是能够的,可是坑略多,待会儿讲。react
调整代码结构,定义一个Tab
页内通用的列表对象,这种场景下使用ListView.builder()
建立不定长度的列表:git
//由于列表的长度不定,所以须要用有状态类来承载列表 class NewsList extends StatefulWidget{ final String newsType; //新闻类型 @override NewsList({Key key, this.newsType} ):super(key:key); _NewsListState createState() => new _NewsListState(); } class _NewsListState extends State<NewsList>{ ... @override Widget build(BuildContext context){ return new ListView.builder( //ListView.builder很是适合用于建立不肯定长度的的列表 padding: const EdgeInsets.all(16.0), itemCount: data == null ? 0 : data.length, itemBuilder: (context, i) { return _newsRow(data[i]);//把数据项塞入ListView中 } ); } ... }
将Tab
页的数据表达进行结构化处理,在最外层定义新闻Tab
页的类,方便后面使用:github
//定义TAB页对象,这样作的好处就是,能够灵活定义每一个tab页用到的对象,可结合Iterable对象使用,之后讲 class NewsTab { String text; NewsList newsList; NewsTab(this.text,this.newsList); }
到_MyTabbedPageState对象中实例化这些Tab
:数组
//将每一个Tab页都结构化处理下,因为http的请求须要传入新闻类型的参数newsType,所以将新闻类型参数值做为对象属性传入Tab中 final List<NewsTab> myTabs = <NewsTab>[ new NewsTab('头条',new NewsList(newsType: 'toutiao')), //拼音就是参数值 new NewsTab('社会',new NewsList(newsType: 'shehui')), new NewsTab('国内',new NewsList(newsType: 'guonei')), new NewsTab('国际',new NewsList(newsType: 'guoji')), new NewsTab('娱乐',new NewsList(newsType: 'yule')), new NewsTab('体育',new NewsList(newsType: 'tiyu')), new NewsTab('军事',new NewsList(newsType: 'junshi')), new NewsTab('科技',new NewsList(newsType: 'keji')), new NewsTab('财经',new NewsList(newsType: 'caijing')), new NewsTab('时尚',new NewsList(newsType: 'shishang')), ];
因为从新了Tabs,原来的TabBar和TabBarView获取对应值的方式也发生了改变,用map
+toList
方法处理下:app
@override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( backgroundColor: Colors.orangeAccent, title: new TabBar( controller: _tabController, tabs: myTabs.map((NewsTab item){ //NewsTab能够不用声明 return new Tab(text: item.text==null?'错误':item.text); }).toList(), //记住要用toList()转换一下map的结果,不然会因为类型不匹配而报错 indicatorColor: Colors.white, isScrollable: true, //水平滚动的开关,开启后Tab标签可自适应宽度并可横向拉动,并自动从左到右排列,默认关闭 ), ), body: new TabBarView( controller: _tabController, children: myTabs.map((item) { return item.newsList; //使用参数值 }).toList(), ), ); }
以上对新闻页面的结构进行了重构,重头戏就是完善NewsList对象。因而乎,在初始化NewsList对象时发起HTTP请求应该是个不错的办法:框架
具体是怎么初始化数据的,第三步会讲到,踩了很多坑。这里的重点是,Flutter提倡数据驱动组件的建立,组件本身没法触发动态建立对象,只有经过数据绑定的方式,实现对象的重绘和动态加载,原理和react相似,好比:异步
到了这一步,彻底进入踩坑模式。ide
Future<String>
,返回值也须要await
异步处理后才能够转换成须要的数据类型:上图中列举了两种方法,建议使用下面那种,由于若是能从返回值中提取请求获取的数据,便可将全部的http请求封装到API文件中去,没必要写在页面代码中,缘由你们都懂的。
注意在setState()
以前有一句if(!mounted) return
,由于异步请求数据和控件的渲染是同时进行的,若是代码已经执行到了setState
,可是数据尚未获取到,此时setState
触发的控件渲染就会报错,为了不这种空值错误,在setState
以前先判断空间是否已经渲染完成,mounted
即Flutter内置的当前控件的状态标识,记住就好。函数
图中的提示说使用Flexible控件更佳,然而实际上Flexible也会报错。报错的英文大概意思是ListView控件生成未知长度的列表时,老是会自动压缩每个子元素的高度,而Expanded和Flexible都是能够自由伸缩的控件,形成ListView的子元素没法肯定绘制的高度,为了使超出屏幕宽度的新闻标题自动换行,这个时候用ListTile顶替一下吧。
如上图,确定是不行的,控件的子元素是不容许为空的,因而使用条件判断的方式封装一下:
map
数组和Object
数组的使用,newsinfo.["title"]
和newsinfo.title
二者的newsinfo
类型是不同的,详细仍是到源码中去体会吧,注意对比newsinfo
和myTab
这两个的用法。此次页面写的很是辛苦,并且还没实现滚动刷新或顶部下拉刷新的效果,下一篇再更吧,还有不少要点我在源码中有标识,能够去个人Git中慢慢品味,今天就更到这里,滚去睡觉,真的来不起了。
感谢你们的支持,请关注个人Flutter圈子,多多投稿,也能够加入flutter 中文社区(官方QQ群:338252156)共同成长,谢谢你们~