Flutter基本组件
官网地址:选择你的开发平台,开始使用 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter
文档地址:第二版序 | 《Flutter实战·第二版》 (flutterchina.club)
Flutter
1、使用MaterialApp 和 Scaffold两个组件装饰App
1.1、MaterialApp
MaterialApp是一个方便的Widget,它封装了应用程序实现Material Design所需要的一些Widget。一
般作为顶层widget使用。
常用的属性:
home(主页)
title(标题) --任务管理器上面看到的名字
color(颜色)
routes(路由)
...
1.2、Scaffold
Scaffold是Material Design布局结构的基本实现。此类提供了用于显示drawer、snackbar和底部sheet
的API。
Scaffold 有下面几个主要属性:
appBar - 显示在界面顶部的一个 AppBar。
body - 当前界面所显示的主要内容 Widget。
drawer - 抽屉菜单控件。
1.3、示例
import 'package:flutter/material.dart';
void main() {
return runApp(MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('首页'),
),
body: const Center(
child: Text(
'this is home',
textDirection: TextDirection.ltr,
style:
TextStyle(color: Color.fromRGBO(255, 145, 123, 1), fontSize: 40),
),
),
),
));
}
2、Flutter把内容单独抽离成一个组件
在Flutter中自定义组件其实就是一个类,这个类需要继承StatelessWidget或StatefulWidget
前期我们都继承StatelessWidget。后期给大家讲StatefulWidget的使用。
StatelessWidget 是无状态组件,状态不可变的widget
StatefulWidget 是有状态组件,持有的状态可能在widget生命周期改变
2.1、新建一个dart文件
VsCode安装Awesome Flutter Snippets插件,输入stless会有快捷生成提示提示
import 'package:flutter/material.dart';//importM快捷引入
//继承无状态组件StatelessWidget 按照提示生成
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
//需要返回构建的内容
return const Center(
child: Text(
'我是一个自定义组件',
textDirection: TextDirection.ltr,
style: TextStyle(color: Color.fromRGBO(255, 145, 123, 1), fontSize: 40),
),
);
}
}
2.2、在main.dart中使用
import 'package:flutter/material.dart';
import 'pages/home//home_page.dart';//引入刚才那个组件
void main() {
return runApp(MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('首页'),
),
body: MyApp(),//使用组件
)));
}
2、基础组件
2.1 、Container容器组件
- 常用属性
名称 | 功能 |
---|---|
alignment | 容器对齐方式 |
decoration | 容器装饰 |
margin,padding,height,width | ... |
transform | 变换 |
child | 容器子元素 |
和div有点相似主要用于布局

