Skip to content

Flutter 组件

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

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

MaterialApp

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

常用属性

属性含义
home主页
title标题
theme主题
Routes路由

去掉 debug 图标

dart
 @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
    );
  }

Scaffold 布局组件

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

常用属性

属性含义
appBar显示在界面顶部的一个 AppBar
body当前界面所显示的主要内容 Widget
drawer抽屉菜单控件
BottomNavigationBar底部导航条
FloatingActionButtonFloatingActionButton 简称 FAB ,可以实现浮动按钮,也可以实现类似闲鱼 app 的底部凸起导航

BottomNavigationBar

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

属性名说明
itemsList 底部导航条按钮集合
iconSizeicon
currentIndex默认选中第几个
onTap选中变化回调函数
fixedColor选中的颜色
typeBottomNavigationBarType.fixed BottomNavigationBarType.shifting

底部菜单选中

dart

FloatingActionButton

FloatingActionButton 简称 FAB ,可以实现浮动按钮,也可以实现类似闲鱼 app 的底部凸起导航

属性名称属性值
child子视图,一般为 Icon,不推荐使用文字
tooltipFAB 被长按时显示,也是无障碍功能
backgroundColor背景颜色
elevation未点击的时候的阴影
hignlightElevation点击时阴影值,默认 12.0
onPressed点击事件回调
shape可以定义 FAB 的形状等
mini是否是 mini 类型默认 false

Drawer 抽屉菜单

在 Scaffold 组件里面传入 drawer 参数可以定义左侧边栏,传入 endDrawer 可以定义右侧边栏。侧边栏默认是隐藏的,我们可以通过手指滑动显示侧边栏,也可以通过点击按钮显示侧边栏。

DrawerHeader

常见属性

属性描述
decoration设置顶部背景颜色
child配置子元素
padding内边距
margin外边距

UserAccountsDrawerHeader

属性描述
decoration设置顶部背景颜色
accountName账户名称
accountEmail账户邮箱
currentAccountPicture用户头像
otherAccountsPictures用来设置当前账户其他账户头像
margin

AppBar

AppBar 自定义顶部按钮图标、颜色

属性描述
leading在标题前面显示的一个控件,在首页通常显示应用的 logo;在其他界面通常显示为返回按钮
title标题,通常显示为当前界面的标题文字,可以放组件
actions通常使用 IconButton 来表示,可以放按钮组
bottom通常放 tabBar,标题下面显示一个 Tab 导航栏
backgroundColor导航背景颜色
iconTheme图标样式
centerTitle标题是否居中显示

TabBar

属性描述
tabs显示的标签内容,一般使用 Tab 对象,也可以是其他的 Widget
controllerTabController 对象
isScrollable是否可滚动
indicatorColor指示器颜色
indicatorWeight指示器高度
indicatorPadding底部指示器的 Padding
indicator指示器 decoration,例如边框等
indicatorSize指示器大小计算方式,TabBarIndicatorSize.label 跟文字等宽,TabBarIndicatorSize.tab 跟每个 tab 等宽
labelColor选中 label 颜色
labelStyle选中 label 的 Style
labelPadding每个 label 的 padding 值
unselectedLabelColor未选中 label 颜色
unselectedLabelStyle未选中 label 的 Style

AppBar 结合 TabBar 实现顶部 Tab 切换

dart

Container 容器组件

