Flutter(六)——多子元素组件:ListView,Scaffold,AppBar,Row,Column

前言

在Flutter开发中,多子元素组件包括:Scaffold,AppBar,Row,Column,Stack,IndexedStack,ListView,GridView,Flow,Table,Flex,Wrap,CustomScrollView,CustomMultiChildLayout等,下面博主将一一介绍其使用方式。(本文学完能实现如下效果)

滚动ListView

Scaffold

Scaffold是基于Material库的一个与路由相关的,良好的“结构体”,它可以被认为是Flutter为我们提供的一个标准化的布局容器,在开发中,使用的频率非常高,所以必须掌握,我们先来看看其使用代码:

return Scaffold(
      appBar: AppBar(),
      body: Row(),
      bottomNavigationBar: ,
      floatingActionButton: 
      ), 
    );

根据上面的代码,我们可以看到,Scaffold结构体为我们很好继承了AppBar,FloatingActionButton等控件。这里我们先认识一下,待介绍完其他组件,在后续章节深入使用。

AppBar

做过Android开发应该一眼就能看出来,这就是一个标题栏组件,用于控制App的路由,显示标题栏,以及显示右侧的一些操作栏。其绘制区域一般位于屏幕的顶端,我们来看看AppBar的整体分布区域图:
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: <Widget>[
            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按钮,上面两段代码合并在一起实现的效果如下:
AppBar实现效果

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: <Widget>[
          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容器(如行或列)中窗口小部件之间的间距, 显示效果如下:
Row显示效果
接着我们再来看看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: <Widget>[
          Text("One"),
          Text("Two"),
          Text("Three"),
          Text("Four"),
          Text("Five"),
        ],
      ),

这算是最基本的使用方式,显示下过如下:
ListView
需要解释两个属性,这里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<String> 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: <Widget>[
            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: <Widget>[
                Image.network(img,width: 150.0),
                Text("随便文字",style: new TextStyle(color: Colors.black45,fontSize: 20.0),
                ),
              ],
            ),
          );
        }).toList(),
      ),
    );
  }

这段代码的显示的效果如该帖开头动图效果一样,多子元素组件这篇博文就讲到这里,下篇博文介绍剩余组件。

发布了96 篇原创文章 · 获赞 129 · 访问量 83万+

猜你喜欢

转载自blog.csdn.net/liyuanjinglyj/article/details/104098744