import 'package:flutter/material.dart';
void main() {
return runApp(MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('首页')),
body: homePage(),
),
));
}
class homePage extends StatelessWidget {
const homePage({super.key});
Widget build(BuildContext context) {
//返回一个居中组件
return Center(
//容器
child: Container(
alignment: Alignment.center,//内容显示的方位
width: 100,
height: 100,
transform: Matrix4.rotationZ(0.5),//旋转
//设置背景装饰
decoration: const BoxDecoration(
color:Colors.blue,//背景颜色
gradient: LinearGradient(colors: [Colors.red, Colors.orange]), //背景渐变
boxShadow: [
BoxShadow(
color: Colors.blue, offset: Offset(2, 2), blurRadius: 20), //阴影
],
),
//在容器里居中一个文字
child: const Text(
'xxx',
style: TextStyle(color: Colors.amberAccent),
),
));
}
}
2.2、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 | ... |
import 'package:flutter/material.dart';
class MyText extends StatelessWidget {
const MyText({super.key});
Widget build(BuildContext context) {
return Container(
width: 200,
height: 200,
margin: const EdgeInsets.fromLTRB(0, 10, 0, 0),
padding: const EdgeInsets.all(10),
decoration: const BoxDecoration(color: Colors.blueGrey),
child: const Text(
'this is a Text ldawdawdwadadwddhuawiadwuwduduuiuiwauwd',
textAlign: TextAlign.center,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold, //加粗
fontStyle: FontStyle.italic, //倾斜
letterSpacing: 3, //字间距
decoration: TextDecoration.underline, //线条装饰
decorationColor: Colors.red,//线条颜色
decorationStyle: TextDecorationStyle.dashed, //虚线
),
),
);
}
}
2.3、图片组件
常用属性
名称 | 类型 | 说明 |
---|---|---|
alignment | Alignment | 图片的对齐方式 |
color和colorBlendMode | 设置图片的背景颜色,通常和colorBlendMode配合一起使用,这样可以是图片颜色和背景色混合。上面的图片就是进行了颜色的混合,绿色背景和图片红色的混合 | |
fit | BoxFit | BoxFit.fill:全图显示,图片会被拉伸,并充满父容器。 BoxFit.contain:全图显示,显示原比例,可能会有空隙。 BoxFit.cover:显示可能拉伸,可能裁切,充满(图片要充满整个容器,还不变形)。 BoxFit.fitWidth:宽度充满(横向充满),显示可能拉伸,可能裁切 BoxFit.fitHeight :高度充满(竖向充满),显示可能拉伸,可能裁切。 BoxFit.scaleDown:效果和contain差不多,但是此属性不允许显示超过源图片大小,可小不可大。 |
repeat | ImageRepeat.repeat : 横向和纵向都进行重复,直到铺满整个画布。 ImageRepeat.repeatX: 横向重复,纵向不重复。 ImageRepeat.repeatY:纵向重复,横向不重复。 | |
width | 宽度 一般结合ClipOval才能看到效果 | |
height | 高度 一般结合ClipOval才能看到效果 |
2.3.1、本地图片:Image.asset('url')
新建文件夹 名字随便取 设置2.0x和3.0x会自动根据当前手机的dpi匹配对应尺寸 当然也可以不设置2.0x和3.0x
images
home.png
2.0x
home.png
3.0x
home.png
在pubspec.yaml中的assets:下面添加你要引入的图片
assets: # 你也可以设置- images/ #表示加载images目录下的全部 - images/home.png - images/2.0x/home.png - images/3.0x/home.png
使用Image.asset()加载本地图片
Image.asset( 'images/home.png', width: 24, height: 24, );
2.3.2、网络图片:Image.network('url')
import 'package:flutter/material.dart';
class MyImages extends StatelessWidget {
const MyImages({super.key});
Widget build(BuildContext context) {
return Center(
child: Container(
width: 200,
height: 200,
margin: const EdgeInsets.fromLTRB(0, 10, 0, 0),
decoration: const BoxDecoration(color: Colors.yellow),
child: Image.network(
'https://picsum.photos/50/50',
alignment: Alignment.topLeft, //t图片对齐位置
// fit: BoxFit.cover, //图片剪切
repeat: ImageRepeat.repeat, //设置平铺
),
),
);
}
}
2.3.3、圆形图片
- Container实现圆形图片
import 'package:flutter/material.dart';
class MyImages extends StatelessWidget {
const MyImages({super.key});
Widget build(BuildContext context) {
return Center(
child: Container(
width: 200,
height: 200,
margin: const EdgeInsets.fromLTRB(0, 10, 0, 0),
decoration: BoxDecoration(
color: Colors.yellow,
borderRadius: BorderRadius.circular(100), //圆角设置
image: const DecorationImage(
//设置背景图
image: NetworkImage('https://picsum.photos/200/200'),
fit: BoxFit.cover, //填充
)),
//普通图片
/* child: Image.network(
'https://picsum.photos/200/200',
fit: BoxFit.cover,
), */
),
);
}
}
- ClipOval实现圆形图片
import 'package:flutter/material.dart';
class ClipOvalImage extends StatelessWidget {
const ClipOvalImage({super.key});
Widget build(BuildContext context) {
return ClipOval(
child: Image.network(
'https://picsum.photos/100/100?id=1',
width: 100,
height: 100,
fit: BoxFit.cover,
),
);
}
}
- CircleAvatar实现圆形图片
import 'package:flutter/material.dart';
class MyClipOvalImage extends StatelessWidget {
const MyClipOvalImage({super.key});
Widget build(BuildContext context) {
return const CircleAvatar(
radius: 100,
backgroundImage:
NetworkImage("https://www.itying.com/images/flutter/3.png"),
);
}
}
基本上,CircleAvatar 不提供设置边框的属性。但是,可以将其包裹在具有更大半径和不同背景颜色的
不同 CircleAvatar 中,以创建类似于边框的内容。
import 'package:flutter/material.dart';
class MyClipOvalImage extends StatelessWidget {
const MyClipOvalImage({super.key});
Widget build(BuildContext context) {
return const CircleAvatar(
radius: 110,
backgroundColor: Color(0xffFDCF09),//背景色就是边框
child: CircleAvatar(
radius: 100,
backgroundImage:
NetworkImage("https://www.itying.com/images/flutter/3.png"),
));
}
}
2.4、自带图标组件件和自定义图标
2.4.1、系统图标使用Icon.(Icons.home)
import 'package:flutter/material.dart';
class MyIcon extends StatelessWidget {
const MyIcon({super.key});
Widget build(BuildContext context) {
return const Column(
children: [
SizedBox(
height: 20,
),
Icon(
Icons.home,
color: Colors.red,
size: 50,
),
Icon(Icons.person_pin)
],
);
}
}
2.4.2、使用自定义图标
前往阿里巴巴的iconfont
2.4.2.1、下载图标复制iconfont.json和iconfont.ttf到项目
2.4.2.2、在项目根目录新建fonts文件夹后复制刚才的两个文件
2.4.2.3、在pubspec.yaml中找到fonts: 进行配置
fonts:
- family: MyiCON #名字可以自定义
fonts:
- asset: fonts/iconfont.ttf #引入字体文件
# - family: Trajan Pro
# fonts: #也可以引入多个fonts
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
2.4.2.4、为了方便使用 定义一个MyIcons类 功能和Icons类一样
MyIcon.dart
import 'package:flutter/material.dart';
class MyIcon {
// 定义静态属性 直接调用
// 支付宝
static const IconData aliPay =IconData(
0xe634,//可在iconfont.json中查看unicode码 要加上0x
fontFamily: 'MyiCON',//pubspec.yaml中定义的family
matchTextDirection: true//文本方向绘制图表
);
// 微信
static const IconData weixinPay =IconData(
0xe607,
fontFamily: 'MyiCON',
matchTextDirection: true
);
// 蓝牙
static const IconData lanya =IconData(
0xee65f,
fontFamily: 'MyiCON',
matchTextDirection: true
);
}
页面中使用
import 'package:flutter/material.dart';
//引入刚才定义的图标
import 'pages/TestComponents/MyIcon.dart';
void main() {
return runApp(MaterialApp(
theme: ThemeData(primarySwatch: Colors.blue),
home: Scaffold(
appBar: AppBar(
title: const Text('首页'),
), //导航标题
// 主体内容
body: const Column(children: [
Icon(
MyIcon.aliPay,//通过MyIcon.aliPay直接访问
size: 40,
color: Colors.blue,
)
]),
),
));
}
2.5、ListView列表组价
*列表组件常用参数
名称 | 类型 | 说明 |
---|---|---|
scrollDirection | Axis | Axis.horizontal水平列表Axis.vertical垂直列表 |
padding | EdgeInsetsGeometry | 内边距 |
resolve | bool | 组件反向排序 |
children | List | 列表元素 |
2.5.1、垂直列表
由于Column中内容太多 需用使用ListView展示列表

body: ListView(children:const [//我们只需要把Column换成ListView 就可以滚动了
MyApp(),
MyButton(),
MyText(),
MyImages(),
SizedBox(
height: 20,
),
// MyCricularImage(),
MyClipOvalImage(),
SizedBox(
height: 20,
),
Icon(
MyIcon.aliPay,
size: 40,
color: Colors.blue,
)
]),
搭配ListTile()使用
// 定义body
class MyHome extends StatelessWidget {
const MyHome({super.key});
Widget build(BuildContext context) {
return ListView(
children: const <Widget>[
ListTile(
leading: Icon(Icons.home),
title: Text('首页'),
),
Divider(), //虚线
ListTile(
leading: Icon(Icons.person),
title: Text('个人中心'),
),
Divider(),
ListTile(
leading: Icon(Icons.car_rental),
title: Text('订单'),
),
Divider(),
ListTile(
leading: Icon(MyIcon.jntm), //列表左侧的内容
title: Text('鸡哥'),
trailing: Icon(Icons.chevron_right), //列表右侧的内容
),
Divider(),
],
);
}
}
2.5.2、设置水平列表
设置scrollDirection: Axis.horizontal,
注意:ListView垂直布局:Container中是需要设置高度,设置宽度无效
ListView水平布局:Container中是需要设置宽度,设置高度无效 ,但是想要显示出水平列表 需要在listView外面再嵌套一个SizedBox或者Container来设置高度

