Flutter组件基本是要用 | 青训营笔记

164 阅读15分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 17 天

Flutter 第一个 Demo Center 组件的使用

import 'package:flutter/material.dart';
void main() {
    runApp(Center(
        child: '我是一个文本内容',
        textDirection: TextDirection.ltr
    ))
}

Flutter 把内容单独抽离成一个组件

在 Flutter 中,自定义组件其实就是一个类,这个类需要继承 StatelessWidget/StatefulWidget。

StatelessWidget 就是无状态组件,即状态不可变的 widget。

StatefulWidget 是有状态组件,持有的状态可能在 widget 生命周期改变。

import 'package:flutter/material.dart';
void main() {
    runApp(MyApp());
}
class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
        // TODO: implement build
        return Center {
            child: Text(
                "我是一个文本内容",
                textDirection: TextDirection.ltr
            )
        }
    }
}

给 Text 组件增加一些装饰

import 'package:flutter/material.dart';
void main() {
  runApp(MyApp());
}
// 自定义组件
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return  Center(
      child: Text(
        '你好 Flutter',
        textDirection: TextDirection.ltr,
        style: TextStyle(
          fontSize: 40.0,
          color: Color.fromRGBO(244, 233, 121, 1)
        ),
      )
    );
  }
}

使用 MaterialApp 和 Scaffold 两个组件装饰 App

MaterialApp

MaterialApp 是一个方便的 Widget,它封装了应用程序实现 Material Design 所需要的一些 Widget。一般作为顶层 widget 使用。

常用属性:

  • home : 主页
  • title : 标题
  • color : 颜色
  • theme : 主题
  • routes : 路由

Scaffold

Scaffold 是 Material Design 布局结构的基本实现。此类提供了用于显示 drawer、snackbar 和底部 sheet 的 API。

主要属性:

  • appBar --> 显示在界面顶部的一个 AppBar。
  • body --> 当前界面所显示的主要内容 Widget。
  • drawer --> 抽屉菜单组件
  • ......
import 'package:flutter/material.dart';
void main() {
  runApp(MyApp());
}
// 自定义组件
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Demo')
        ),
        body: HomeContent(),
      ),
      theme: ThemeData(
        primarySwatch: Colors.yellow
      ),
    );
  }
}
class HomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Center(
        child: Text(
      '你好 Flutter',
      textDirection: TextDirection.ltr,
      style: TextStyle(fontSize: 40.0, color: Color.fromRGBO(244, 233, 121, 1)),
    ));
  }
}

Flutter Container 组件、Text 组件详解

Text 组件

名称功能
textAlign文本对齐方式(center 居中,left 左对齐, right 右对齐,justify 两端对齐)
textDirection文本方向(ltr 从左至右,rtl 从右至左)
overflow文字超出屏幕之后的处理方式(clip 裁剪,fade 渐变,ellipsis 省略号)
textScaleFactor字体显示倍率
maxLines文字显示最大行数
style文字的样式设置

TextStyle 的参数:

名称功能
decoration文字装饰线(none 没有线,lineThrough 删除线,overline 上划线,underline 下划线)
decorationColor文字装饰线颜色
decorationStyle文字装饰线风格([dashed, dotter]虚线,double 两根线,solid 一根实线,wavy 波浪线)
wordSpacing单词间隙(如果是负值,会让单词变得更紧凑)
letterSpacing字母间隙(如果是负值,会让字母变得更紧凑)
fontStyle文字显示(italic 斜体,normal 正常体)
fontSize文字大小
color文字颜色
fontWeight字体粗细(bold 粗体,normal 正常体)

更多参数: docs.flutter.io/flutter/pai…

Flutter Container 组件