名称功能
alignmenttopCenter:顶部居中对齐 topLeft:顶部左对齐 topRight:顶部右对齐 center:水平垂直居中对齐 centerLeft:垂直居中水平居左对齐 centerRight:垂直居中水平居右对齐 bottomCenter 底部居中对齐 bottomLeft:底部居左对齐 bottomRight:底部居右对齐
decorationdecoration: BoxDecoration( color: Colors.blue, border: Border.all( color: Colors.red, width: 2.0), borderRadius:BorderRadius.circular((8)),// 圆角 , boxShadow: [ BoxShadow( color: Colors.blue, offset: Offset(2.0, 2.0), blurRadius: 10.0, ) ], ) //LinearGradient 背景线性渐变 RadialGradient 径向渐变 gradient: LinearGradient( colors: [Colors.red, Colors.orange], ),
marginmargin 属性是表示 Container 与外部其他组件的距离。 EdgeInsets.all(20.0),
paddingpadding 就是 Container 的内边距,指 Container 边缘与 Child 之间的距离 padding:EdgeInsets.all(10.0)
transform让 Container 容易进行一些旋转之类的 transform: Matrix4.rotationZ(0.2)
height容器高度
width容器宽度
child容器子元素
dart
import 'dart:ffi';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
          appBar: AppBar(
            title: const Text('Flutter Demo'),
          ),
          body: Center(
            child: Container(
              alignment: Alignment.center,
              padding: const EdgeInsets.all(10),
              transform: Matrix4.rotationZ(0.1),
              width: 300,box
              height: 300,
              decoration: BoxDecoration(
                  color: Colors.red[100],
                  border: Border.all(color: Colors.black45, width: 10),
                  borderRadius: BorderRadius.circular(20),
                  boxShadow: const [
                    BoxShadow(color: Colors.blue, offset: Offset(2, 2)),
                  ],
                  gradient: const RadialGradient(
                      colors: [Colors.pink, Colors.orange])),
              child: const Text(
                'Container使用',
                style: TextStyle(
                    color: Colors.white,
                    fontSize: 30,
                    fontWeight: FontWeight.w900),
              ),
            ),
          )),
    );
  }
}

Text 组件详解

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

TextStyle 的参数

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

Image 图片组件

Flutter 中,我们可以通过 Image 组件来加载并显示图片 Image 的数据源可以是 asset、文件、内存以及网络

  • Image.asset, 本地图片
  • Image.network 远程图片

Image 组件的常用属性

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

加载远程图片

dart

Container 实现圆形图片

ClipOval 实现圆形图片

CircleAvatar 实现圆形图片

加载本地图片

CircleAvatar

dart
import 'package:flutter/material.dart';

class User extends StatelessWidget {
  const User({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: const [
        Text('个人中心'),
        CircleAvatar(
          radius: 200,
          backgroundImage: NetworkImage('https://doc.ronhai.com/bg.png'),
        )
      ],
    );
  }
}

基本上,CircleAvatar 不提供设置边框的属性。但是,可以将其包裹在具有更大半径和不同背景颜色的不同 CircleAvatar 中,以创建类似于边框的内容。

dart

图标组件

使用 Flutter 官方 Icons 图标

Material Design 所有图标可以在其官网查看:https://material.io/tools/icons/

阿里巴巴图标库

Flutter 中借助阿里巴巴图标库自定义字体图标

我们也可以使用自定义字体图标。阿里巴巴图标库官网 iconfont.cn 上有很多字体图标素材,我们可以选择自己需要的图标打包下载后,会生成一些不同格式的字体文件,在 Flutter 中,我们使用 ttf 格式即可。

假设我们项目中需要使用一套图标,我们打包下载后导入

image-20240716103611671

image-20240716103651884

也可以配置多个字体文件

dart
  fonts:
    - family: iconfont
      fonts:
        - asset: assets/fonts/iconfont.ttf
   - family: myicon
      fonts:
        - asset: assets/fonts/myicon.ttf

为了使用方便,我们定义一个 MyIcons 类,功能和 Icons 类一样:将字体文件中的所有图标都定义成静态变量

dart
import 'package:flutter/material.dart';

class IconFont {
  static const IconData view = IconData(
    0xe633,
    fontFamily: 'iconfont',
    matchTextDirection: true,
  );
  static const IconData message = IconData(
    0xe635,
    fontFamily: 'iconfont',
    matchTextDirection: true,
  );
}

使用

