Flutter 教程(二)Flutter 组件

614 阅读13分钟

基础组件

Text

Text 是文本控件,类似于 Android 中 TextView。Text的常用属性如下图所示:

image.png

代码示例如下:

Text(' Flutter是谷歌推出的移动端跨平台开发框架,使用的编程语言是Dart',
    maxLines: 2,
    overflow:TextOverflow.ellipsis ,
    style: TextStyle(
      color: Colors.red,
      fontSize: 32,
      decoration:TextDecoration.underline
      ),
    ),

SelectableText

上面介绍的 Text 是无法选择复制的,如果我们需要支持选择复制的功能,这时我们就需要将 Text 改变成 SelectableText。代码示例如下:

SelectableText(
  "Text - ZeroFlutter",
  style: TextStyle(
    // 颜色蓝色
    color: Colors.blue,
    // 字号 24
    fontSize: 24,
  ),
)

Button

Flutter 没有直接给出 Button 组件,而是提供了各种样式的 Button 组件。常用的 Button 组件有:

  • RaisedButton:RaisedButton是中规中矩的按钮,点击的时候会带一点波纹以及阴影效果
  • FlatButton:FlatButton意为平滑的按钮,所以它非常简洁、扁平,没有背景也没有边框
  • IconButton:IconButton 是一个图标控件的按钮,没有背景,没有文字,只有一个图标
  • OutlineButton:OutlineButton是一个带有边框的按钮,但是它也没有背景,点击OutlineButton,边框和背景颜色才会呈高亮显示。
  • FloatingActionButton:悬浮的按钮,专用于分享和导航等需求

代码示例如下:

RaisedButton(
  child: Text('RaisedButton'),
  color: Colors.blue,
  textColor: Colors.red,
  onPressed: ()=>{},
),

FlatButton(
  child: Text('FlatButton'),
  textColor: Colors.red,
  onPressed: ()=>{},
),

IconButton(
  icon: Icon(Icons.close),
  onPressed: ()=>{},
),