名称功能
alignmenttopCenter : 顶部居中对齐 topLeft : 顶部左对齐 topRight : 顶部右对齐 center : 水平垂直居中对齐 centerLeft : 垂直居中水平局左对齐 centerRight : 垂直居中水平居右对齐 bottomCenter: 底部居中对齐 bottomLeft : 底部居左对齐 bottomRight : 底部居右对齐
decoration
marginmargin 属性时表示 Container 与外部其他组件的距离。 EdgeInsets.all(2.0)
transform让 Container 容器进行一些旋转之类的 transform: Matrix4.rotationZ(0.2)
height容器高度
width容器宽度
child容器子元素

更多参考: api.flutter.dev/flutter/wid…

import 'dart:html';
import 'package:flutter/material.dart';
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // MaterialApp 根元素
    return MaterialApp(
       // 利用 Scaffold 组件创建 appBar 和 body
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Demo'),
        ),
        body: HomeContent()
      )
    );
  }
}
class HomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Center(
        // Container 组件内有 child子元素
      child: Container(
        child: Text(
            // 显示文本
          'I am text',
            // 溢出效果
          overflow: TextOverflow.ellipsis,
            // 文字对齐方式
          textAlign: TextAlign.left,
            // 文字最大行数
          maxLines: 1,
            // 文字缩放倍数
          textScaleFactor: 1.2,
            // Text 中的 style 样式
          style: TextStyle(
            fontSize: 16.0,
            // color: Color.fromARGB(a, r, g, b)
            color: Colors.red,
            fontWeight: FontWeight.w800,
            fontStyle: FontStyle.italic,
              // css 中的 text-decoration
            decoration: TextDecoration.lineThrough,
              // decoration 的颜色、样式
            decorationColor: Colors.white,
            decorationStyle: TextDecorationStyle.dashed,
              // 文字之间的间隔
            letterSpacing: 5.0
          ),
        ),
          // Container 子容器的高度、宽度
        height: 300.0,
        width: 300.0,
          // Container 子容器的样式
        decoration: BoxDecoration(
            // 背景颜色
          color: Colors.yellow,
            // 容器边框
          border: Border.all(
            color: Colors.blue,
            width: 2.0
          ),
            // 容器圆角
          borderRadius: BorderRadius.all(
            // Radius.circular(150), // 圆形
            Radius.circular(8)
          )
        ),
        // padding: EdgeInsets.all(50),
        padding: EdgeInsets.fromLTRB(10, 30, 5, 0),
        // transform: Matrix4.translationValues(100, 0, 0),
        // transform: Matrix4.rotationZ(0.3),
        // transform: Matrix4.diagonal3Values(1.2, 1, 1),
        alignment: Alignment.bottomLeft,
      ),
    );
  }
}

Flutter 图片组件

网络图片

图片组件是实现图像的组件,Image 组件有很多构造函数,主要介绍两个:

Image.asset --> 本地图片

Image.network --> 远程图片

Image 组件的常用属性:

名称类型说明
alignmentalignment图片的对齐方式
color / colorBlendMode设置图片的背景颜色,通常和 colorBlendMode 配合一起使用,这样可以是图片颜色和背景色混合。
fitBoxFitfit 属性用来控制图片的拉伸和挤压,这都是根据父容器来的。 BoxFit.fill : 全图显示,图片会被拉伸,并充满父容器。 BoxFit.cover : 全图显示,显示原比例,可能会有空隙。 BoxFit.cover : 显示可能拉伸,可能剪裁,充满(图片要充满整个容器,还不变形)。 BoxFit.fitWidth : 宽度充满(横向充满),现实可能拉伸,可能裁切。 BoxFit.fitHeigth : 高度充满(竖向充满),显示可能拉伸,可能裁切。 BoxFit.scaleDown : 效果和 contain 差不多,但是此属性不允许超过原图片大小,可小不可大
repeat平铺ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。 ImageRepeat : 横向重复,纵向不重复。 ImageRepeat.repeatY : 纵向重复,横向不重复。
width宽度(一般结合 ClipOval 才能看到效果)
height高度(一般结合 ClipOval 才能看到效果)

更多属性参考:api.flutter.dev/flutter/wid…