// 水平列表
class MyListView4 extends StatelessWidget {
const MyListView4({super.key});
Widget build(BuildContext context) {
return SizedBox(
height: 120,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
Container(
width: 120,
decoration: const BoxDecoration(
color: Colors.blue,
),
),
Container(
width: 120,
decoration: const BoxDecoration(
color: Colors.yellow,
),
),
Container(
width: 120,
decoration: const BoxDecoration(
color: Colors.red,
),
),
Container(
width: 120,
decoration: const BoxDecoration(
color: Colors.green,
),
),
Container(
height: 120,
decoration: const BoxDecoration(
color: Colors.black,
),
),
Container(
height: 120,
decoration: const BoxDecoration(
color: Colors.cyan,
),
)
],
));
}
}
2.6、ListView动态列表
2.6.1、基础生成列表
通过循环实现动态列表
// 以下是动态列表
import 'package:flutter/material.dart';
//
class MyListViewTrends extends StatelessWidget {
const MyListViewTrends({super.key});
// 定义一个返回List<Widget>的函数来渲染list列表
List<Widget> _getInitData() {
// 定义一个List<Widget>类型的list数组
List<Widget> list = [];
for (var i = 0; i < 20; i++) {
list.add(ListTile(
title: Text('我是列表---$i'),
));
}
return list;
}
Widget build(BuildContext context) {
return ListView(
// ListView的children需要返回一个List<Widget>来与渲染列表
children: _getInitData(),
);
}
}
使用map更方便
// 模拟网络请求的列表
class RequestTrendsList extends StatelessWidget {
const RequestTrendsList({super.key});
// 从服务器获取数据然后遍历list
List<Widget> getDataList() {
// 使用map渲染
var list = dataList.map((item) {
return ListTile(
leading: Image.network("${item["img"]}"),
title: Text('${item["title"]}'),
subtitle: Text('${item["author"]}'),
);
});
return list.toList(); //需要调用toList()转换为数组
}
Widget build(BuildContext context) {
return ListView(
children: getDataList(),
);
}
}
2.6.1、ListBuilder生成列表
推荐使用ListBuilder的方式
class ListBuilderTrendsList extends StatelessWidget {
const ListBuilderTrendsList({super.key});
Widget build(BuildContext context) {
return ListView.builder(
itemCount: dataList.length, //循环次数
itemBuilder: (context, i) {
//类似js的forEach
// 需要返回构Widget
return ListTile(
leading: Image.network('${dataList[i]["img"]}'),
title: Text('${dataList[i]["title"]}'),
subtitle: Text('${dataList[i]["author"]}'),
);
});
}
}
2.7、GridView网格布局组件
*常用属性
名称 | 类型 | 说明 |
---|---|---|
scrollDirection | Axis | 滚动方法 |
padding | EdgeInsetsGeometry | 内边距 |
resolve | bool | 组件反向排序 |
crossAxisSpacing | double | 水平子Widget之间间距 |
mainAxisSpacing | double | 垂直子Widget之间间 |
crossAxisCount | int 用在GridView.count | 一行的Widget数量 |
maxCrossAxisExtent | double 用在GridView.extent | 横轴子元素的最大长度 |
childAspectRatio | double | 子Widget宽高比例 |
children | [ ] | |
gridDelegate | SliverGridDelegateWithFixedCrossAxisCount SliverGridDelegateWithMaxCrossAxisExtent | 控制布局主要用在GridView.builder里面 |
*实现效果

*以下案例定义一个公共List方法
List<Widget> _getInitData() {
// 定义一个List<Widget>类型的list数组
List<Widget> list = [];
for (var i = 0; i < 36; i++) {
list.add(
Image.network('https://picsum.photos/120?id=${i + 1}'),
);
}
return list;
}
1、通过GridView.count 实现网格布局
// 1、通过GridView.count 实现网格布局
class YGridViewCount extends StatelessWidget {
const YGridViewCount({super.key});
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 3, //一行显示多少个Weight
children: _getInitData(),
);
}
}
2、通过GridView.extent 实现网格布局
// 通过GridView.extent 实现网格布局
class YGridViewExtent extends StatelessWidget {
const YGridViewExtent({super.key});
Widget build(BuildContext context) {
return GridView.extent(
padding: const EdgeInsets.all(20), //内边距
maxCrossAxisExtent: 180, //横轴子元素的最大长度 会根据设置的值,自动计算,排列
crossAxisSpacing: 10, //纵向间距
mainAxisSpacing: 10, //横向间距
childAspectRatio: 1, //改变子元素宽高的比例 默认1.0 正方形
children: _getInitData()); //调用Weight方法
}
}
3、通过GridView.builder实现动态网格布局
SliverGridDelegateWithFixedCrossAxisCount中实现GridView.count的功能
itemBuilder是一个函数 需要返回Widget
// 通过GridView.builder实现动态网格布局
// 构建函数返回Widget
Widget _buildWidget(context, index) {
return Container(
padding: const EdgeInsets.all(5),
alignment: Alignment.center,
decoration: BoxDecoration(border: Border.all(color: Colors.black26)),
child: Column(children: [
Image.network(
'${dataList[index]["img"]}',
),
const SizedBox(
height: 10,
),
Text(
'${dataList[index]["title"]}',
)
]),
);
}
class YGridViewBuilder extends StatelessWidget {
const YGridViewBuilder({super.key});
Widget build(BuildContext context) {
return GridView.builder(
padding: const EdgeInsets.all(10), //内边距
itemCount: dataList.length, //传入循环次数
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, //横轴子元素的最大长度 会根据设置的值,自动计算,排列
crossAxisSpacing: 10, //纵向间距
mainAxisSpacing: 10, //横向间距
childAspectRatio: 0.8, //改变子元素宽高的比例 默认1.0 正方形
),
itemBuilder: _buildWidget, // 注册方法 不需要加上()
); //调用Weight方法
}
}
SliverGridDelegateWithMaxCrossAxisExtent中实现类似GridView.count的功能
//_buildWidget方法不变
class YGridViewBuilder extends StatelessWidget {
const YGridViewBuilder({super.key});
Widget build(BuildContext context) {
return GridView.builder(
padding: const EdgeInsets.all(10), //内边距
itemCount: dataList.length, //传入循环次数
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
crossAxisSpacing: 10, //纵向间距
mainAxisSpacing: 10, //横向间距
childAspectRatio: 0.8, //改变子元素宽高的比例 默认1.0 正方形
maxCrossAxisExtent: 220,//横轴子元素的最大长度
),
itemBuilder: _buildWidget, // 注册方法 不需要加上()
); //调用Weight方法
}
}
2.8、页面布局组件
2.8.1、Padding组件
padding组件功能单一 就是内边距,单纯只需要添加边距 使用Padding比Container的性能消耗少
class YLayoutPadding extends StatelessWidget {
const YLayoutPadding({super.key});
Widget build(BuildContext context) {
return const Padding(
padding: EdgeInsets.all(10),
child: Text('你好'),
);
}
}
2.8.2、线性布局 (Column和Row)
*共同属性
属性 | 类型 | 说明 |
---|---|---|
mainAxisAlignment | MainAxisAlignment | 主轴的排序方式 |
crossAxisAlignment | CrossAxisAlignment | 次轴的排序方式 |
children | 组件子元素 |
2.8.2.1、Column组件(垂直)
// 实现传参控制背景色,icon等
class IconContainer extends StatelessWidget {
Color color;
IconData icon;
IconContainer(this.icon, {Key? key, this.color = Colors.red})
: super(key: key);
Widget build(BuildContext context) {
return Container(
width: 120,
height: 120,
color: color,
alignment: Alignment.center,
child: Icon(
icon,
color: Colors.white,
size: 30,
),
);
}
}
使用
import 'package:flutter/material.dart';
import 'pages/TestComponents/YLayout.dart';
void main() {
return runApp(MaterialApp(
theme: ThemeData(
colorScheme: const ColorScheme.light(primary: Colors.blue),
),
home: Scaffold(
appBar: AppBar(
title: const Text('首页'),
), //导航标题
// 主体内容
body: IconContainer(color: Colors.blue, Icons.home),//传入参数
),
));
}
2.8.2.2、Row组件(水平)
// Row
class IconContainerRow extends StatelessWidget {
const IconContainerRow({super.key});
Widget build(BuildContext context) {
return Container(
width: double.infinity,//也可以设为double.maxFinite 或可以设置非常大 都可以达到效果 99999
height: double.infinity,
decoration: const BoxDecoration(color: Colors.black54),
//Row改成Column则垂直显示
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IconContainerColumn(Icons.home, color: Colors.blue),
IconContainerColumn(Icons.search, color: Colors.green),
IconContainerColumn(Icons.ac_unit)
],
));
}
}
让Container组检撑满屏幕可以把width和高设置非常大的值 或者使用double.infinity以达到撑满全屏幕的效果

2.8.3、Flex弹性布局组件
使用Expanded组件

import 'package:flutter/material.dart';
//引入IconContainerColumn组件
import './YLayout.dart';
// 弹性布局
class ElasticityFlex extends StatelessWidget {
const ElasticityFlex({super.key});
Widget build(BuildContext context) {
return Flex(
direction: Axis.horizontal, //设置水平排列还是垂直方向
children: [
// 左边占1份
Expanded(
flex: 1,
child: IconContainerColumn(Icons.home,
color: Colors.blue), //设置元素宽度是没有效果的
),
// 右边占2份
Expanded(
flex: 2,
child: IconContainerColumn(Icons.search, color: Colors.green),
),
],
);
}
}
右侧固定宽度 左侧自适应
不是使用Expanded包裹组件即可

//其余代码与上方相同
children: [
// 左边占1份
Expanded(
flex: 2,
child: IconContainerColumn(Icons.home,
color: Colors.blue), //设置元素宽度是没有效果的
),
// 右边占2份
IconContainerColumn(Icons.search, color: Colors.green)
],
2.8.4、使用Row 或Column 结合Expanded实现下面示例

import 'package:flutter/material.dart';
class YFlexDome extends StatelessWidget {
const YFlexDome({super.key});
Widget build(BuildContext context) {
return ListView(
children: [
Container(
width: double.infinity,
height: 200,
decoration: const BoxDecoration(
image: DecorationImage(
image: NetworkImage('https://picsum.photos/800?id=1'),
fit: BoxFit.cover)),
),
const SizedBox(
height: 10,
),
Row(
children: [
Expanded(
flex: 2,
child: SizedBox(
height: 180,
child: Image.network(
'https://picsum.photos/800?id=2',
fit: BoxFit.cover,
),
)),
const SizedBox(
width: 10,
),
Expanded(
flex: 1,
child: SizedBox(
height: 180,
child: Column(
children: [
Expanded(
flex: 1,
child: SizedBox(
width: double.infinity,
child: Image.network(
'https://picsum.photos/800?id=3',
fit: BoxFit.cover,
),
)),
const SizedBox(
height: 10,
),
Expanded(
flex: 1,
child: SizedBox(
width: double.infinity,
child: Image.network(
'https://picsum.photos/800?id=4',
fit: BoxFit.cover,
),
))
],
),
),
)
],
),
],
);
}
}
2.9、层叠布局
Stack是堆的意思,你可以理解成H5的定位
2.9.1、Stack组件
属性 | 说明 |
---|---|
alignment | 配置所有子元素的显示位置 |
children | 子组件 |

import 'package:flutter/material.dart';
class YStack extends StatelessWidget {
const YStack({super.key});
Widget build(BuildContext context) {
return Stack(
//alignment: Alignment.center, //设置居中
children: [
Container(
width: 400,
height: 300,
color: Colors.red,
),
Container(
width: 200,
height: 200,
color: Colors.yellow,
),
const Text('你好'),
const Text('wdawda'),
],
);
}
}
2.9.2 、Align组件
属性 | 说明 |
---|---|
alignment | 配置所有子元素的显示位置 |
children | 子组件 |
Align结合Container

// Align组件
class YAlign extends StatelessWidget {
const YAlign({super.key});
Widget build(BuildContext context) {
return Container(
width: 300,
height: 300,
color: Colors.red,
child: const Align(
alignment: Alignment(1, 0), //可以使用方位 也可以使用Alignment.center ....
child: Text('123'),
),
);
}
}
2.9.3 、Positioned组件
在Stack中的children下使用Positioned设置位置
Stack相对于外部容器进行定位,如果没有外部容器就相对于整个屏幕进行定位

import 'package:flutter/material.dart';
class YStack extends StatelessWidget {
const YStack({super.key});
Widget build(BuildContext context) {
return Container(
width: double.infinity,
height: 300,
color: Colors.red,
child: Stack(//相对于外部日期进行定位,如果没有外部容器就相对于整个屏幕进行定位
children: [
// 使用Positioned包裹后设置位置
Positioned(
left: 10,
bottom: 10,
child: Container(width: 100, height: 100, color: Colors.yellow),
),
const Positioned(right: 0, top: 150, child: Text('你好'))
],
),
);
}
}
2.9.4 、MediaQuery获取屏幕宽度和高度
final size =MediaQuery.of(context).size;
组件的build方法中可以通过,=MediaQuery.of(context).size;
Widget build(BuildContext context) {
final size =MediaQuery.of(context).size;
final width =size.width;
final height =size.height;
}
2.9.5、Stack Positioned 固定导航案例