OutlineButton(
  child: Text('OutlineButton'),
  color: Colors.blue,
  textColor: Colors.red,
  onPressed: ()=>{},
  
floatingActionButton: FloatingActionButton(
  child: Icon(Icons.add),
  onPressed: ()=>{},
  tooltip: '你点击的是FloatingActionButton',
),  

如果 Button 组件无法满足需求,可以使用 shape 属性来设置按钮的形状,代码示例如下:

OutlineButton(
  child: Text("我是自定义按钮"),
  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
),

Icon

Icon 是图标组件,通常使用系统内置的图标。代码示例如下:

Icon(
    Icons.favorite,
    size: 48,
    color: Colors.red,
)

Image

Image 组件可以加载和显示各种类型的图像,包括网络图片、本地图片等。代码示例如下:

// 加载本地图片
Image(
  image: AssetImage("images/press.jpg"),
  width: 500,
)
// 加载网络图片
Image(
  image: NetworkImage(""),
  width: 200,
),
// 加载手机图片
Image.file(
  File('/storage/emulated/0/Download/one.jpg'),
  width: 200,
),

需要注意,加载本地图片必须在 pubspec.yaml 文件中配置本地图片的位置。代码示例如下:

flutter:
  assets:
    - images/press.jpg

常用的 Iamge 属性如下图所示:

image.png

fit 属性主要用于 Image 组件,用于控制图像在 Image 组件中的缩放和裁剪方式。有三种,分别是:

  • BoxFit.fill:将图像拉伸或压缩以填充整个 Image 组件的边界,不保持图像的原始宽高比。
  • BoxFit.contain:将图像等比例缩放,使图像能够完整地显示在 Image 组件的边界内,并且尽可能地占据最大空间,可能会留下空白区域。
  • BoxFit.cover:将图像等比例缩放,使图像能够完全覆盖 Image 组件的边界,可能会裁剪掉部分图像。

FlutterLogo

是一个显示 Flutter logo 的组件,一般用于开发时占位。代码示例如下:

FlutterLogo(
  size:100.0,
  color:Colors.blue,
),

TextField

TextField 是文本框组件。代码示例如下:

TextEditingController userController=new TextEditingController();
TextEditingController passwordController=new TextEditingController();
body: Center(
  child: Column(
    children: <Widget>[
      Padding(
        padding: EdgeInsets.all(10),
        child: TextField(
          controller: _userController,
          autofocus: false,
          decoration: InputDecoration(
            labelText: '请输入邮箱地址',
            icon: Icon(Icons.email),
            errorText: '邮箱地址输入错误',
          ),
          keyboardType: TextInputType.emailAddress,
          readOnly: false,
          maxLines: 1,
          minLines: 1,
          onChanged: (String text){
            print(text);
          },
          onSubmitted: (String text){
            print('你在文本框中输入了'+text);
          },
          cursorWidth: 10,
          cursorColor: Colors.red,
          cursorRadius: Radius.circular(5),
        ),
      ),
    ],
  ),
),

TextField 的属性如下图所示:

image.png

TextFormField

TextFormField 基于TextField组件封装了一层,能够做到数据的前置校验,也能够设置其默认值。代码示例如下:

TextFormField(
  decoration: InputDecoration(
    labelText: '密码',
  ),
  initialValue: '23365+989+8+98',
  obscureText: true,
  validator: (value) {
    if (value.isEmpty) {
      return '请输入密码';
    }
    return null;
  },
),

TextFieldTextFormField 都是通过 TextEditingController 来操作内部的文本内容的

单一子元素组件(single-child)

单一子元素(single-child)组件,顾名思义就是只能包含一个子组件的组件,常见的有 Container、Padding、Align、Center、FittedBox、AspectRatio、SingleChildScrollView、FractionallySizedBox、ConstrainedBox和Baseline等。下面分别介绍。

Container

Container 是最常用的单一子元素(single-child)组件。代码示例如下

Container(
  // 设置子组件的位置为居中
  alignment: Alignment.center,
  // 默认 Container 是占据整个屏幕的,constraints 属性可以控制 Container 的大小
  constraints: BoxConstraints.expand(width: 100,height: 80),
  //装饰器
  decoration: BoxDecoration(
  // 边框:黄色、大小为5的实线边框
  border: Border.all(color: Colors.yellowAccent, style: BorderStyle.solid, width: 5),
  // 背景图
  image: new DecorationImage(
    image: AssetImage('images/phone.jpg'),
    ),
  // 边框圆角
  borderRadius: BorderRadius.all(Radius.circular(30)),
  //阴影效果
  boxShadow: [
      BoxShadow(
      color: Colors.redAccent,//阴影颜色
      offset: Offset(20, 20),//阴影相偏移量
      blurRadius: 10,//高斯模糊数值
      ),
  ],
),
  //设置旋转角度
  transform: Matrix4.rotationZ(.3),
  child: Text(''),
),

Padding

Padding 用于设置内边距。代码示例如下:

Container(
  width: 200.0,
  height: 200.0,
  color: Colors.blue,
  child: Padding(
      child: Text('我是文本'),
      padding: const EdgeInsets.all(10.0),
  ),
),

Padding的布局分为以下两种情况。

  1. child为空时:产生一个宽为left + right、高为top + bottom的区域。
  2. child不为空时:Padding会将布局约束传递给child,根据设置的padding属性,缩小child的布局尺寸;然后Padding将自己调整到child设置了padding属性的尺寸,在child周围创建空白区域。

EdgeInsets 是用于填充各个方向的空白像素,有三种,分别是:

  • EdgeInsets.symmetric:用于设置对称方向的填充,vertical指top和bottom,horizontal指left和right
  • EdgeInsets.fromLTRB:分别指定4个方向的填充
  • EdgeInsets.All: 所有方向均使用相同数值的填充

Align

Align 用于设置其对齐方式,例如居中、居左、居右等。Align 有两个常用的属性——widthFactor与heightFactor。当Align不设置widthFactor与heightFactor属性的时候,Align只会跟随alignment属性调整其位置。当Align设置这两个属性后,Align会随着这两个属性改变自己的尺寸。代码示例如下:

new Align(
  child: Text('我是一个Align'),
  heightFactor: 2.0,
  alignment: Alignment.center,
),

Center

Center 组件继承自 Align,表示一个居中的组件。代码示例如下:

new Center(
  child: Text('我是一个Align'),
  heightFactor: 2.0,
),

FittedBox

FittedBox 是用于给子组件三种缩放方式的组件。其类似于 Android ImageView 的 scaleType 属性。代码示例如下:

new Column(
  children: <Widget>[
  Container(
      width: 100.0,
      height: 100.0,
      color: Colors.blue,
      child: FittedBox(
          child: Text('BoxFit.contain',style: TextStyle(fontSize: 32),),
          fit: BoxFit.contain,
      ),
  ),
  Container(
      width: 100.0,
      height: 100.0,
      color: Colors.red,
      child: FittedBox(
          child: Text('BoxFit.cover',style: TextStyle(fontSize: 32),),
          fit: BoxFit.cover,
      ),
  ),
  Container(
      width: 100.0,
      height: 100.0,
      color: Colors.yellow,
      child: FittedBox(
          child: Text('BoxFit.fill',style: TextStyle(fontSize: 32),),
          fit: BoxFit.fill,
      ),
  ),
  Container(
      width: 100.0,
      height: 100.0,
      color: Colors.orange,
      child: FittedBox(
          child: Text('BoxFit.scaleDown',style: TextStyle(fontSize: 32),),
          fit: BoxFit.scaleDown,
      ),
  ),
  Container(
      width: 100.0,
      height: 100.0,
      color: Colors.indigo,
      child: FittedBox(
          child: Text('BoxFit.fitHeight',style: TextStyle(fontSize: 32),),
          fit: BoxFit.fitHeight,
       ),
  ),
  ],
),

AspectRatio

AspectRatio的作用是根据设置调整子组件 child 的宽高比。代码示例如下:

new Container(
  width: 200.0,
  color: Colors.blue,
  child: AspectRatio(
  // aspectRatio 子组件表示宽高比
  aspectRatio: 2.0/1.0, 
        child: Container(
        color: Colors.yellow,
        ),
  ),
),

SingleChildScrollView

SingleChildScrollView 的作用是一个只能嵌套一个组件的滚动布局。虽然SingleChildScrollView只能有一个子组件,但是其子组件可以是一个多元素组件。代码示例如下:

new SingleChildScrollView(
  child:Column(
      children: <Widget>[...其他子组件],
  )
),

SingleChildScrollView 默认滚动方向是垂直的,可以通过 scrollDirection属性设置为 scrollDirection属性设置为 Axis.horizontal 改成水平滚动。也可以根据reverse属性设置阅读顺序。

FractionallySizedBox

FractionallySizedBox的用途是基于宽度缩放因子和高度缩放因子来调整布局大小,和FittedBox一样,子组件都有可能超出父组件设置的范围。代码示例如下:

Container(
    color: Colors.yellow,
    height: 50.0,
    width: 50.0,
    child: FractionallySizedBox(
        alignment: Alignment.topLeft,
        widthFactor: 2.0,
        heightFactor: 1.0,
        child: new Container(
            width: 200.0,//(1)
            color: Colors.blue,
        ),
  ),
),

ConstrainedBox

ConstrainedBox是一种有约束性的组件。例如,子组件无论如何都不能超出设置的约定范围。代码示例如下:

ConstrainedBox(
  constraints: BoxConstraints(
      minWidth: 100.0,
      minHeight: 100.0,
      maxWidth: 200.0,
      maxHeight: 200.0,
  ),
  child: Container(
      color: Colors.blue,
      width: 100.0,
      height: 50.0,
  ),
),

ConstrainedBox组件必须设置其constraints属性值,如果不设置的话,虽然编译器不会报错,但是运行之后,App会崩溃并提示错误

Baseline

Baseline是一个基线组件,它可以把不相关的几个组件设置在同一条水平线上进行对齐。代码示例如下:

body:new Row(
  children: <Widget>[
    Baseline(
      child: FlutterLogo(
        size: 100.0,
        colors: Colors.yellow,
      ),
      baseline: 100.0,
      baselineType: TextBaseline.alphabetic,
    ),
    Baseline(
      child: FlutterLogo(
        size: 100.0,
        colors: Colors.blue,
      ),
      baseline: 100.0,
      baselineType: TextBaseline.alphabetic,
    ),
    Baseline(
      child: FlutterLogo(
        size: 100.0,
        colors: Colors.indigo,
      ),
      baseline: 100.0,
      baselineType: TextBaseline.alphabetic,
    ),
  ],
),

Drawer

Drawer 是侧滑菜单组件。代码示例如下:

//main.dart文件
import 'package:flutter/material.dart';
import 'onePage.dart';
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      routes: {
        '/page1':(context)=>RightPage("我的主页",),
        '/page2':(context)=>RightPage("我的相册",),
        '/page3':(context)=>RightPage("我的文件",),
        '/page4':(context)=>RightPage("我的游戏",),
      },
      home: DrawerDemo(title: '侧滑菜单'),
    );
  }
}

class DrawerDemo extends StatefulWidget {
  DrawerDemo({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _DrawerDemoState createState() => _DrawerDemoState();
}

class _DrawerDemoState extends State<DrawerDemo> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text('侧滑菜单'),
        ),
        drawer: Drawer(
          child: ListView(
            children: <Widget>[
              UserAccountsDrawerHeader(
                accountName: Text('liyuanjinglyj'),
                accountEmail: Text('liyuanjinglyj@163.com'),
                currentAccountPicture: GestureDetector(
                    child: new CircleAvatar(
                      backgroundImage: AssetImage('images/header.png'),
                    )
                ),
                decoration: BoxDecoration(
                  color: Colors.blue,
                ),
              ),
              ListTile(
                title: Text('我的主页'),
                leading: Icon(Icons.description),
                trailing: Icon(Icons.arrow_forward_ios),
                onTap: (){
                  Navigator.pushNamed(context, "/page1");
                },
              ),
              ListTile(
                title: Text('我的相册'),
                leading: Icon(Icons.image),
                trailing: Icon(Icons.arrow_forward_ios),
                onTap: (){
                  Navigator.pushNamed(context, "/page2");
                },
              ),
              ListTile(
                title: Text('我的文件'),
                leading: Icon(Icons.insert_drive_file),
                trailing: Icon(Icons.arrow_forward_ios),
                onTap: (){
                  Navigator.pushNamed(context, "/page3");
                },
              ),
              new Divider(),//分割线
              ListTile(
                title: Text('我的游戏'),
                leading: Icon(Icons.videogame_asset),
                trailing: Icon(Icons.arrow_forward_ios),
                onTap: (){
                  Navigator.pushNamed(context, "/page4");
                },
              ),
            ],
          ),
        ),
        body:Center(
          child: Text('主页面',style: TextStyle(fontSize: 50),),
        ),
    );
  }
}
//onePage.dart文件
import 'package:flutter/material.dart';