dart
import 'package:flutter/material.dart';
import 'common/iconfont.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
          appBar: AppBar(
            title: const Text('Flutter Demo'),
          ),
          body: Center(
            child: Container(
                alignment: Alignment.center,
                width: 300,
                height: 300,
                child: const Column(
                  children: [
                    Icon(
                      IconFont.view,
                      size: 30,
                      color: Colors.amber,
                    ),
                    Icon(
                      IconFont.message,
                      size: 30,
                      color: Colors.amber,
                    )
                  ],
                )),
          )),
    );
  }
}

image-20240716103716547

脚本生成 iconfont.dart 文件

新建generate_icons.dart文件放入 common 文件夹中,增加以下内容:

使用Code Runner运行脚本即可

dart
import 'dart:convert';
import 'dart:io';

String toCamelCase(String name) {
  List<String> parts = name.split(RegExp(r'[-_]'));
  String camelCaseName = parts[0];
  for (int i = 1; i < parts.length; i++) {
    camelCaseName += parts[i][0].toUpperCase() + parts[i].substring(1);
  }
  return camelCaseName;
}

void generateIconFontClass(String jsonFilePath, String outputFilePath) {
  File jsonFile = File(jsonFilePath);
  if (!jsonFile.existsSync()) {
    print('JSON file not found: $jsonFilePath');
    return;
  }

  String jsonString = jsonFile.readAsStringSync();
  Map<String, dynamic> jsonData = jsonDecode(jsonString);

  StringBuffer buffer = StringBuffer();
  buffer.writeln("import 'package:flutter/material.dart';\n");
  buffer.writeln("class IconFont {");

  if (jsonData.containsKey('glyphs')) {
    List<dynamic> glyphs = jsonData['glyphs'];
    glyphs.forEach((glyph) {
      if (glyph.containsKey('unicode_decimal') && glyph.containsKey('name')) {
        String name = glyph['name'];
        int unicode = glyph['unicode_decimal'];
        // String fontClass = glyph['font_class'];

        String fieldName = toCamelCase(name);

        buffer.writeln("  static const IconData $fieldName = IconData(");
        buffer.writeln("    0x${unicode.toRadixString(16)},");
        buffer.writeln("    fontFamily: 'iconfont',");
        buffer.writeln("    matchTextDirection: true,");
        buffer.writeln("  );");
      }
    });
  }

  buffer.writeln("}");

  // Write generated code to output file
  File outputFile = File(outputFilePath);
  outputFile.writeAsStringSync(buffer.toString());

  print('IconFont class generated successfully at: $outputFilePath');
}

void main() {
  String jsonFilePath = 'assets/fonts/iconfont.json';
  String outputFilePath = 'lib/common/iconfont.dart';
  generateIconFontClass(jsonFilePath, outputFilePath);
}

引用

dart
 const Icon(
   IconFont.view,
   color: Colors.red,
   size: 30,
 )

ListView 列表组件

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

  • 垂直列表
  • 垂直图文列表
  • 水平列表
  • 动态列表

列表组件常用参数

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

Divider 横线, ListTile 一行内容

垂直列表

dart

垂直图文列表

dart

水平列表

可以左右滑动

动态列表

GridView 网格布局组件

GridView 网格布局在实际项目中用的也是非常多的,当我们想让可以滚动的元素使用矩阵方式排列的时候。此时我们可以用网格列表组件 GridView 实现布局。

GridView 创建网格列表主要有下面三种方式

  • 可以通过 GridView.count 实现网格布局
  • 可以通过 GridView.extent 实现网格布局
  • 通过 GridView.builder 实现动态网格布局

常用属性

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

实现网格布局

GridView.count 构造函数内部使用了 SliverGridDelegateWithFixedCrossAxisCount,我们通过它可以快速的创建横轴固定数量子元素的 GridView

实现网格布局

GridView.extent 构造函数内部使用了 SliverGridDelegateWithMaxCrossAxisExtent,我们通过它可以快速的创建横轴子元素为固定最大长度的的 GridView。

Paddiing 组件

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

属性说明
paddingpadding 值, EdgeInsetss 设置填充的值
child子组件

Row 和 Column 线性布局

Row 水平布局组件

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

Column 垂直布局组件

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

double

double.infinity 和 double.maxFinite 可以让当前元素的 width 或者 height 达到父元素的尺寸

Flex Expanded 弹性布局

Flex 组件可以沿着水平或垂直方向排列子组件,如果你知道主轴方向,使用 Row 或 Column 会方便一些,因为 Row Column 都继承自 Flex ,参数基本相同,所以能使用 Flex 的地方基本上都可以使用 Row 或 Column 。 Flex 本身功能是很强大的,它也可以和 Expanded 组件配合实现弹性布局 。

水平弹性布局

dart

垂直弹性布局

dart

使用Row Column 结合 Expanded 实现下面示例

dart

Stack、Align、Positioned 层叠布局

Stack 组件

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

属性说明
alignment配置所有子元素的显示位置
children子组件

Stack Align

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

属性说明
alignment配置所有子元素的显示位置
child子组件

Stack Positioned

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

属性说明
top子元素距离顶部的距离
bottom子元素距离底部的距离
left子元素距离左侧距离
right子元素距离右侧距离
child子组件
width组件的高度 (注意:宽度和高度必须是固定值,没法使用 double.infinity)
height子组件的高度

MediaQuery 获取屏幕宽度和高度

需要在 build 中使用

dart
final size=MediaQuery.of(context).size;

Stack Positioned 固定导航案例

AspectRatio

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

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

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

属性说明
aspectRatio宽高比,最终可能不会根据这个值去布局,具体则要看综合因素,外层是否允许按照这种比率进行布局,这只是一个参考值
child子组件

Card 组件

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

属性说明
margin外边距
child子组件
elevation阴影值的深度
color背景颜色
shadowColor阴影颜色
margin外边距
clipBehaviorclipBehavior 内容溢出的剪切方式 Clip.none 不剪切 Clip.hardEdge 裁剪但不应用抗锯齿 Clip.antiAlias 裁剪而且抗锯齿 Clip.antiAliasWithSaveLayer 带有抗锯齿的剪辑,并在剪辑之后立即保存 saveLayer
ShapeCard 的阴影效果,默认的阴影效果为圆角的长方形边。 shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(10)) ),

通讯录列表

dart

图文列表

dart

CircleAvatar 实现一个圆形图片

按钮组件

常用属性

属性说明
onPressed必填参数,按下按钮时触发的回调,接收一个方法,传 null 表示按钮禁用,会显示禁用相关样式
child子组件
style通过 ButtonStyle 装饰

ButtonStylee 组件属性

常用的参数

属性名称值类型属性值
foregroundColorColor文本颜色
backgroundColorColor按钮的颜色
shadowColorColor阴影颜色
elevationdouble阴影的范围,值越大阴影范围越大
padding内边距
shape设置按钮的形状 shape: MaterialStateProperty.all( RoundedRectangleBorder( borderRadius: BorderRadius.circular(10)) )
side设置边框MaterialStateProperty.all(BorderSide(width:1,color: Colors.red))

ElevatedButton

ElevatedButton 即"凸起"按钮,它默认带有阴影和灰色背景。按下后,阴影会变大

TextButton

TextButton 即文本按钮,默认背景透明并不带阴影。按下后,会有背景色

OutlinedButton

OutlineButton 默认有一个边框,不带阴影且背景透明。按下后,边框颜色会变亮、同时出现背景和阴影

dart
Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: [
    ElevatedButton(onPressed: () {}, child: const Text('普通按钮')),
    TextButton(onPressed: () {}, child: const Text('文本按钮')),
    OutlinedButton(onPressed: () {}, child: const Text('边框按钮')),
]),

IconButton

IconButton 是一个可点击的 Icon,不包括文字,默认没有背景,点击后会出现背景

带图标的按钮

ElevatedButton 、 TextButton 、 OutlineButton 都有一个 icon 构造函数,通过它可以轻松创建带图标的按钮