import 'package:flutter/material.dart';
// 顶部固定导航
class YFloatNav extends StatelessWidget {
const YFloatNav({super.key});
List<Widget> _getList() {
List<Widget> list = [];
for (var i = 0; i < 20; i++) {
list.add(ListTile(title: Text('我是列表---${i + 1}')));
}
return list;
}
Widget build(BuildContext context) {
// 获取设备的宽高
final size = MediaQuery.of(context).size;
return Stack(
children: [
ListView(
padding: const EdgeInsets.only(top: 50), //解决列表1被挡住
children: _getList()),
Positioned(
width:
size.width, //Positioned中无法设置double.infinity 使用MediaQuery获取页面宽度
height: 44, //需要配置宽高
left: 0,
top: 0,
child: Container(
alignment: Alignment.center, //设置居中
height: 44,
color: Colors.red,
child: const Text(
'二级导航',
style: TextStyle(color: Colors.white),
),
),
)
],
);
}
}
2.10、AspectRatio
AspectRatio的作用是根据设置调整子元素child的宽高比。
属性 | 说明 |
---|---|
aspectRatio | 宽高比,最终可能不会根据这个值去布局,具体则要看综合因素,外层是否允许按照这种比率进行布局,这只是一个参考值 |
child | 子组件 |
import 'package:flutter/material.dart';
// AspectRatio调节宽高比
class YAspectRatio extends StatelessWidget {
const YAspectRatio({super.key});
Widget build(BuildContext context) {
// 页面上显示一个容器 宽度为屏幕的的宽度 高度为容器宽度的一半
return AspectRatio(
aspectRatio: 2 / 1, //宽高2/1
child: Container(
color: Colors.red,
),
);
}
2.11、Card组件
Card是卡片组件块,内容可以由大多数类型的Widget构成,Card具有圆角和阴影,这让它看起来有立体感。
属性 | 说明 |
---|---|
margin外边距 | |
child | 子组件 |
elevation | 阴影值的深度 |
color | 背景颜色 |
shadowColor | 阴影颜色 |
clipBehavior | clipBehavior 内容溢出的剪切方式 : Clip.none不剪切 Clip.hardEdge裁剪但不应用抗锯齿 Clip.antiAlias裁剪而且抗锯齿 Clip.antiAliasWithSaveLayer带有抗锯齿的剪辑,并在剪辑之后立即保存saveLayer |
Shape | Card的阴影效果,默认的阴影效果为圆角的长方形边。 shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(10)) |
使用Card实现以下效果

//引入循环的数据
import 'package:bangda/pages/TestComponents/datas.dart';
// 实现一个卡片图文卡片列表
class YImageTextCard extends StatelessWidget {
const YImageTextCard({super.key});
// 循环生成card
List<Widget> _initCardData() {
var cardList = dataList.map((item) {
return Card(
clipBehavior: Clip.antiAlias, //超出部分剪切掉
margin: const EdgeInsets.all(10),
elevation: 20,
shadowColor: const Color.fromARGB(255, 221, 218, 208),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10), //圆角设置
),
child: Column(
children: [
AspectRatio(
aspectRatio: 16 / 9, //图片的宽高比16/9
child: Image.network(
'${item["img"]}',
fit: BoxFit.cover,
),
),
ListTile(
leading: CircleAvatar(
radius: 25, //配置半径
backgroundImage: NetworkImage('${item["img"]}1'),
),
title: Text('${item['title']}'),
subtitle: Text('${item['author']}'),
)
],
),
);
});
return cardList.toList();
}
Widget build(BuildContext context) {
return ListView(
children: _initCardData(),
);
}
}
2.12、按钮组件
常用属性
属性 | 说明 |
---|---|
onPressed | 必填参数,按下按钮时触发的回调,接收一个方法,传null表示按钮禁用,会显示禁用相关样式 |
style | 通过ButtonStyle装饰 |
child | 子组件 |
- ButtonStylee里面的常用的参数
属性 | 值类型 | 说明 |
---|---|---|
foregroundColor | Color | 文本颜色 |
backgroundColor | Color | 按钮的颜色 |
shadowColor | Color | 阴影颜色 |
elevation | double | 阴影的范围,值越大阴影范围越大 |
padding | 内边距 | |
shape | 设置按钮的形状 shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(10))) | |
side | 设置边框 | MaterialStateProperty.all(BorderSide(width:1,color:Colors.red)) |
按钮示例

import 'package:flutter/material.dart';
class YButtons extends StatelessWidget {
const YButtons({super.key});
Widget build(BuildContext context) {
return ListView(
padding: const EdgeInsets.all(10),
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ElevatedButton按钮
const Text('ElevatedButton'),
const SizedBox(
height: 10,
),
Row(
children: [
ElevatedButton(onPressed: () {}, child: const Text('凸起按钮')),
],
),
const SizedBox(
height: 10,
),
// 文本按钮
const Text('TextButton'),
TextButton(
child: const Text("文本按钮"),
onPressed: () {},
),
const SizedBox(
height: 10,
),
// 边框按钮
const Text('OutlinedButton'),
OutlinedButton(
child: const Text("边框按钮"),
onPressed: () {},
),
const SizedBox(
height: 10,
),
// IconButton图标按钮
const Text('IconButton'),
IconButton(
icon: const Icon(Icons.thumb_up),
onPressed: () {},
),
const SizedBox(
height: 10,
),
// 带图标的按钮
const Text('带图标的按钮'),
Row(
children: [
ElevatedButton.icon(
icon: const Icon(Icons.send),
label: const Text("发送"),
onPressed: () {},
),
const SizedBox(
width: 5,
),
OutlinedButton.icon(
icon: const Icon(Icons.add),
label: const Text("添加"),
onPressed: () {},
),
const SizedBox(
width: 5,
),
TextButton.icon(
icon: const Icon(Icons.info),
label: const Text("详情"),
onPressed: () {},
),
],
),
const SizedBox(
height: 10,
),
// 设置按钮宽高和圆角
const Text('设置按钮宽高和圆角'),
SizedBox(
height: 80,
width: 200,
child: ElevatedButton(
style: ButtonStyle(
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10))), //设置圆角
backgroundColor:
MaterialStateProperty.all(Colors.red), //按钮背景色
foregroundColor:
MaterialStateProperty.all(Colors.black)), //按钮的文字颜色
onPressed: () {},
child: const Text('宽度高度'),
),
),
const SizedBox(
height: 10,
),
// 自适应按钮
const Text('自适应按钮'),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: Container(
height: 60,
margin: const EdgeInsets.all(10),
child: ElevatedButton(
child: const Text('自适应按钮'),
onPressed: () {
print("自适应按钮");
},
),
),
)
],
),
const SizedBox(
height: 10,
),
// 圆形按钮
const Text('圆形按钮'),
SizedBox(
height: 80,
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.blue),
foregroundColor: MaterialStateProperty.all(Colors.white),
elevation: MaterialStateProperty.all(20),
shape: MaterialStateProperty.all(//设置圆形+白色边框
const CircleBorder(
side: BorderSide(color: Colors.white)))),
onPressed: () {},
child: const Text('圆形按钮'),
),
),
const SizedBox(
height: 10,
),
// 修改OutlinedButton边框
const Text('修改OutlinedButton边框'),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: Container(
margin: const EdgeInsets.all(20),
height: 50,
child: OutlinedButton(
style: ButtonStyle(
foregroundColor:
MaterialStateProperty.all(Colors.black),
side: MaterialStateProperty.all(const BorderSide(
width: 1, color: Colors.red))), //边框颜色以及宽度
onPressed: () {},
child: const Text("注册 配置边框")),
),
)
],
)
],
),
],
);
}
}
2.12.1自定义一个按钮组件传入文本和点击事件
import 'package:flutter/material.dart';
// 自定义一个按钮组件
// ignore: must_be_immutable
class YCustomButton extends StatelessWidget {
String text = "按钮"; // 按钮文字
// 事件
Function onPressed; //按钮事件
// 构造函数
YCustomButton(this.text, {Key? key, required this.onPressed})
: super(key: key);
Widget build(BuildContext context) {
return ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.blue),
foregroundColor: MaterialStateProperty.all(Colors.white),
),
onPressed: onPressed(),
child: const Text('第一集'));
}
}
使用
import 'package:flutter/material.dart';
import 'pages/TestComponents/YButtons.dart';//引入刚才的组件
void main() {
return runApp(MaterialApp(
theme: ThemeData(),
home: Scaffold(
appBar: AppBar(
title: const Text('首页'),
), //导航标题
// 主体内容
body: YCustomButton('自定义按钮', onPressed: () {})),
));
}
2.13、Wrap组件
Wrap可以实现流布局,单行的Wrap跟Row表现几乎一致,单列的Wrap则跟Column表现几乎一致。但
Row与Column都是单行单列的,Wrap则突破了这个限制,mainAxis上空间不足时,则向crossAxis上
去扩展显示。
属性 | 说明 |
---|---|
direction | 主轴的方向,默认水平 |
alignment | 主轴的对其方式 |
spacing | 主轴方向上的间距 |
textDirection | 文本方向 |
verticalDirection | 定义了children摆放顺序,默认是down,见Flex相关属性介绍。 |
runAlignment | run的对齐方式。run可以理解为新的行或者列,如果是水平方向布局的话,run可以理解为新的一行 |
runSpacing | run的间距 |
实现一个搜索记录