class RightPage extends StatelessWidget {
  final String title;

  RightPage(this.title);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(this.title),
      ),
      body: Center(
        child: Text(this.title,style: TextStyle(fontSize: 50),),
      ),
    );
  }
}

Swiper

Swiper 是用于轮播广告或者资讯的组件。但是 Swiper组件并不是官方提供的,所以我们需要引入这个组件。Flutter 提供的是 PageView,但是这个使用比较复杂。代码示例如下:

引入组件:

dependencies:
flutter_swiper: ^1.1.6

实现轮播效果:

body:Container(
  height: 200,
  child: Swiper(
    scrollDirection: Axis.horizontal,//设置横向
    itemCount: 4,//数量为4
    autoplay: true,//自动翻页
    itemBuilder: (BuildContext context,int index){
      return Image.network(imgLists[index]);//返回图片
    },
    pagination: SwiperPagination(//创建圆形分页指示
      alignment: Alignment.bottomCenter,//分页指示位置底部中间
      margin: const EdgeInsets.fromLTRB(0, 0, 20, 10),//间距
      builder: DotSwiperPaginationBuilder(//圆形,选中为白色圆点,没选中为黑色圆点
          color: Colors.black54,
          activeColor: Colors.white
      ),
    ),
  ),
),

SliverPersistentHeaderDelegate

SliverPersistentHeaderDelegate 用于实现自定义折叠的效果。代码示例如下:

class MySliverPersistentHeaderDelegate implements SliverPersistentHeaderDelegate{
  @override
  //折叠前大小
  double maxExtent;

  @override
  //折叠后大小
  double minExtent;

  MySliverPersistentHeaderDelegate({this.maxExtent,this.minExtent});

  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    return Stack(
      fit: StackFit.expand,//大小与父组件一样
      children: <Widget>[
        Image.asset(
          'images/phone.jpg',
          fit: BoxFit.cover,//尽可能小,同时覆盖整个目标
        ),
        Positioned(//层叠组件
          left: 20,
          bottom: 20,
          right: 20,
          child: Text(
            '我的相册',
            style: TextStyle(
              fontSize: 30,
              color: Colors.red
            ),
          ),
        ),
      ],
    );
  }

  @override
  bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
    return true;
  }

  @override
  // TODO: implement snapConfiguration
  FloatingHeaderSnapConfiguration get snapConfiguration => null;

  @override
  // TODO: implement stretchConfiguration
  OverScrollHeaderStretchConfiguration get stretchConfiguration => null;

}