dart
Row(
    mainAxisAlignment: MainAxisAlignment.start,
    children: [
        IconButton(
            onPressed: () {}, icon: const Icon(Icons.theater_comedy)),
        ElevatedButton.icon(
            onPressed: () {},
            label: const Text('图标按钮'),
            icon: const Icon(
                Icons.send,
                color: Colors.red,
            ),
        ),
        OutlinedButton.icon(
            onPressed: () {},
            label: const Text('边框带图标'),
            icon: const Icon(Icons.access_time_filled),
        )
    ],
)

设置按钮颜色和背景色

dart
ElevatedButton.icon(
    onPressed: () {},
    label: const Text('边框带图标'),
    icon: const Icon(Icons.access_time_filled),
    style: ButtonStyle(
        backgroundColor: WidgetStateProperty.all(Colors.pink),
        foregroundColor: WidgetStateProperty.all(Colors.white),
    ),
),
//side改变边框颜色,width边框粗细
 OutlinedButton.icon(
              onPressed: () {},
              label: const Text('边框带图标'),
              icon: const Icon(Icons.access_time_filled),
              style: ButtonStyle(
                backgroundColor: WidgetStateProperty.all(Colors.pink),
                foregroundColor: WidgetStateProperty.all(Colors.white),
                side: WidgetStateProperty.all(
                  const BorderSide(color: Colors.blue, width: 2.0),
                ),
              ),
            ),

设置按钮宽度高度

自适应按钮

圆形圆角按钮

OutlinedButton 边框

Wrap 组件

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

常用属性

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

preferredSize 组件

PreferredSize 可以改变 appBar 的高度

KeepAliveWrapper 缓存页面

AutomaticKeepAliveClientMixin 可以快速的实现页面缓存功能,但是通过混入的方式实现不是很优雅, 所以我们有必要对 AutomaticKeepAliveClientMixin 混入进行封装

监听 TabController 改变事件

dart

Dialog

AlertDialog

SimpleDialog SimpleDialogOption

showModalBottomSheet

Toast

fluttertoast 的使用:https://pub.dev/packages/fluttertoast

ftoast 的使用

支持 LINUX MACOS WEB WINDOWS 的另一个插件https://pub.dev/packages/ftoast

PageView

Flutter 中的轮动图以及抖音上下滑页切换视频功能等等,这些都可以通过 PageView 轻松实现

PageView 常见属性

属性描述
scrollDirectionAxis.horizonta 水平方向 Axis.vertical 锤子方向
children配置子元素
allowImplicitScrolling缓存当前页面的前后两页
onPageChangedpage 改变的时候触发

Widget

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

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

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

通俗的讲:如果我们想改变页面中的数据的话这个时候就需要用到 StatefulWidget

实现一个计数器的功能

dart

实现一个动态列表

dart

路由

Flutter 中的路由通俗的讲就是页面跳转。在 Flutter 中通过 Navigator 组件管理路由导航。并提供了管理堆栈的方法。如:Navigator.push 和 Navigator.pop

**Flutter****中给我们提供了两种配置路由跳转的方式**:1、基本路由 2、命名路由

普通路由

比如我们现在想从 HomePage 组件跳转到 SearchPage 组件。

需要在 HomPage 中引入 SearchPage.dart

dart

在 HomePage 中通过下面方法跳转

dart

普通路由跳转传值

跳转传值和调用组件传值的实现方法是一样的

命名路由

命名路由传值

替换路由

比如我们从用户中心页面跳转到了 registerFirst 页面,然后从 registerFirst 页面通过 pushReplacementNamed 跳转到了 registerSecond 页面。这个时候当我们点击 registerSecond 的返回按钮的时候它会直接返回到用户中心。

dart

返回到根路由

比如我们从用户中心跳转到 registerFirst 页面,然后从 registerFirst 页面跳转到 registerSecond 页面,然后从 registerSecond 跳转到了 registerThird 页面。这个时候我们想的是 registerThird 注册成功后返回到用户中心。 这个时候就用到了返回到根路由的方法。

全局配置主题

dart

Key 详解