return: Center {
    child: Container {
        child: Image.network {
            'http://xxxx',
            alignment: Alignment.topLeft,
            color: Colors.red,
            colorBlendMode: BlendMode.colorDodge,
            // repeat: ImageRepeat.repeatX,
            fit: BoxFit.cover
        },
        width: 300.0,
        height: 400.0,
        decoration: BoxDecoration {
            color: Colors.yellow
        }
    }
};

3.6.2 本地图片

  1. 新建对应目录(严格);
  2. 然后打开 pubspec.yaml 声明一下添加的图片文件,注意配对
  3. 在代码中使用
child: Container {
    child: Image.asset(
        'images/a.jpeg',
        fit: BoxFit.cover
    ),
    width: 300.0,
    height: 300.0,
    decoration: BoxDecoration(
        color: Colors.yellow
    )
}

Flutter 实现圆角以及实现圆形图片

return Center(
    child: Container(
        width: 300.0,
        height: 300.0,
        decoration: BoxDecoration(
            color: Colors.yellow,
            borderRadius: BorderRadius.circular(150),
            image: DecorationImage(
                image: NetWorkImage('https://xxx'),
                fit: BoxFit.cover
            )
        )
    )
)

更简单的方式

return Center(
    child: Container(
        child: ClipOval(
            child: Image.network('https://xxx'),
            width: 150.0,
            height: 150.0
        )
    )
)

Flutter ListView 基础列表组件、水平列表组件、图表组件

列表布局是我们项目开发中最常用的一种布局方式。Flutter 中我们可以通过 ListView 来定义列表项,支持垂直和水平方向展示。通过一个属性就可以控制列表的显示方向。列表有以下分类:

  1. 垂直列表
  2. 垂直图文列表
  3. 水平列表
  4. 动态列表
  5. 矩阵式列表

列表参数

名称类型说明
scrollDirectionAxisAxis.horizontal 水平列表 Axis.vertical 垂直列表
paddingEdgeInsetsGeometry内边距
resolvebool组件反向排序
childrenList列表元素

Flutter 垂直组件

class HomeContent extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
        // TODO:implement build
        return Center(
            child: ListView(
                // ListView 中的 children 属性
                children: <Widget>[
                    // ListTitle
                    ListTitle(
                        // 列表前面的Icon(也可以是图片)
                        leading: Icon(Icon.phone),
                        // 标题
                        title: Text(
                            'this is list',
                            style: TextStyle(fontSize: 28.0)
                        ),
                        // 次级标题
                        subtitle: Text('this is list this is a list')
                    ),
                    ListTitle(
                        title: Text('this is list'),
                        // 列表后面的 Icon(也可以是图片)
                        trailing: Icon(Icons.phone)
                    ),
                    ListTitle(
                        leading: Icon(Icon.phone),
                        title: Text('this is list'),
                    ),
                ]
            )
        );
    }
}

Flutter 水平列表

class HomeContent extends StatelessWidget(
    @override
    Widget build(BuildContext context) {
        // TODO:implement build
        return Container(
            height: 200.0,
            margin: EdgeInsets.all(5),
            child: ListView(
                // 设置方向
                scrollDirection: Axis.horizontal,
                // children 属性用于布局
                children: <Widget>[
                    Container(
                        width: 180.0,
                        color: Colors.lightBlue
                    ),
                    container(
                        width: 180.0,
                        color: Colors.amber,
                        // 水平列表也能嵌套垂直列表
                        child: ListView(
                            children: <Widget>[
                                Image.network(
                                    'https://xxx'
                                ),
                                SizeBox(height: 16.0),
                                Text('这是一个文本信息',
                                    textAlign: TextAlign.center,
                                     style: TextStyle(
                                        fontSize: 16.0
                                     )
                                )
                            ]
                        )
                    ),
                    Container(
                        width: 180.0,
                        color: Colors.deepOrange
                    ),
                    Container(
                        width: 180.0,
                        color: Colors.deepPurpleAccent
                    )
                ]
            )
        );
    }
)

Flutter ListView 列表组件、动态列表

