这是我参与「第五届青训营 」伴学笔记创作活动的第 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 组件
| 名称 | 功能 |
|---|---|
| alignment | topCenter : 顶部居中对齐 topLeft : 顶部左对齐 topRight : 顶部右对齐 center : 水平垂直居中对齐 centerLeft : 垂直居中水平局左对齐 centerRight : 垂直居中水平居右对齐 bottomCenter: 底部居中对齐 bottomLeft : 底部居左对齐 bottomRight : 底部居右对齐 |
| decoration | |
| margin | margin 属性时表示 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 组件的常用属性:
| 名称 | 类型 | 说明 |
|---|---|---|
| alignment | alignment | 图片的对齐方式 |
| color / colorBlendMode | 设置图片的背景颜色,通常和 colorBlendMode 配合一起使用,这样可以是图片颜色和背景色混合。 | |
| fit | BoxFit | fit 属性用来控制图片的拉伸和挤压,这都是根据父容器来的。 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 本地图片
- 新建对应目录(严格);
- 然后打开 pubspec.yaml 声明一下添加的图片文件,注意配对
- 在代码中使用
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 来定义列表项,支持垂直和水平方向展示。通过一个属性就可以控制列表的显示方向。列表有以下分类:
- 垂直列表
- 垂直图文列表
- 水平列表
- 动态列表
- 矩阵式列表
列表参数
| 名称 | 类型 | 说明 |
|---|---|---|
| scrollDirection | Axis | Axis.horizontal 水平列表 Axis.vertical 垂直列表 |
| padding | EdgeInsetsGeometry | 内边距 |
| resolve | bool | 组件反向排序 |
| children | List | 列表元素 |
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 来定义列表项,支持垂直和水平方向展示。通过一个属性就可以控制列表的显示方向。列表有以下分类:
- 垂直列表
- 垂直图文列表
- 水平列表
- 动态列表
- 矩阵式列表
Flutter 列表参数
| 名称 | 类型 | 说明 |
|---|---|---|
| scrollDirection | Axis | Axis.horizontal 水平列表 Axis.vertical 垂直列表 |
| padding | EdgeInsetsGeometry | 内边距 |
| resolve | bool | 组件反向排序 |
| chilren | List | 列表元素 |
Flutter 动态列表
// 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: 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_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: 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 创建网络列表有多种方式,下面主要介绍两种:
- GirdView.count
- GridView.builder
常用属性:
| 名称 | 类型 | 说明 |
|---|---|---|
| scrollDirection | Axis | 滚动方法 |
| padding | EdgeInsetsGeometry | 内边距 |
| resolve | bool | 组件反向排序 |
| crossAxisSpacing | double | 水平子 Widget 之间的间距 |
| mainAxisSpacing | double | 垂直子 Widget 之间的间距 |
| crossAxisCount | int | 一行的 Widget 数量 |
| childAspectRatio | double | 子 Widget 宽高比例 |
| children | [] | |
| gridDelegate | SliverGridDelegateWithFixedCrossAxisCount(常用) SliverGridDelegateWithMaxCrossAxisExtent | 控制布局主要用在 GridView.builder 里面 |
GridView.count
// 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: 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_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: 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
| 属性 | 说明 |
|---|---|
| padding | padding 值,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 | 子组件 |
| Shape | Card 的阴影效果,默认的阴影效果为圆角的长方形边。 |
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 相关属性的介绍。 |
| runAlignment | run 的对齐方式。run 可以理解为新的行或者列,如果是水平方向布局的话,run 可以理解为新的一行 |
| runSpacing | run 的行距 |
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 常见的属性
| 属性名 | 说明 |
|---|---|
| items | List 底部导航条按钮集合 |
| iconSize | icon |
| currentIndex | 默认选中第几个 |
| onTap | 选中变化回调函数 |
| fixedColor | 选中的颜色 |
| type | BottomNavigationBarType.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());
}
}