// 搜索+记录
class YSearchRecord extends StatelessWidget {
const YSearchRecord({super.key});
Widget build(BuildContext context) {
return ListView(
padding: const EdgeInsets.all(10),
children: [
Row(
children: [
Text(
'热搜',
style: Theme.of(context).textTheme.titleLarge,
),
],
),
const Divider(),
Wrap(
spacing: 10,
runSpacing: 10,
children: [
YCustomButton(
'女装',
onPressed: () {},
),
YCustomButton(
'男装',
onPressed: () {},
),
YCustomButton(
'笔记本',
onPressed: () {},
),
YCustomButton(
'玩具',
onPressed: () {},
),
YCustomButton(
'文学',
onPressed: () {},
),
YCustomButton(
'时尚',
onPressed: () {},
),
YCustomButton(
'电脑',
onPressed: () {},
),
YCustomButton(
'手机',
onPressed: () {},
),
YCustomButton(
'数码',
onPressed: () {},
),
YCustomButton(
'宠物',
onPressed: () {},
),
],
),
const SizedBox(
height: 10,
),
Row(
children: [
Text(
'历史记录',
style: Theme.of(context).textTheme.titleLarge,
),
],
),
const Divider(),
const Column(
children: [
ListTile(
title: Text('女装'),
),
Divider(),
ListTile(
title: Text('男装'),
),
Divider(),
ListTile(
title: Text('手机'),
),
Divider(),
],
),
const SizedBox(
height: 40,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
OutlinedButton.icon(
style: ButtonStyle(
foregroundColor: MaterialStateProperty.all(Colors.black54)),
onPressed: () {},
icon: const Icon(Icons.delete),
label: const Text('清空历史记录'))
],
)
],
);
}
}
3.StatefulWidget有状态组件
在Flutter中自定义组件其实就是一个类,这个类需要继承StatelessWidget/StatefulWidget。
StatelessWidget是无状态组件,状态不可变的widget
StatefulWidget是有状态组件,持有的状态可能在widget生命周期改变。
通俗的讲:如果我们想改变页面中的数据的话这个时候就需要用到StatefulWidget
下载插件后 statefw 快速生成有状态组件
3.1、使用StatefulWidget实现一个计数器的功能

import 'package:flutter/material.dart';
// 使用有状态组件实现计数器
class SfHomePage extends StatefulWidget {
const SfHomePage({super.key});
State<SfHomePage> createState() => _SfHomePageState();
}
class _SfHomePageState extends State<SfHomePage> {
int countNum = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Flutter App')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'$countNum',
style: Theme.of(context).textTheme.headlineLarge,
),
const SizedBox(
height: 100,
),
ElevatedButton(
onPressed: () {
setState(() {
countNum++;
});
print(countNum);
},
child: const Text('点击'))
],
),
),
// Scaffold下可设置浮动按钮
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
countNum++;
});
print(countNum);
},
child: const Icon(Icons.add),
),
);
}
}
3.2、使用StatefulWidget实现一个动态列表功能

class YDynamicList extends StatefulWidget {
const YDynamicList({super.key});
State<YDynamicList> createState() => _YDynamicListState();
}
class _YDynamicListState extends State<YDynamicList> {
List<String> list = [];
int count = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('动态列表')),
body: ListView(
children: [
Column(
children: list.map((item) {
return ListTile(
title: Text(item),
);
}).toList(),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
onPressed: () {
setState(() {
list.removeLast();
if (list.isEmpty) {
count = 0;
}
});
},
child: const Text('删除数据')),
ElevatedButton(
onPressed: () {
setState(() {
count++;
list.add("新数据$count");
});
},
child: const Text('新增数据')),
],
),
],
));
}
}
4.BottonmNavigationBar自定义底部导航条
常用属性
属性 | 说明 |
---|---|
items | List 底部导航条按钮集合 |
iconSize | icon |
currentIndex | 默认选中第几个 |
onTap | 选中变化回调函数 |
fixedColor | 选中的颜色 |
type | BottomNavigationBarType.fixed BottomNavigationBarType.shifting,如果tab超过三个需要配置 |
在items中定义每一项

4.1、自定义底部导航+点击切换状态
import 'package:flutter/material.dart';
class YBottonmNavigationBar extends StatefulWidget {
const YBottonmNavigationBar({super.key});
State<YBottonmNavigationBar> createState() => _YBottonmNavigationBarState();
}
class _YBottonmNavigationBarState extends State<YBottonmNavigationBar> {
int tabIndex = 0;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('自定义底部')),
body: const Text('自定义底部菜单'),
bottomNavigationBar: BottomNavigationBar(
currentIndex: tabIndex,
onTap: (e) {
setState(() {
tabIndex = e;
});
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: '首页',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: '设置',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: '我的',
)
],
),
);
}
}
4.2、自定义底部导航实现页面切换