列表布局是我们项目开发中最常用的一种布局方式。Flutter 中我们可以通过 ListView 来定义列表项,支持垂直和水平方向展示。通过一个属性就可以控制列表的显示方向。列表有以下分类:

  1. 垂直列表
  2. 垂直图文列表
  3. 水平列表
  4. 动态列表
  5. 矩阵式列表

Flutter 列表参数

名称类型说明
scrollDirectionAxisAxis.horizontal 水平列表 Axis.vertical 垂直列表
paddingEdgeInsetsGeometry内边距
resolvebool组件反向排序
chilrenList列表元素

Flutter 动态列表

// ignore_for_file: prefer_const_constructors, sort_child_properties_lastimport 'dart:html';
import 'package:flutter/material.dart';
import 'res/listData.dart';
​
void main() {
  runApp(MyApp());
}
​
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
            title: Text('Flutter Demo'),
        ),
        body: HomeContent()));
  }
}
​
class HomeContent extends StatelessWidget {
  // 自定义方法
  List<Widget> _getData() {
    var tempList = listData.map((value) {
      return ListTile(
        leading: Image.network(value['imageUrl']),
        title: Text(value['title']),
        subtitle: Text(value['author']),
      );
    });
    return tempList.toList();
  }
​
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return ListView(children: this._getData());
  }
}
// ignore_for_file: prefer_const_constructors, sort_child_properties_lastimport 'dart:html';
import 'package:flutter/material.dart';
import 'res/listData.dart';
​
void main() {
  runApp(MyApp());
}
​
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
              title: Text('Flutter Demo'),
            ),
            body: HomeContent()));
  }
}
​
class HomeContent extends StatelessWidget {
  // 自定义方法
  Widget _getListData(ctx, index) {
    return ListTile(
      leading: Image.network(listData[index]['imageUrl']),
      title: Text(listData[index]['title']),
      subtitle: Text(listData[index]['author']),
    );
  }
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return ListView.builder(
      itemCount: listData.length,
      itemBuilder: this._getListData,
    );
  }
}
​

Flutter GridView 组件的常用常用参数

当数据量很大的时候用矩阵方式排列比较清晰。此时我们可以用网络列表组件 GridView 实现布局。

GridView 创建网络列表有多种方式,下面主要介绍两种:

  1. GirdView.count
  2. GridView.builder

常用属性:

名称类型说明
scrollDirectionAxis滚动方法
paddingEdgeInsetsGeometry内边距
resolvebool组件反向排序
crossAxisSpacingdouble水平子 Widget 之间的间距
mainAxisSpacingdouble垂直子 Widget 之间的间距
crossAxisCountint一行的 Widget 数量
childAspectRatiodouble子 Widget 宽高比例
children[]
gridDelegateSliverGridDelegateWithFixedCrossAxisCount(常用) SliverGridDelegateWithMaxCrossAxisExtent控制布局主要用在 GridView.builder 里面

GridView.count

// ignore_for_file: prefer_const_constructors, sort_child_properties_lastimport 'dart:html';
import 'package:flutter/material.dart';
import 'res/listData.dart';
​
void main() {
  runApp(MyApp());
}
​
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
                title: Text('Flutter Demo'),
            ),
            body: HomeContent()
        )
    );
  }
}
​
class HomeContent extends StatelessWidget {
  List<Widget> _getListData() {
    var tempList = listData.map((item) {
      return Container(
        child: Column(
          children: <Widget>[
            Image.network(item['imageUrl']),
            SizedBox(height: 10,),
            Text(
              item['title'],
              textAlign: TextAlign.center,
              style: TextStyle(
                fontSize: 18
              ),
            )
          ],
        ),
        decoration: BoxDecoration(
          border: Border.all(
            color: Color.fromRGBO(233, 233, 233, 0.9),
            width: 1
          )
        ),
      );
    });
    return tempList.toList();
  }
​
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return GridView.count(
      crossAxisSpacing: 20.0, // 水平子 Widget 之间的间距
      mainAxisSpacing: 20.0, // 垂直子 Widget 之间的间距
      padding: EdgeInsets.all(10),
      crossAxisCount: 2, // 一行的 Widget 数量
      children: this._getListData(),
    );
  }
}

