前言
在Flutter开发中,多子元素组件包括:Scaffold,AppBar,Row,Column,Stack,IndexedStack,ListView,GridView,Flow,Table,Flex,Wrap,CustomScrollView,CustomMultiChildLayout等,下面博主将一一介绍其使用方式。(本文学完能实现如下效果)
Scaffold
Scaffold是基于Material库的一个与路由相关的,良好的“结构体”,它可以被认为是Flutter为我们提供的一个标准化的布局容器,在开发中,使用的频率非常高,所以必须掌握,我们先来看看其使用代码:
return Scaffold( appBar: AppBar(), body: Row(), bottomNavigationBar: , floatingActionButton: ), );
根据上面的代码,我们可以看到,Scaffold结构体为我们很好继承了AppBar,FloatingActionButton等控件。这里我们先认识一下,待介绍完其他组件,在后续章节深入使用。
AppBar
做过Android开发应该一眼就能看出来,这就是一个标题栏组件,用于控制App的路由,显示标题栏,以及显示右侧的一些操作栏。其绘制区域一般位于屏幕的顶端,我们来看看AppBar的整体分布区域图:
从上图我们可以看到,Leading区域默认是隐藏的,但是如果有左侧滑栏时会显示。并且,当这个界面时上一个界面跳转过来的时候,这里会显示返回键,我们来看看具体使用的代码:
appBar: AppBar( // Here we take the value from the MyHomePage object that was created by // the App.build method, and use it to set our appbar title. title: Text("AppBar"), actions: [ IconButton( icon: Icon(Icons.playlist_play), tooltip: '菜单1', onPressed: (){}, ), IconButton( icon: Icon(Icons.playlist_add), tooltip: '菜单2', onPressed: (){}, ), IconButton( icon: Icon(Icons.playlist_add_check), tooltip: '菜单3', onPressed: (){}, ), ], ),
大多数情况下,使用上面的代码基本囊括了所有的AppBar常用使用方式,但是,可能我们有些情况还需要自定义标题栏。比如左侧不是返回按钮, 可能是商标,或者别的图标或者点击功能,这个时候我们可以通过重写leading属性,代码如下:
leading: IconButton( icon:new Icon(Icons.face), onPressed: (){} ),
这样我们就自定义了leading按钮,上面两段代码合并在一起实现的效果如下:
Row和Column
Row和Column在Flutter里的布局是需要我们终点掌握的内容,他们都属于线性布局。从Android层面来理解,类似的布局是LinearLayout,从前端来看,有点像标签。
需要注意的是,Column是不支持滚动的,如果需要实现滚动功能,则要考虑使用ListView。我们先来看看Row。Row是一个多子元素组件,用于在水平方向上防止并显示子组件,Row的基本属性如下表所示:
属性 |
取值 |
childred | 传入子组件数组 |
crossAxisAlignment | 子组件在纵轴方向上的对齐方式 |
mainAxisAlignment | 子组件在水平方向上的对齐方式 |
textDirection | 布局顺序,一般情况下从左到右 |
mainAxisSize | max,表示尽可能多地占用水平方向上的位置,min则反之 |
了解Row组件的属性后,我们来看看它的使用代码:
body: Row( textDirection: TextDirection.rtl,//从右到左 children: [ Container( width: 100, height: 100, color: Colors.blue, alignment: Alignment.center, child: Text( "A", style: new TextStyle(color: Colors.white,fontSize: 25), ), ), Spacer( flex: 1, ), Container( width: 100, height: 100, color: Colors.blue, alignment: Alignment.center, child: Text( "B", style: new TextStyle(color: Colors.white,fontSize: 25), ), ), Spacer( flex: 1, ), Container( width: 100, height: 100, color: Colors.blue, alignment: Alignment.center, child: Text( "C", style: new TextStyle(color: Colors.white,fontSize: 25), ), ), ], ),
上面代码不用多说前面已经基本介绍过,除了Spacer,它的作用是创建一个可调整的空间隔,可用于调整Flex容器(如行或列)中窗口小部件之间的间距, 显示效果如下:
接着我们再来看看Column组件,其实它和Row组件基本相似,区别仅仅在于是在垂直方向上放置控件,唯一需要注意的是Row和Column里Cross Axis和Main Axis是不一样的,Row的Main Axis是横方向上的,Column的Main Axis是纵方向上的,Cross Axis同理。
ListView
ListView是Android开发中非常重要的组件,与Java开发Android中的ListView,RecycleView类似,作用都是可滚动项的线性列表,里面存放着相关组件的集合。在一般情况下,这些组件的结构都具有重复性,即每个item的结构相同。
ListView构建方式如下:
(1)ListView
(2)ListView.builder
(3)ListView.custom
(4)ListView.separated
我们来看看它的使用方式,代码如下:
body: ListView( padding: const EdgeInsets.all(10.0), itemExtent: 30.0, children: [ Text("One"), Text("Two"), Text("Three"), Text("Four"), Text("Five"), ], ),
这算是最基本的使用方式,显示下过如下:
需要解释两个属性,这里itemExtent表示子项的高度,如果是ListView是水平显示,表示子项的宽度,特别注意,尽量设置这个参数,因为这样不需要动态计算,代码运行效率高,至于其他属性,看看下表:
属性 | 取值 |
shrinkWrap |
是否根据子组件的高度来设置ListView的高度,默认为false |
RepaintBoundart | 当addRepaintBoundaries为true时,避免列表项滚动时重绘 |
AutomaticKeepAlive | 当addRepaintKeepdaries为true时,则ListView滑出屏幕的区域不会被回收 |
接着我们再来看看ListView.builder的使用方式(ListView.builder被用于创建重复的子项布局):
body: ListView.builder( padding: const EdgeInsets.all(10.0), itemExtent: 30.0, itemCount: 5,//可以不传 itemBuilder:(context,position){ return ListItem(); } ),
因为ListView是懒加载,所以可以不指定itemCount。列表有可能是无限长的,而itemBuilder传入的类型是IndexedWidgetBuilder,只会返回一个组件。当我们滚动到具体的位置因子时,就会创建列表项。
接着,我们来看看ListView.separated。他的作用是“分割”,即在列表子项中夹杂其他项,代码的基本结构如下所示:
ListView.separated( itemCount:itemCount, itemBuilder:(context,position){ return ListItem(); } separatorBuilder:(context,position){ return SeparatorItem(); } )
separatorBuilder和itemBuilder基本类似。我们在实际的项目实践中,有可能需要用到分割线来分割列表的每一项item,这个时候ListView.separated就派上用处了。然而,在separatorBuilder里面,不一定只能传入分割线,也可以传入其他东西,比如图片,我们来看看其效果:
body: ListView.separated( itemBuilder: (BuildContext context,int index){ return ListTile(title: Text("第$index列表")); }, separatorBuilder: (BuildContext context,int index){ return Align( alignment: Alignment.centerLeft, child: FlutterLogo(), ); }, itemCount: 20)
显示效果如下图所示:
最后,我们再来看看ListView.custom。它可以通过SliverChildListDelegate来接收IndexedWidgetBuilder,并且为ListView生成列表项,从而实现自定义功能,这里就不赘述了,后续实践中会详细介绍。
以上便是ListView的各种创建方式。我们现在来做一个有意思的效果,这里需要用到ScrollPhysics。这个属性是ListView中的physics上面设置的,包括NeverScrollablePhysics(不滚动效果),BouncingScrollPhysics(IOS效果),ClampingScrollPhysics(Anroid效果),FixedExtentScrollPhysics(固定范围的滚动效果)等,FixedExtentScrollPhysics使用代码如下:
@override Widget build(BuildContext context) { FixedExtentScrollController fixedExtentScrollController = new FixedExtentScrollController(); final List imgList = [ 'https://img.tukuppt.com//ad_preview/00/10/93/5c993b5d9d798.jpg!/fw/780', 'https://img.tukuppt.com//ad_preview/00/05/71/5c98de9ce54c3.jpg!/fw/780', 'https://img.tukuppt.com//ad_preview/00/07/27/5c9902dec7ca4.jpg!/fw/780', 'https://img.tukuppt.com//ad_preview/00/10/95/5c993bc348d70.jpg!/fw/780', 'https://img.tukuppt.com//ad_preview/00/04/92/5c98cbc632893.jpg!/fw/780', 'https://img.tukuppt.com//ad_preview/00/05/74/5c98df32309c8.jpg!/fw/780', 'https://img.tukuppt.com//ad_preview/00/17/37/5c99cebeb89f6.jpg!/fw/780', 'https://img.tukuppt.com//ad_preview/00/15/33/5c99a01628499.jpg!/fw/780', 'https://img.tukuppt.com//ad_preview/00/13/28/5c9970fdc6932.jpg!/fw/780' ]; // This method is rerun every time setState is called, for instance as done // by the _incrementCounter method above. // // The Flutter framework has been optimized to make rerunning build methods // fast, so that you can just rebuild anything that needs updating rather // than having to individually change instances of widgets. return Scaffold( appBar: AppBar( // Here we take the value from the MyHomePage object that was created by // the App.build method, and use it to set our appbar title. leading: IconButton( icon:new Icon(Icons.face), onPressed: (){} ), title: Text("AppBar"), actions: [ IconButton( icon: Icon(Icons.playlist_play), tooltip: '菜单1', onPressed: (){}, ), IconButton( icon: Icon(Icons.playlist_add), tooltip: '菜单2', onPressed: (){}, ), IconButton( icon: Icon(Icons.playlist_add_check), tooltip: '菜单3', onPressed: (){}, ), ], ), body: ListWheelScrollView( controller: fixedExtentScrollController, physics: FixedExtentScrollPhysics(), itemExtent: 150.0, children : imgList.map((img){ return Card( child: Row( children: [ Image.network(img,width: 150.0), Text("随便文字",style: new TextStyle(color: Colors.black45,fontSize: 20.0), ), ], ), ); }).toList(), ), ); }
这段代码的显示的效果如该帖开头动图效果一样,多子元素组件这篇博文就讲到这里,下篇博文介绍剩余组件。