我们平时一定接触过很多的 Widget,比如 Container、Row、Column 等,它们在我们绘制界面的过程中发挥着重要的作用。但是不知道你有没有注意到,在几乎每个 Widget 的构造函数中,都有一个共同的参数,它们通常在参数列表的第一个,那就是 Key。

在 Flutter 中,Key 是不能重复使用的,所以 Key 一般用来做唯一标识。组件在更新的时候,其状态的保存主要是通过判断组件的类型或者key 值是否一致。因此,当各组件的类型不同的时候,类型已经足够用来区分不同的组件了,此时我们可以不必使用 key。但是如果同时存在多个同一类型的控件的时候,此时类型已经无法作为区分的条件了,我们就需要使用到 key

LocalKey、GlobalKeyFlutter key 子类包含 LocalKey 和 GlobalKey 。

  • 局部键(LocalKey):ValueKey、ObjectKey、UniqueKey
  • 全局键(GlobalKey): GlobalKey、GlobalObjectKey

ValueKey (值 key)把一个值作为 key ,UniqueKey(唯一 key)程序生成唯一的 Key,当我们不知道如何指定 ValueKey 的时候就可以使用 UniqueKey,ObjectKey(对象 key)把一个对象实例作为 key。

GlobalKey(全局 key),GlobalObjectKey(全局 Objec key,和 ObjectKey 有点类似)

获取子组件状态

  • globalKey.currentState 可以获取子组件的状态,执行子组件的方法
  • globalKey.currentWidget 可以获取子组件的属
  • _globalKey.currentContext!.findRenderobject()可以获取渲染的属性。

Flutter 动画

动画原理

动画基本原理以及 Flutter 动画简介

在任何系统的 UI 框架中,动画实现的原理都是相同的,即:在一段时间内,快速地多次改变 UI 外观;由于人眼会产生视觉暂留,所以最终看到的就是一个“连续”的动画,这和电影的原理是一样的。我们将 UI 的一次改变称为一个动画帧,对应一次屏幕刷新,而决定动画流畅度的一个重要指标就是帧率 FPS(Frame Per Second),即每秒的动画帧数。很明显,帧率越高则动画就会越流畅!一般情况下,对于人眼来说,动画帧率超过 16 FPS,就基本能看了,超过 32 FPS 就会感觉相对平滑,而超过 32 FPS,大多数人基本上就感受不到差别了。由于动画的每一帧都是要改变 UI 输出,所以在一个时间段内连续的改变 UI 输出是比较耗资源的,对设备的软硬件系统要求都较高,所以在 UI 系统中,动画的平均帧率是重要的性能指标,而在 Flutter 中,理想情况下是可以实现 60FPS 的,这和原生应用能达到的帧率是基本是持平的。

Flutter 动画简介

FLutter 中的动画主要分为:隐式动画、显式动画、自定义隐式动画、自定义显式动画、和 Hero 动画

隐式动画

通过几行代码就可以实现隐式动画,由于隐式动画背后的实现原理和繁琐的操作细节都被隐去了,所以叫隐式动画,FLutter 中提供的 AnimatedContainer、AnimatedPadding、AnimatedPositioned、 AnimatedOpacity、AnimatedDefaultTextStyle、AnimatedSwitcher 都属于隐式动画。隐式动画中可以通过 duration 配置动画时长、可以通过 Curve (曲线)来配置动画过程

AnimatedContainer

AnimatedContainer 的属性和 Container 属性基本是一样的,当 AnimatedContainer 属性改变的时候就会触发动画

AnimatedPadding 以及 curve 属性

Curves 曲线值

曲线名动画过程
linear匀速的
decelerate匀减速
ease开始加速,后面减速
easeIn开始慢,后面快
easeOut开始快,后面慢
easeInOut开始慢,然后加速,最后再减速
更多曲线https://docs.flutter.io/flutter/animation/Curves-class.html 官方文档打不开也可以参考教程目录中提供的 gif 截图

AnimatedPositioned

AnimatedOpacity

AnimatedDefaultTextStyle