GridView.builder

// ignore_for_file: prefer_const_constructors, sort_child_properties_lastimport 'dart:html';
import 'package:flutter/material.dart';
import 'res/listData.dart';
​
void main() {
  runApp(MyApp());
}
​
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
              title: Text('Flutter Demo'),
            ),
            body: HomeContent()));
  }
}
​
class HomeContent extends StatelessWidget {
  Widget _getListData(ctx, index) {
      return Container(
        child: Column(
          children: <Widget>[
            Image.network(listData[index]['imageUrl']),
            SizedBox(height: 10,),
            Text(
              listData[index]['title'],
              textAlign: TextAlign.center,
              style: TextStyle(
                fontSize: 18
              ),
            )
          ],
        ),
        decoration: BoxDecoration(
          border: Border.all(
            color: Color.fromRGBO(233, 233, 233, 0.9),
            width: 1
          )
        ),
      );
  }
​
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return GridView.builder(
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisSpacing: 20.0, // 水平子 Widget 之间的间距
        mainAxisSpacing: 20.0, // 垂直子 Widget 之间的间距
        crossAxisCount: 2, // 一行的 Widget 数量
      ),
      itemCount: listData.length,
      itemBuilder: this._getListData,
    );
  }
}

flutter 页面布局 Padding、Row、Column、Expanded 组件详解

在 HTML 中常见的布局标签都有 padding 属性,但是 Flutter 中很多 Widget 是没有 padding 属性。这个时候我们可以用 Padding 组件处理容器于子元素之间的间距。

Padding

属性说明
paddingpadding 值,EdgeInsets 设置填充的值
child子组件
class LayoutDemo extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
        return Padding(
            padding: EdgeInsets.fromLTRB(0, 0, 10, 0),
            child: GridView.count(
                crossAxisCount: 2,
                childAspectRatio: 5,
                children: <Widget>[
                    Padding(
                        padding: EdgeInsets.formLTRB(10, 10, 0, 0),
                        child: Image.network('https://xxx/1.png', fit: BoxFit.cover)
                    ),
                    Padding(
                        padding: EdgeInsets.formLTRB(10, 10, 0, 0),
                        child: Image.network('https://xxx/2.png', fit: BoxFit.cover)
                    ),
                    Padding(
                        padding: EdgeInsets.formLTRB(10, 10, 0, 0),
                        child: Image.network('https://xxx/3.png', fit: BoxFit.cover)
                    ),
                ]
            )
        )
    }
}

Flutter Row 水平布局组件

属性说明
mainAxisAlignment主轴的排序方式
crossAxisAlignment次轴的排序方式
children组件子元素

自定义Icon组件

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
              title: Text('Flutter Demo'),
            ),
            body: layoutDemo()));
  }
}
​
class layoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return IconContainer(
      Icons.search,
      color: Colors.black,
    );
  }
}
​
class IconContainer extends StatelessWidget {
  late double size;
  late Color color;
  IconData icon;
  IconContainer(this.icon, {this.color = Colors.red, this.size = 32.0}) {}
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      height: 100.0,
      width: 100.0,
      color: this.color,
      child: Center(
        child: Icon(
          this.icon,
          size: this.size,
          color: Colors.white,
        ),
      ),
    );
  }
}

Row 组件使用

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
              title: Text('Flutter Demo'),
            ),
            body: layoutDemo()));
  }
}
​
class layoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      height: 400.0,
      width: 350.0,
      color: Colors.pink,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        crossAxisAlignment: CrossAxisAlignment.start, // 用的比较少
        children: [
          IconContainer(Icons.search, color: Colors.blue),
          IconContainer(Icons.home, color: Colors.orange),
          IconContainer(Icons.select_all, color: Colors.red)
        ],
      ),
    );
  }
}
​
class IconContainer extends StatelessWidget {
  late double size;
  late Color color;
  IconData icon;
  IconContainer(this.icon, {this.color = Colors.red, this.size = 32.0}) {}
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      height: 100.0,
      width: 100.0,
      color: this.color,
      child: Center(
        child: Icon(
          this.icon,
          size: this.size,
          color: Colors.white,
        ),
      ),
    );
  }
}

