Flutter页面布局
1 盒约束模型
盒约束是指组件可以按照指定限制条件来决定如何布局自身位置。Flutter提供的尺寸限制类容器可以用于限制容器的大小,并提供了多种约束容器组件,常见的有ConstrainedBox、SizedBox和UnconstrainedBox。
1.1 ConstrainedBox
用来对子组件添加额外的约束。
构造函数格式:
1 2 3 4 5
| ConstrainedBox({ Key key, @required this.constraints, Widget child, })
|
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| import 'package:flutter/material.dart';
void main() => runApp(BoxWidget());
class BoxWidget extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '盒约束模型', home: Scaffold( appBar: AppBar(title: Text('盒约束模型--ConstrainedBox')), body: ConstrainedBox( constraints: BoxConstraints( minWidth: 50, maxWidth: 100, minHeight: 50, maxHeight: 100 ), child: Container( width: 80, height: 5, child: DecoratedBox( decoration: BoxDecoration( color: Colors.red ) ) ) ) ) ); } }
|
示例效果:
BoxConstraints常见的构造函数:
1)BoxConstraints.tight():创建一个生成给定大小的限制约束布局;
2)BoxConstraints.expand():创建一个尽可能大的用于填充另一个容器的约束布局;
3)BoxConstraints.tightForFinite():创建一个最大值是确定值的松散约束布局;
4)BoxConstraints.tightFor():创建一个不限最大值的松散约束布局。
1.2 SizedBox
用于给子组件指定固定的宽高。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import 'package:flutter/material.dart';
void main() => runApp(BoxWidget());
class BoxWidget extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '盒约束模型', home: Scaffold( appBar: AppBar(title: Text('盒约束模型--SizedBox')), body: SizedBox( width: 100, height: 100, child: Container( width: 80, height: 5, child: DecoratedBox( decoration: BoxDecoration( color: Colors.red ) ) ) ) ) ); } }
|
示例效果:
1.3 UnconstrainedBox
不会对子组件产生任何限制,它允许子组件按照自己本身的大小进行绘制,可以去除多重约束的限制。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import 'package:flutter/material.dart';
void main() => runApp(BoxWidget());
class BoxWidget extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '盒约束模型', home: Scaffold( appBar: AppBar(title: Text('盒约束模型--UnconstrainedBox')), body: ConstrainedBox( constraints: BoxConstraints( minWidth: 100, minHeight: 100, ), child: UnconstrainedBox( child: Container( width: 50, height: 50, color: Colors.red ) ) ) ) ); } }
|
示例效果:
UnconstrainedBox对父级的约束并非是真正的去除,父组件的限制依然生效,只不过它不影响子组件显示大小,但仍然会占据相应的空间。
2 线性布局
线性布局指的是沿水平或垂直方向排布子组件的布局方式。Flutter使用Row或Column来实现线性布局,作用类似于Android的线性布局(LinearLayout),且Row和Column都继承自弹性布局。
线性布局有主轴和纵轴之分。如果布局沿水平方向排列,那么水平方向就是主轴,垂直方向为纵轴;如果布局沿垂直方向排列,那么垂直方向就是主轴,水平方向为纵轴。
Row表示在水平方向排列子组件,Column表示在垂直方向排列子组件,支持的属性如下:
1)mainAxisAlignment:表示子组件在主轴的对齐方式;
2)mainAxisSize:表示主轴应该占用多大空间;
3)crossAxisAlignment:表示子组件在交叉轴的对齐方式;
4)textDirection:表示子组件在主轴方向上的布局顺序;
5)verticalDirection:表示子组件在交叉轴方向上的布局顺序;
6)textBaseline:排列子组件时使用的基线标准;
7)children:线性布局里排列的内容。
5.3 弹性布局
Flutter中使用Flex来实现弹性布局,类似于CSS的FlexBox,支持的属性如下:
1)direction:主轴的方向;
2)mainAxisAlignment:子组件在主轴的对齐方式;
3)mainAxisSize:主轴占用的空间大小;
4)crossAxisAlignment:子组件在交叉轴的对齐方式;
5)textDirection:子组件在主轴方向上的布局顺序;
6)verticalDirection:子组件在交叉轴方向上的布局顺序;
7)textBaseline:排列子组件时使用的基线标准;
8)children:弹性布局里排列的内容。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| import 'package:flutter/material.dart';
void main() => runApp(FlexWidget());
class FlexWidget extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '页面布局', home: Scaffold( appBar: AppBar(title: Text('页面布局--弹性布局')), body: Flex( direction: Axis.horizontal, mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[ Expanded( flex: 1, child: Container( height: 50, color: Colors.red ), ), Expanded( flex: 1, child: Container( height: 50, color: Colors.green ), ), Expanded( flex: 1, child: Container( height: 50, color: Colors.blue ), ), ], ) ) ); } }
|
示例效果:
Flex组件与Expanded组件可以让Row、Column、Flex的子组件具有弹性能力。当子组件超过主轴的大小时会自动换行;当还有剩余空间时,Expanded组件会占满剩余的所有空间,而Flexible组件只会占用自身大小的空间。
5.4 流式布局
流式布局指的是页面元素的宽度可以根据屏幕的分辨率适配调整,但整体布局风格保持不变。
Flutter中使用Wrap来实现流式布局,支持的属性如下:
1)direction:主轴的方向,默认是Axis.horizontal;
2)alignment:子组件在主轴上的对齐方式;
3)runAlignment:流式布局会自动换行或换列,runAlignment属性指的是每行或每列的对齐方式;
4)runSpacing:每行或每列的间距,默认是0.0;
5)crossAxisAlignment:子组件在交叉轴上的对齐方式;
6)textDirection:子组件在主轴方向上的布局顺序;
7)verticalDirection:子组件在交叉轴方向上的布局顺序;
8)children:流式布局里的子组件。 示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| import 'package:flutter/material.dart';
void main() => runApp(WrapWidget());
class WrapWidget extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '页面布局', home: Scaffold( appBar: AppBar(title: Text('页面布局--流式布局')), body: Wrap( spacing: 8, runSpacing: 4, children: <Widget>[ Chip( avatar: CircleAvatar( backgroundColor: Colors.blue, child: Text('S') ), label: Text('少年的你'), ), Chip( avatar: CircleAvatar( backgroundColor: Colors.blue, child: Text('Z') ), label: Text('中国机长'), ), Chip( avatar: CircleAvatar( backgroundColor: Colors.blue, child: Text('W') ), label: Text('我和我的祖国'), ), Chip( avatar: CircleAvatar( backgroundColor: Colors.blue, child: Text('Z') ), label: Text('终结者:黑暗命运'), ), ] ) ) ); } }
|
示例效果:
5.5 层叠布局
层叠布局是指子组件可以根据父组件的位置来确定自身位置的布局。层叠布局允许子组件以堆叠的方式来排列子组件,它和Web中的绝对定位、Android中的Frame布局类似。
Flutter使用Stack和Positioned两个组件来配合实现绝对定位,Stack组件主要用于子组件的堆叠操作,而Positioned组件则用于确定子组件在Stack组件中的位置。
层叠布局支持的属性如下:
1)alignment:决定如何去对齐没有定位或者部分定位的子组件;
2)textDirection:用于确定alignment的对其方向;
3)fit:用于决定non-positioned子组件如何去适应层叠布局的大小;
4)overflow:当子组件超出Stack组件的范围时,决定如何显示超出的子组件;
5)children:Stack组件里排列的内容。
在层叠布局中,先排列的子组件会出现在视图的底部,后排列的则会显示在上面。Stack组件将子组件分为无定位的子组件和有定位的子组件。
无定位的子组件不被Positioned组件嵌套,需要设置alignment属性来确定自己在父组件里面的位置。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import 'package:flutter/material.dart';
void main() => runApp(StackWidget());
class StackWidget extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '页面布局', home: Scaffold( appBar: AppBar(title: Text('页面布局--层叠布局')), body: Stack( alignment: const Alignment(0, 0), children: <Widget>[ Container( constraints: BoxConstraints.tightFor(width: 200, height: 150), decoration: BoxDecoration( gradient: RadialGradient( colors: [Colors.red, Colors.orange], center: Alignment.topLeft, radius: .98 ), ), ), Text( 'Hello Flutter', style: TextStyle( fontSize: 24, color: Colors.white ) ) ], ) ) ); } }
|
示例效果:
有定位的子组件被Positioned组件嵌套,Positioned组件可以控制子组件到父组件4条边的距离。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| import 'package:flutter/material.dart';
void main() => runApp(StackWidget());
class StackWidget extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '页面布局', home: Scaffold( appBar: AppBar(title: Text('页面布局--层叠布局')), body: Stack( children: <Widget>[ Positioned( left: 30, top: 40, child: Container( constraints: BoxConstraints.tightFor(width: 200, height: 150), decoration: BoxDecoration( gradient: RadialGradient( colors: [Colors.red, Colors.orange], center: Alignment.topLeft, radius: .98 ), ), ), ), Positioned( left: 60, top: 105, child: Text( 'Hello Flutter', style: TextStyle( fontSize: 24, color: Colors.white ) ) ), ], ) ) ); } }
|
示例效果: