Flutter中的布局&状态管理
布局
若想将文字置于当前页面的中间位置,可以使用Center
或者Alignment
控件
Center
class LayoutDemo extends StatelessWidget {
const LayoutDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Center(
child: Text('LayoutDemo', style:TextStyle(fontSize: 20, color: Colors.red, backgroundColor: Colors.cyan))
);
}
}
Alignment
使用Container
包装一层,设置子控件的成员变量Alignment
class LayoutDemo extends StatelessWidget {
const LayoutDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.orange,
alignment: const Alignment(0, 0),
child: const Text('LayoutDemo', style:TextStyle(fontSize: 20, backgroundColor: Colors.cyan))
);
}
}
通过调试Alignment(x, y)
可知坐标系如下图,原点位于中心位置,x
、y
的取值范围均为[-1,1]
布局
Flutter
中的布局与前端中的布局方式很相似,都是FlexBox
模式,下面分别介绍横向布局
、纵向布局
、层级布局
Row(横向布局)
使用Row(横向布局)
布局的控件会在横轴方向占满父部件
注意这个属性mainAxisAlignment(主轴方向布局)
共有以下几个选项:
start
:布局从主轴开始的位置正向布局end
:布局从主轴结束的位置开始反向布局center
:布局位于主轴中心的位置spaceBetween
:把主轴方向剩余的空间均匀的分布于控件之间spaceAround
:把主轴方向剩余的空间平均分配到空间周围spaceEvenly
:把主轴发现剩余的空间平均分配在控件周围空余的部分
class LayoutDemo extends StatelessWidget {
const LayoutDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.orange,
alignment: const Alignment(0, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
color: Colors.red,
width: 50,
height: 50,
child: cosnt Icon(Icons.add),
),
Container(
color: Colors.blue,
width: 50,
height: 50,
child: cosnt Icon(Icons.search),
),
Container(
color: Colors.white,
width: 50,
height: 50,
child: cosnt Icon(Icons.battery_alert),
),
],
)
);
}
}
mainAxisAlignment
start
布局沿主轴开始的方向,刚才已经阐述过这里的坐标系,又是Row
排列,则主轴是x
方向,从左至右
end
布局沿主轴结束的方向,所以是从右至左来排布
center
布局位于主轴中心的位置
spaceBetween
把主轴方向剩余的空间均匀的分布于控件之间
spaceAround
把主轴方向剩余的空间平均分配到空间周围
spaceEvenly
把主轴发现剩余的空间平均分配在控件周围空余的部分
crossAxisAlignment
这里举例仍然是在Row(横向布局)
的情况下来讨论crossAxisAlignment
属性
crossAxisAlignment
中有以下几个选项:
start
:向交叉轴开始的方向对齐end
:向交叉轴结束的方向对齐center
:交叉轴方向居中对齐stretch
:填满交叉轴方向baseline
:文字基线对齐
class LayoutDemo extends StatelessWidget {
const LayoutDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.orange,
alignment: const Alignment(0, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
color: Colors.red,
child: const Icon(Icons.add, size: 50),
),
Container(
color: Colors.blue,
child: const Icon(Icons.search, size: 100),
),
Container(
color: Colors.white,
child: const Icon(Icons.battery_alert, size: 150),
),
],
)
);
}
}
start
在交叉轴方向从上到下进行布局,相当于顶部对齐
end
在交叉轴方向从下到上进行布局,相当于底部对齐
center
在交叉轴方向上居中布局,这也是默认属性
stretch
子控件将撑满交叉轴方向
baseline
baseline
要配合文字来进行演示,同时也需要设置textBaseline
,意思就是以文字的基线来进行对齐
class LayoutDemo extends StatelessWidget {
const LayoutDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.orange,
alignment: const Alignment(0, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
Container(
height: 80,
color: Colors.red,
child: const Text('好好学习', style: TextStyle(fontSize: 15))
),
Container(
height: 80,
color: Colors.blue,
child: const Text('天天要吃饭', style: TextStyle(fontSize: 30))
),
Container(
height: 80,
color: Colors.white,
child: const Text('晚上', style: TextStyle(fontSize: 60))
),
],
)
);
}
}
Expanded(填充布局)
Expanded(填充布局)
:
- 在主轴方向不会剩下间隙,将被
Expanded
包装的部件进行拉伸和压缩 - 主轴横向,设置宽度没有意义
- 主轴纵向,设置高度没有意义
- 当
Text
被Expanded
包装后文字可以自动换行,所以Expanded
也被称为灵活布局
class LayoutDemo extends StatelessWidget {
const LayoutDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// return const Center(
// child: Text('LayoutDemo', style:TextStyle(fontSize: 20, color: Colors.red, backgroundColor: Colors.cyan))
// );
return Container(
color: Colors.orange,
alignment: const Alignment(0, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
Expanded(child: Container(
height: 80,
color: Colors.red,
child: const Text('好好学习', style: TextStyle(fontSize: 15))
)),
Expanded(child: Container(
height: 80,
color: Colors.blue,
child: const Text('天天要吃饭', style: TextStyle(fontSize: 30))
)),
Expanded(child: Container(
height: 80,
color: Colors.white,
child: const Text('晚上', style: TextStyle(fontSize: 60))
))
],
)
//child: const Text('LayoutDemo', style:TextStyle(fontSize: 20, backgroundColor: Colors.cyan))
);
}
}
Column(纵向布局)
和Row
一样,使用Column(纵向布局)
布局的控件会在纵轴方向占满父部件,mainAxisAlignment
和crossAxisAlignment
属性同上,这里不再演示
class LayoutDemo extends StatelessWidget {
const LayoutDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.orange,
alignment: const Alignment(0, 0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.end,
// textBaseline: TextBaseline.alphabetic,
children: [
Expanded(child: Container(
height: 80,
color: Colors.red,
child: const Text('好好学习', style: TextStyle(fontSize: 15))
)),
Expanded(child: Container(
height: 80,
color: Colors.blue,
child: const Text('天天要吃饭', style: TextStyle(fontSize: 30))
)),
Expanded(child: Container(
height: 80,
color: Colors.white,
child: const Text('晚上', style: TextStyle(fontSize: 60))
))
],
)
);
}
}
Stack(层次布局)
class StackDemo extends StatelessWidget {
const StackDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
alignment: const Alignment(-1,-1),
children: [
Container(
color: Colors.white,
width: 200,
height: 200,
child: const Icon(Icons.add)
),
Container(
color: Colors.blue,
width: 100,
height: 100,
child: const Icon(Icons.search)
),Container(
color: Colors.cyan,
width: 50,
height: 50,
child: const Icon(Icons.alarm)
),
],
);
}
}
Stack
也叫层次布局,如果这里需要子控件一个在左一个在右的话则需要使用到Stack
中的Positioned
属性来完成控制子控件的相对位置
class StackDemo extends StatelessWidget {
const StackDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
alignment: const Alignment(-1,-1),
children: [
Positioned(child: Container(
color: Colors.white,
width: 200,
height: 200,
child: const Icon(Icons.add)
)),
Positioned(
left: 1,
bottom: 1,
child: Container(
color: Colors.blue,
width: 100,
height: 100,
child: const Icon(Icons.search)
)),
Positioned(
right: 1,
child: Container(
color: Colors.cyan,
width: 50,
height: 50,
child: const Icon(Icons.alarm)
))
],
);
}
}
AspectRatio(设置宽高比)
class AspectRatioDemo extends StatelessWidget {
const AspectRatioDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
//padding: const EdgeInsets.all(10),
color: Colors.blue,
//width: 400,
height: 150,
child: AspectRatio(
aspectRatio: 2/1,
child: Container(
alignment: const Alignment(0,0),
color: Colors.red,
),
),
);
}
}
通过设置aspectRatio(宽高比)
为2/1
来设置宽度为300
,如果父控件中设置了宽度,则以父控件为主
状态管理
现在需要是如果有数据变化了需要实时渲染到页面上以便看到变化,举例如下
class StateManageDemo extends StatelessWidget {
StateManageDemo({Key? key}) : super(key: key);
int count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('StateManageDemo', style: TextStyle(fontSize: 15),)),
body: Center(
child: Chip(
label: Text('$count'),
),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: (){
count++;
debugPrint('count = $count');
},
),
);
}
}
但是运行后发现,每次点击按钮后确实已经改变了count
的值,但是并未更新到页面上,甚至热更新也不能更新结果上去。原因是StatelessWidget
是一个无状态的控件,也就是说在创建完成后就不会再进入其build
方法了,那么也就无法完成更新,所以如果想使用数据有变化的控件可以使用StatefullWidget
class StateManageDemo extends StatefulWidget {
const StateManageDemo({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _SMDState();
}
class _SMDState extends State<StateManageDemo>{
int count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('StateManageDemo', style: TextStyle(fontSize: 15),)),
body: Center(
child: Chip(
label: Text('$count'),
),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: (){
count++;
setState(() {
});
debugPrint('count = $count');
},
),
);
}
}
使用StatefullWidget
控件时需要搭配使用一个状态管理的类,原来的build
方法放在这个类中实现,也就是说数据由这个类来管理和渲染,并且在状态改变后需要设置回调setState
,这样就可以完成上面的需求了。
转载自:https://juejin.cn/post/7165498144245940261