Flutter Column 垂直布局组件

属性说明
mainAxisAlignment主轴的排序方法
crossAxisAlignment次轴的排序方式
children组件子元素

注: 只需要把上面代码中的 Row 改为 Column 就可以了

Flutter Expanded 类似 Web 中的 Flex 布局

Expanded 可以用在 Row 和 Column 布局中

属性说明
flex元素占整个父 Row/Column 的比例
child子元素

SizeBox 可以作为 padding 另类实现

class layoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Column(
      children: <Widget> [
        Row(
          children: <Widget> [
            Expanded(
              child: Container(
                height: 180,
                color: Colors.black,
                child: Text('你好 Flutter'),
              ),
            )
          ],
        ),
        SizedBox(height: 10,),
        Row(
          children: <Widget> [
            Expanded(
              flex: 2,
              child: Container(
                child: Image.network('https://www.itying.com/images/flutter/1.png', fit: BoxFit.cover),
                height: 200.0,
              ),
            ),
            SizedBox(width: 10,),
            Expanded(
              flex: 1,
              child: Container(
                child: ListView(
                  children: <Widget>[
                    Container(
                      height: 95,
                      child: Image.network('https://www.itying.com/images/flutter/3.png', fit: BoxFit.cover),
                    ),
                    SizedBox(height: 10),
                    Container(
                      height: 95,
                      child: Image.network('https://www.itying.com/images/flutter/4.png', fit: BoxFit.cover),
                    ),
                  ],
                ),
                height: 200.0,
              ),
            )
          ],
        )
      ],
    );
  }
}

Flutter 页面布局、Stack 层叠组件、Stack 与 Align、Stack 与 Positioned 实现定位布局

Stack 组件

Stack 表示堆的意思,我们可以用 Stack 或者 Stack 结合 Align 或者 Stack 结合。Positioned 来实现页面的定位布局。

属性说明属性值
alignment配置所有子元素的显示位置Alignment(x, y): x,y在区间[-1, 1]
children子组件
return Center(
      child: Stack(
        alignment: Alignment(-1, 0.3),
        children: <Widget>[
          Container(
            height: 400,
            width: 300,
            color: Colors.red,
          ),
          Text('我是一个文本1', style: TextStyle(
            fontSize: 20,
            color: Colors.white
          ))
        ],
      ),
    )

Align 组件

Stack 组件中结合 Align 组件可以控制每个子元素的显示位置。

属性说明
top子元素距离顶部的距离
bottom子元素距离底部的距离
left子元素距离左侧的距离
right子元素距离右侧的距离
child子组件
class layoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Center(
      child: Container(
        height: 400,
        width: 300,
        color: Colors.red,
        child: Stack(
          children: [
            Align(
              alignment: Alignment.topLeft,
              child: Icon(Icons.home, size: 40, color: Colors.white),
            ),
            Align(
              alignment: Alignment.center,
              child: Icon(Icons.search, size: 30, color: Colors.orange),
            ),
            Align(
              alignment: Alignment.bottomRight,
              child: Icon(Icons.send, size: 60, color: Colors.yellow),
            ),
          ],
        ),
      ),
    );
  }
}

Positioned 组件

Stack 组件中结合 Positioned 组件也可以控制每个子元素的显示位置。

属性说明
top子元素距离顶部的距离
bottom子元素距离底部的距离
left子元素距离左侧的距离
right子元素距离右侧的距离
child子组件

AspectRatio、Card 卡片组件

AspectRatio 组件

ApsectRatio 的作用是根据设置调整子元素 child 的宽高比。

AspectRatio 首先会在布局限制条件允许的范围内尽可能的扩展,widget 的高度是由宽度和比率决定的,类型与 BoxFit 中的 contain,按照固定比率去尽量占满区域。

如果在满足所有限制条件过后无法找到一个可行的尺寸,AspectRatio 最终将会优先适应布局限制条件,而忽略所设置的比率。

属性说明
aspectRatio宽高比,最终可能不会根据这个值去布局,具体则要看综合因素,外层是否允许这种比率进行布局,这只是一个参考值
child子组件
class layoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      // width: 300,
      child: AspectRatio(
        aspectRatio: 3.0/1.0,
        child: Container(
          color: Colors.red
        ),
      ),
    );
  }
}

Card 组件

Card 是卡片组件块,内容可以有大多数类型的 Widget 构成,Card 具有圆角和阴影,这让它看起来有立体感。

属性说明
margin外边距
child子组件
ShapeCard 的阴影效果,默认的阴影效果为圆角的长方形边。
class layoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return ListView(
      children: <Widget>[
        Card(
          margin: EdgeInsets.all(10),
          child: Column(
            children: <Widget>[
              ListTile(
                title: Text('张三', style: TextStyle(fontSize: 28),),
                subtitle: Text('高级工程师')
              ),
              ListTile(
                title: Text('电话: xxxx')
              ),
              ListTile(
                title: Text('地址:杭州电子科技大学')
              ),
            ],
          )
        )
      ],
    );
  }
}

CircleAvatar 组件

class layoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return ListView(
      children: <Widget>[
        Card(
          margin: EdgeInsets.all(10),
          child: Column(
            children: <Widget>[
              AspectRatio(
                aspectRatio: 20/9,
                child: Image.network('https://www.itying.com/images/flutter/2.png', fit: BoxFit.cover,),
              ),
              ListTile(
                 // 专门用来处理头像的组件 CircleAvatar
                leading: CircleAvatar(
                  backgroundImage: NetworkImage('https://www.itying.com/images/flutter/2.png'),
                ),
                title: Text('糖果屋'),
                subtitle: Text('Candy House'),
              )
            ],
          ),
        ),
      ],
    );
  }
}

循环渲染

import 'res/listData.dart';
​
class layoutDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return ListView(
      children: listData.map((value) {
        return Card(
          margin: EdgeInsets.all(10),
          child: Column(children: <Widget>[
            AspectRatio(
              aspectRatio: 20 / 9,
              child: Image.network(value['imageUrl'], fit: BoxFit.cover,),
            ),
            ListTile(
              leading: CircleAvatar(backgroundImage: NetworkImage(value['imageUrl'])),
              title: Text(value['title']),
              // maxLines 与 overflow 最开始的内容
              subtitle: Text(value['description'], maxLines: 2, overflow: TextOverflow.ellipsis,),
            )
          ]),
        );
      }).toList(),
    );
  }

布局:Wrap组件

Wrap 可以实现流布局,单行的 Wrap 跟 Row 表现几乎一致,单列的 Wrap 则跟 Row 表现几乎一致。但 Row 与 Column 都是单行单列的,Wrap 则突破了这个限制,maxAxis 上空间不足时,则向 crossAxis 上去扩展显示。

属性说明
direction主轴的方向,默认水平
alignment主轴的对齐方式
spacing主轴方向上的间距
textDirection文本方向
verticalDirection定义了 children 摆放方向,默认是 down,见 Flex 相关属性的介绍。
runAlignmentrun 的对齐方式。run 可以理解为新的行或者列,如果是水平方向布局的话,run 可以理解为新的一行
runSpacingrun 的行距

ElevatedButton 定义一个按钮

Flutter 中通过 RaisedButton 定义一个按钮。RaiseButton 里面有很多参数,下面是简单使用:

return ElevatedButton(
  onPressed: () {},
  child: Text('第一季'),
  style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Colors.red)),
)

Flutter StatefulWidget 有状态组件、页面绑定数据、改变页面数据

Flutter 中自定义有状态组件

在 Flutter 中自定义组件其实就是一个类,这个类需要继承 StatelessWidget/StatefulWidget。