多子元素组件

多子元素(multi-child)组件,顾名思义就是可以包含多个子组件的组件。其中常用的有:Scaffold、AppBar、Row、Column、ListView、GridView、CustomScrollView、CustomMultiChildLayout、stack、IndexedStack、Table、Flex、Wrap、Flow等。下面一一介绍:

Scaffold

Scaffold(脚手架),是基于 Material Design 可视化布局的结构,也是Flutter提供的标准化布局容器。它集成了AppBar(顶部导航栏)、body(界面内容)、bottomNavigationBar(底部菜单)、floatingActionButton(右下角按钮)以及drawer(侧滑菜单)。Scaffold 的结构如下:

Return Scaffold(
  AppBar://...
  body://...
  bottomNavigationBar://...
  floatingActionButton://...
  drawer://...
);

AppBar

AppBar是顶部导航栏,它的结构如下图所示:

image.png

使用示例如下:

home: new Scaffold(
  appBar: new AppBar(
    leading: IconButton(
      icon: Icon(Icons.add_to_photos),
      onPressed: ()=>{},
    ),
    title: new Text('AppBar'),
    actions: <Widget>[
      IconButton(
        icon: Icon(Icons.add),
        tooltip: '添加',
        onPressed: ()=>{},
      ),
      IconButton(
        icon: Icon(Icons.delete),
        tooltip: '删除',
        onPressed: ()=>{},
      ),
      IconButton(
        icon: Icon(Icons.search),
        tooltip: '查询',
        onPressed: ()=>{},
      ),
    ],
  ),
),

一般来说,在Flutter开发中,actions最多放3个

Row和Column

Row和Column属于线性布局组件。其中Row是行多子元素组件,Column是列多子元素组件。它们的属性都是类似的,这里以 Row 为例,代码示例如下:

body: new Row(
  mainAxisAlignment: MainAxisAlignment.spaceAround,//(1)
  crossAxisAlignment: CrossAxisAlignment.stretch,//(2)
  mainAxisSize: MainAxisSize.min,//(3)
  textDirection: TextDirection.rtl,//(4)
  children: <Widget>[
    Container(
      width: 100.0,
      height: 100.0,
      color: Colors.yellow,
      alignment: Alignment.center,
      child: Text('1',style: TextStyle(fontSize: 20),),
    ),
    Container(
      width: 100.0,
      height: 100.0,
      color: Colors.deepOrange,
      alignment: Alignment.center,
      child: Text('2',style: TextStyle(fontSize: 20),),
    ),
    Container(
      width: 100.0,
      height: 100.0,
      color: Colors.green,
      alignment: Alignment.center,
      child: Text('3',style: TextStyle(fontSize: 20),),
    ),
  ],
),

常见的Row的属性如下图所示:

image.png

ListView

Flutter中的ListView与Android开发中的ListView、RecycleView有些类似,都是线性列表组件。代码示例如下:

// 创建 ListView 的方式一
ListView(
    itemExtent:30.0, // 子组件的高度范围
    children:<widget>[
        Text('1'),
        Text('2'),
        Text('3'),
        Text('4'),
    ]
),

// 方式二
ListView.builder(
    itemExtent:30.0,
    itemCount:4, // 如果不设置,那么列表是无限的
    itemBuilder:(context,position){
      return Text('$position');
    }
),

// 方式三,separated 方法创建的列表可以加上分割符
ListView.separated(
  itemBuilder:(context,position){
    return Text('$position');
  }
  separatorBuilder:(context,position){
    return Container(
        width:500,
        height:20,
        color:Colors.red,
    );
  }
  itemCount:10,
),

如果想要实现自定义的功能,可以使用 ListView.custom

GridView

GridView 是实现网格结构列表的组件。代码示例如下:

body: GridView.count(
    crossAxisCount: 2,
    mainAxisSpacing: 10.0,
    crossAxisSpacing: 10.0,
    children: <Widget>[
      Container(
          width: 200,
          height: 200,
          color: Colors.yellow,
      ),
      //省略N个Container
    ],
),
body: GridView.extent(
    maxCrossAxisExtent: 130.0,
    mainAxisSpacing: 10,
    crossAxisSpacing: 10,
    children: <Widget>[
      Container(
          width: 200,
          height: 200,
          color: Colors.yellow,
      ),
    ],
),

CustomScrollView

CustomScrollView是可以包裹ListView与GridView的集合组件。主要用于,单个界面并不包含一个滚动列表组件时。代码示例如下:

body: CustomScrollView(
  slivers: <Widget>[
    SliverGrid(
      gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
          maxCrossAxisExtent: 200,
          mainAxisSpacing: 10,
          crossAxisSpacing: 10,
          childAspectRatio: 4
      ),
      delegate: SliverChildBuilderDelegate((BuildContext context,int index){
        return Container(
            alignment: Alignment.center,
            color: Color.fromARGB(255, 255-index*6, 255-index*20, index*20),
            child: Text('gridview$index'),
        );
      },childCount: 10),
    ),
    SliverFixedExtentList(
        itemExtent: 50,
        delegate: SliverChildBuilderDelegate((BuildContext context,int index){
          return Container(
              alignment: Alignment.center,
              color: Colors.teal[100*(index%5)],
              child: Text('listview$index'),
          );
        }),
    ),
  ],
),

CustomMultiChildLayout

CustomMultiChildLayout是一个多节点、自定义布局组件,通过提供的delegate可以实现控制节点的位置以及尺寸,其具体的布局行为如下:

  • 可以决定每个子节点的位置;
  • 可以决定每个子节点的布局约束;
  • 可以决定自身的尺寸,而且自身的尺寸不依赖子节点的尺寸。

代码示例如下:

class _MyLayoutDelegate extends MultiChildLayoutDelegate{
  static const String layoutTitle='layout_bar';
  static const String body='body';
  //布局规则
  @override
  void performLayout(Size size) {
    //布局layout,并返回它的大小,方便后续放body组件
    Size layoutSize=layoutChild(layoutTitle ,new BoxConstraints(maxHeight: size.  
            height,maxWidth: size.width));
    //将layoutTitle放在顶部(0.0,0.0)处
    positionChild(layoutTitle, Offset(0.0,0.0));
    //布局body,约束为剩下的空间
    layoutChild(body, BoxConstraints.tight(Size(size.width,size.height)));
    //将body放在距离layoutTitle下方layoutSize.height处
    positionChild(body, Offset(0.0,layoutSize.height));
  }
  //是否需要重新布局
  @override
  bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) {
    return false;
  }
}
body: CustomMultiChildLayout(
    delegate: _MyLayoutDelegate(),
    children: <Widget>[
        LayoutId(
            id: _MyLayoutDelegate.layoutTitle,
            child: Text('这是Title'),
        ),
        LayoutId(
            id: _MyLayoutDelegate.body,
            child: Text('这是body'),
        ),
    ],
),

Stack

Stack 是一个绝对布局的组件。代码示例如下:

body:Center(
  child: Stack(
    alignment: Alignment(0.1,1),
    children: <Widget>[
      CircleAvatar(
          backgroundImage: AssetImage('images/press.jpg'),
          radius: 100,
      ),
      Container(
        decoration: BoxDecoration(
          color: Colors.black45,
        ),
        child: Text(
          '人民邮电出版社',
          style: TextStyle(
              fontSize: 20,
              fontWeight: FontWeight.bold,
              color: Colors.white
          ),
        ),
      ),
    ],
  ),
),

IndexedStack

IndexedStack继承Stack,通过IndexedStack的index属性,可以直接切换它的子组件。代码示例如下:

body:Center(
  child: IndexedStack(
    index: 2,
    alignment: Alignment.center,
    children: <Widget>[
        Text("第一层"),
        Text("第二层"),
        Text("第三层"),
    ],
  ),
),

Table

Table是一个表格组件。Table组件通过TableRow子属性逐行设置数据,同时通过columnWidths属性设置列宽,通过border属性设置表格边框样式等。代码示例如下:

body:Center(
  child: Table(
    columnWidths: const {
        0: FixedColumnWidth(100.0),
        1: FixedColumnWidth(200.0),
        2: FixedColumnWidth(50.0),
    },
    border: TableBorder.all(
        color: Colors.blue,
        width: 2,
        style: BorderStyle.solid
    ),
    children: [
      TableRow(
        decoration: BoxDecoration(
          color: Colors.yellow
        ),
        children: [
            Text('姓名'),
            Text('职业'),
            Text('年龄'),
        ],
      ),
      TableRow(
        decoration: BoxDecoration(
          color: Colors.yellow
        ),
        children: [
            Text('张三'),
            Text('产品经理'),
            Text('30'),
        ],
      ),
      TableRow(
        decoration: BoxDecoration(
          color: Colors.yellow
        ),
        children: [
            Text('李四'),
            Text('软件工程师'),
            Text('27'),
        ],
      ),
      TableRow(
        decoration: BoxDecoration(
          color: Colors.yellow
        ),
        children: [
            Text('王五'),
            Text('执行总裁'),
            Text('55'),
        ],
      ),
    ],
  ),
),

Flex

Flex 借鉴了前端的Flex布局方式,代码示例如下:

body:Column(
  children: <Widget>[
    Container(
      height: 200,
      child: Flex(
        direction: Axis.horizontal,
        children: <Widget>[
          Expanded(
            flex: 1,
            child: Container(
              color: Colors.yellow,
            ),
          ),
          Expanded(
            flex: 2,
            child: Container(
              color: Colors.blue,
            ),
          ),
        ],
      ),
    ),
  ],
),

Wrap

Wrap组件能代替Row组件,当一行显示不全的时候,会自动进行换行处理。代码示例如下:

body:Wrap(
  spacing: 10,
  runSpacing: 1,
  children: <Widget>[
    FlatButton(
      child: Text('Flutter技术开发'),
    ),
    FlatButton(
      child: Text('Python'),
    ),
    FlatButton(
      child: Text('Vue'),
    ),FlatButton(
      child: Text('Android Studio'),
    ),
    FlatButton(
      child: Text('Django'),
    ),
    FlatButton(
      child: Text('C/C++'),
    ),
    FlatButton(
      child: Text('Qt5'),
    ),
    FlatButton(
      child: Text('Weex'),
    ),
  ],
),

Wrap的属性如下图所示:

image.png

Flow

代码示例如下:

class NightFlowDelegate extends FlowDelegate{
  EdgeInsets margin=EdgeInsets.zero;//默认为0

  NightFlowDelegate({this.margin});
  @override
  void paintChildren(FlowPaintingContext context) {
    var left=margin.left;
    var top=margin.top;
    for(int i=0;i<context.childCount;i++){
      var childWidth=context.getChildSize(i).width+left+margin.right;//子组件的长度
      if(childWidth<context.size.width){
        context.paintChild(
          i,
        transform: new Matrix4.compose(Vector.Vector3(left,top,0.0), 
            Vector.Quaternion(0.0,0.0,0.0,0.0), Vector.Vector3(1.0,1.0,1.0)));
        left = childWidth + margin.left;//确定下一个位置的坐标
      }else{
        left = margin.left;
        top += context.getChildSize(i).height + margin.top + margin.bottom;
        //绘制子组件(有优化)
        context.paintChild(i,
            transform: Matrix4.translationValues(left, top, 0.0) //位移
        );
        left += context.getChildSize(i).width + margin.left + margin.right;
      }
    }
  }

  getSize(BoxConstraints constraints) {
    //指定Flow组件的大小
    return Size(double.infinity, double.infinity);
  }

  @override
  bool shouldRepaint(FlowDelegate oldDelegate) {
    return oldDelegate != this;
  }
}
body:Flow(
  delegate: NightFlowDelegate(margin: EdgeInsets.all(1)),
  children: <Widget>[
    Container(color: Colors.yellow,width: 100,height: 100),
    Container(color: Colors.blue,width: 100,height: 100),
    Container(color: Colors.orange,width: 100,height: 100),
    Container(color: Colors.red,width: 100,height: 100),
    Container(color: Colors.deepPurpleAccent,width: 100,height: 100),
    Container(color: Colors.indigoAccent,width: 100,height: 100),
    Container(color: Colors.lightGreenAccent,width: 100,height: 100),
    Container(color: Colors.greenAccent,width: 100,height: 100),
    Container(color: Colors.yellow,width: 100,height: 100),
  ],
),

自定义组件

Flutter学习之自定义组件

参考