import 'package:flutter/material.dart';
// 引入tabbar对应页面
import '../home/home_page.dart';
import '../user/user_page.dart';
import '../order/order_page.dart';
// 自定义底部导航
class TabBars extends StatefulWidget {
const TabBars({super.key});
State<TabBars> createState() => _TabBarsState();
}
class _TabBarsState extends State<TabBars> {
// 所有tabbar列表
final List<Widget> _pageList = const [
HomePage(),
OrderPage(),
UserPage(),
];
int _currentTabs = 0; // 当前激活
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('自定义底部')),
body: _pageList[_currentTabs], //激活index对应的_pageList
bottomNavigationBar: BottomNavigationBar(
iconSize: 24, //底部菜单的大小默认24
fixedColor: Colors.red, //选中的颜色
currentIndex: _currentTabs, //当前激活index
// type: BottomNavigationBarType.fixed, //如果超过三个tab需要设置为fixed来固定
onTap: (e) {
setState(() {
_currentTabs = e; //重新设置激活的index,重新build
});
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: '首页',
),
BottomNavigationBarItem(
icon: Icon(Icons.article),
label: '订单',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: '我的',
),
],
),
);
}
}
5、通过FloatingActionButton实现tabbar凸起按钮
常用属性
属性 | 说明 |
---|---|
child | 子视图,一般为Icon,不推荐使用文字 |
tooltip | FAB被长按时显示,也是无障碍功能 |
backgroundColor | 背景颜色 |
elevation | 未点击的时候的阴影 |
hignlightElevation | 点击时阴影值,默认12.0 |
onPressed | 点击事件回调 |
shape | 可以定义FAB的形状等 |
mini | 是否是mini类型默认false |

import 'package:flutter/material.dart';
// 引入tabbar对应页面
import '../home/home_page.dart';
import '../user/user_page.dart';
import '../order/order_page.dart';
// 自定义底部导航
class TabBars extends StatefulWidget {
const TabBars({super.key});
State<TabBars> createState() => _TabBarsState();
}
class _TabBarsState extends State<TabBars> {
// 所有tabbar列表
final List<Widget> _pageList = const [
HomePage(),
OrderPage(),
UserPage(),
];
int _currentTabs = 0; // 当前激活
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('自定义底部')),
body: _pageList[_currentTabs], //激活index对应的_pageList
bottomNavigationBar: BottomNavigationBar(
iconSize: 24, //底部菜单的大小默认24
fixedColor: Colors.red, //选中的颜色
currentIndex: _currentTabs, //当前激活index
// type: BottomNavigationBarType.fixed, //如果超过三个tab需要设置为fixed来固定
onTap: (e) {
setState(() {
_currentTabs = e; //重新设置激活的index,重新build
});
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: '首页',
),
BottomNavigationBarItem(
icon: Icon(Icons.article),
label: '订单',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: '我的',
),
],
),
);
}
}
6、Scaffold属性 抽屉菜单Drawer
在Scaffold组件里面传入drawer参数可以定义左侧边栏,传入endDrawer可以定义右侧边栏。侧边栏默
认是隐藏的,我们可以通过手指滑动显示侧边栏,也可以通过点击按钮显示侧边栏。
return Scaffold(
appBar: AppBar(
title: Text("Flutter App"),
),
drawer: Drawer(
child: Text('左侧边栏'),
),
endDrawer: Drawer(
child: Text('右侧侧边栏'),
),
);
6.1、DrawerHeader
- 常见属性:
属性 | 说明 |
---|---|
decoration | 设置顶部背景颜色 |
child | 配置子元素 |
padding | 内边距 |
margin | 外边距 |

import 'package:flutter/material.dart';
class YDrawer extends StatefulWidget {
const YDrawer({super.key});
State<YDrawer> createState() => _YDrawerState();
}
class _YDrawerState extends State<YDrawer> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Drawer 抽屉')),
drawer: Drawer(
child: Column(
children: [
// DrawerHeader用于定义drawer的头部信息
DrawerHeader(
decoration: const BoxDecoration(
image: DecorationImage(
image: NetworkImage('https://picsum.photos/500'),
fit: BoxFit.cover, //铺满
),
),
//ListView撑满行
child: ListView(
children: const [Text('Drawer的头部')],
)),
//下面的列表
ListTile(
leading: const CircleAvatar(
child: Icon(Icons.person),
),
title: const Text('个人中心'),
onTap: () {},
),
ListTile(
leading: const CircleAvatar(
child: Icon(Icons.settings),
),
title: const Text('系统设置'),
onTap: () {},
),
],
),
),
body: const Center(
child: Text('Drawer 抽屉'),
),
);
}
}
6.2、UserAccountsDrawerHeader
- 常见属性:
属性 | 说明 |
---|---|
decoration | 设置顶部背景颜色 |
accountName | 账户名称 |
accountEmail | 账户邮箱 |
currentAccountPicture | 用户头像 |
otherAccountsPictures | 用来设置当前账户其他账户头像 |
margin |

class YUserAccountsDrawerHeader extends StatefulWidget {
const YUserAccountsDrawerHeader({super.key});
State<YUserAccountsDrawerHeader> createState() =>
_YUserAccountsDrawerHeaderState();
}
class _YUserAccountsDrawerHeaderState extends State<YUserAccountsDrawerHeader> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('YUserAccountsDrawerHeader')),
drawer: Drawer(
child: Column(
children: [
UserAccountsDrawerHeader(
// 用户头像
currentAccountPicture: const CircleAvatar(
backgroundImage:
NetworkImage('https://picsum.photos/100?id=0'),
),
accountName: const Text('YeKun'), //用户名
accountEmail: const Text('2356009030@qq.com'), //用户邮箱
//当前账户其他账户头像
otherAccountsPictures: [
Image.network('https://picsum.photos/50?id=5'),
Image.network('https://picsum.photos/50?id=6'),
Image.network('https://picsum.photos/50?id=7'),
],
decoration: const BoxDecoration(
image: DecorationImage(
image: NetworkImage('https://picsum.photos/500?id=1'),
fit: BoxFit.cover,
)),
),
//下面的列表
ListTile(
leading: const CircleAvatar(
child: Icon(Icons.person),
),
title: const Text('个人中心'),
onTap: () {},
),
const Divider(),
ListTile(
leading: const CircleAvatar(
child: Icon(Icons.settings),
),
title: const Text('系统设置'),
onTap: () {},
),
],
),
));
}
}
7、AppBar TabBar TabBarView
7.1、AppBar自定义顶部按钮图标、颜色
- AppBar常用属性
属性 | 描述 |
---|---|
leading | 在标题前面显示的一个控件,在首页通常显示应用的 logo;在其他界面通 |
title | 标题,通常显示为当前界面的标题文字,可以放组件 |
actions | 右侧图显示的东西,通常使用 IconButton 来表示,可以放按钮组等多个图标 |
bottom | 通常放tabBar,标题下面显示一个 Tab 导航栏 |
backgroundColor | 导航背景颜色 |
iconTheme | 图标样式 |
centerTitle | 标题是否居中显示 |
7.2、AppBar+TabBar实现顶部Tab切换
- TabBar常见属性:
属性 | 描述 |
---|---|
tabs | 显示的标签内容,一般使用Tab对象,也可以是其他的Widget |
controller | TabController对象 |
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 |
7.3、Tabbar TabBarView实现类似头条顶部导航