StatelessWidget 是无状态组件,状态不可变的 widget;

StatefulWiget 是有状态组件,持有的状态可能在 widget 生命周期改变。通俗地讲:如果我们想要改变页面中的数据的话,这个时候就需要用到 StatefulWidget。

// ignore_for_file: prefer_const_constructors, sort_child_properties_last
import 'dart:html';
import 'package:flutter/material.dart';
import 'res/listData.dart';
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
              title: Text('Flutter Demo'),
            ),
            body: HomePage()));
  }
}
​
// 有状态组件
class HomePage extends StatefulWidget {
  const HomePage({super.key});
​
  @override
  State<HomePage> createState() => _HomePageState();
}
​
class _HomePageState extends State<HomePage> {
  int countNum = 0;
  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        SizedBox(height: 200),
        Chip(label: Text('你好 $countNum')),
        ElevatedButton(
            onPressed: () {
              setState(() {
                // 只有有状态组件里面才有
                this.countNum++;
              });
              ;
            },
            child: Text('按钮'))
      ],
    );
  }
}
class HomePage extends StatefulWidget {
  const HomePage({super.key});
​
  @override
  State<HomePage> createState() => _HomePageState();
}
​
class _HomePageState extends State<HomePage> {
  List list = [];
  @override
  Widget build(BuildContext context) {
    return ListView(
      children: <Widget>[
        Column(
          children: this.list.map((value) {
            return ListTile(
              title: Text(value),
            );
          }).toList(),
        ),
        SizedBox(height: 20),
        ElevatedButton(
            onPressed: () {
              setState(() {
                this.list.add('新增数据1');
                this.list.add('新增数据2');
              });
            },
            child: Text('按钮')
        )
      ],
    );
  }
}

Flutter BottomNavigationBar 自定义底部导航条、实现页面切换

BottomNavigationBar 是底部导航条,可以让我们定义底部 Tab 切换,bottomNavigationBar 是 Scaffold 组件的参数。

BottomNavigation 常见的属性

属性名说明
itemsList 底部导航条按钮集合
iconSizeicon
currentIndex默认选中第几个
onTap选中变化回调函数
fixedColor选中的颜色
typeBottomNavigationBarType.fixed BottomNavigationBarType.shifting
import 'package:flutter/material.dart';
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(home: Tabs());
  }
}
class Tabs extends StatefulWidget {
  const Tabs({super.key});
  @override
  State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
  int _currentIndex = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo'),
      ),
      body: Text('tabBar'),
      bottomNavigationBar: BottomNavigationBar(
          currentIndex: this._currentIndex,
          onTap: (int index) {
            setState(() {
              this._currentIndex = index;
            });
          },
          items: [
            BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
            BottomNavigationBarItem(icon: Icon(Icons.category), label: '分类'),
            BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置')
          ]),
    );
  }
}

实现导航组件

// Tabs.dart
import 'package:flutter/material.dart';
// 引入
import 'tabs/Category.dart';
import 'tabs/Home.dart';
import 'tabs/Setting.dart';
​
class Tabs extends StatefulWidget {
  const Tabs({super.key});
​
  @override
  State<Tabs> createState() => _TabsState();
}
​
class _TabsState extends State<Tabs> {
  int _currentIndex = 0;
  List _pageList = [
    HomePage(),
    CategoryPage(),
    SettingPage()
  ];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Demo'),
      ),
      body: this._pageList[this._currentIndex],
      bottomNavigationBar: BottomNavigationBar(
          currentIndex: this._currentIndex,
          onTap: (int index) {
            setState(() {
              this._currentIndex = index;
            });
          },
          items: [
            BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
            BottomNavigationBarItem(icon: Icon(Icons.category), label: '分类'),
            BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置')
          ]),
    );
  }
}
// main.dart
import 'package:flutter/material.dart';
import 'pages/Tabs.dart';
​
void main() {
  runApp(MyApp());
}
​
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(home: Tabs());
  }
}