7.3.1、混入SingleTickerProviderStateMixin
class _YAppbarState extends State<YAppbar> with SingleTickerProviderStateMixin{
...
}
7.3.2、定义TabController
class _YAppbarState extends State<YAppbar> with SingleTickerProviderStateMixin {
late TabController _tabController;
// 生命周期函数:当组件初始化时触发
void initState() {
super.initState();
// 在生命周期中给_tabController赋值
_tabController = TabController(length: 3, vsync: this);
}
...
}
7.3.3、配置TabBar和TabBarView
完整代码
import 'package:flutter/material.dart';
class YAppbar extends StatefulWidget {
const YAppbar({super.key});
State<YAppbar> createState() => _YAppbarState();
}
class _YAppbarState extends State<YAppbar> with SingleTickerProviderStateMixin {
late TabController _tabController;
// 生命周期函数:当组件初始化时触发
void initState() {
super.initState();
// 在生命周期中给_tabController赋值
_tabController = TabController(length: 3, vsync: this);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Appbar'), //标题
backgroundColor: Colors.blue, //导航的背景颜色
centerTitle: true, //标题是否居中
//左侧图标按钮
leading: IconButton(
onPressed: () {
print('左侧按钮图标');
},
icon: const Icon(Icons.menu)),
//右侧按钮图标
actions: [
IconButton(onPressed: () {}, icon: const Icon(Icons.search)),
IconButton(onPressed: () {}, icon: const Icon(Icons.more_horiz))
],
bottom: TabBar(
controller: _tabController, //配置controller需要去掉TabBar的const
tabs: const [
Tab(
child: Text('关注'),
),
Tab(
child: Text('热门'),
),
Tab(
child: Text('视频'),
)
],
),
),
body:
//TabBarView的子元素需要和TabBar对应
TabBarView(
controller: _tabController, //TabBar和TabBarView都需要配置controller
//可以自定义组件
children: [
ListView(
children: const [
ListTile(title: Text('我是关注')),
],
),
ListView(
children: const [
ListTile(title: Text('我是热门')),
],
),
ListView(
children: const [
ListTile(title: Text('我是视频')),
],
)
],
),
);
}
}
7.4、BottomNavigationBar的页面中使用Tabbar

组件代码
PreferredSize可以改变appBar的高度
mport 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
}
Widget build(BuildContext context) {
return Scaffold(
//PreferredSize可以配置appbar的高度
appBar: PreferredSize(
preferredSize: const Size.fromHeight(50), //自定义高度
child: AppBar(
elevation: 100, //底部阴影
backgroundColor: Colors.white, //背景色
//可以把TabBar放在title中
title: TabBar(
controller: _tabController,
indicatorSize: TabBarIndicatorSize.label, //指示器和label等宽
indicatorColor: Colors.black, //底部指示器颜色
labelColor: Colors.red, //label选中的颜色
unselectedLabelColor: Colors.black, //label未选择的颜色
tabs: const [
Tab(child: Text('关注')),
Tab(child: Text('热门')),
Tab(child: Text('推荐')),
],
),
),
),
body: TabBarView(
controller: _tabController,
children: [
ListView(
children: const [Text('我是关注')],
),
ListView(
children: const [Text('我是热门')],
),
ListView(
children: const [Text('我是推荐')],
),
],
),
);
}
}
使用页面为上方案例5的HomePage()页面
7.5、自定义KeepAliveWrapper 缓存页面
AutomaticKeepAliveClientMixin 可以快速的实现页面缓存功能,但是通过混入的方式实现不是很优
雅, 所以我们有必要对AutomaticKeepAliveClientMixin 混入进行封装
例如:在页面A滑动到底部,切换页面B后再切回页面A会回到顶部 使用缓存页面后再切回页面A会停留在刚才的位置
封装KeepAliveWrapper缓存组件
import 'package:flutter/material.dart'; class YKeepAliveWrapper extends StatefulWidget { const YKeepAliveWrapper( {Key? key, this.child, this.keepAlive = true}) : super(key: key); final Widget? child; final bool keepAlive; State<YKeepAliveWrapper> createState() => _YKeepAliveWrapperState(); } class _YKeepAliveWrapperState extends State<YKeepAliveWrapper> with AutomaticKeepAliveClientMixin { Widget build(BuildContext context) { return widget.child!; } bool get wantKeepAlive => widget.keepAlive; void didUpdateWidget(covariant YKeepAliveWrapper oldWidget) { if (oldWidget.keepAlive != widget.keepAlive) { // keepAlive 状态需要更新,实现在 AutomaticKeepAliveClientMixin 中 updateKeepAlive(); } } }
使用
import 'package:bangda/components/YStateComponents/YKeepAliveWrapper.dart';//引入组件
...
body: TabBarView(
controller: _tabController,
children: [
YKeepAliveWrapper(//使用缓存组件包裹后再次切换,保留状态
child: ListView(
children: getList(),
),
),
ListView(
children: const [Text('我是热门')],
),
ListView(
children: const [Text('我是推荐')],
),
],
),
...
7.6 、监听TabController改变事件
方法1:在initState方法里使用addListener监听[推荐]
void initState() { super.initState(); _tabController = TabController(length: 3, vsync: this); // 监听tab切换 _tabController.addListener(() { //print(_tabController.index); //会获取两次,不推荐1 // 推荐判断一下,只触发一次 if (_tabController.animation!.value == _tabController.index) { print(_tabController.index); //推荐 } }); }
方法2[不推荐]
TabBar( onTap: (value) {//只能监听点击切换,不能监听滑动切换 print(value); }, tabs: const [ Tab(child: Text('关注')), Tab(child: Text('热门')), Tab(child: Text('推荐')), ], ),
7.7、页面销毁时销毁tabbar[优化]
// 页面销毁
void dispose() {
// TODO: implement dispose
super.dispose();
// 页面销毁时把_tabController销毁掉
_tabController.dispose();
}
7.8、MaterialApp 去掉debug图标
return MaterialApp(
debugShowCheckedModeBanner:false , //去掉debug图标
home:Tabs(),
...
);