05*:Flutter之基础布局Container:(Container:padding、margins、borders)(Row、Column、Stack【Align、Positioned】)(MainAxisAlignment、CrossAxisAlignment)(Flex、expanded)(Wrap、Flow)
问题
(Container:padding、margins、borders)
(Row、Column、Stack【Align、Positioned】)
(MainAxisAlignment、CrossAxisAlignment)
目录
1:Container(盒子模型)
2:row、column线性布局
3:stack层叠布局
4:flex弹性布局
5:Expanded: 扩展
预备
正文
一:Container(盒子模型)
Container 给一个组件添加padding,margins,边界(borders),背景颜色或者其他装饰 (decorations)和阴影。
import 'package:flutter/material.dart'; class LearnContainer extends StatefulWidget{ @override State<StatefulWidget> createState() { return new _LearnContainer(); } } class _LearnContainer extends State<LearnContainer>{ @override Widget build(BuildContext context) { return new Container( width: 10.0, height: 10.0, alignment: new Alignment(0.0, 0.0),//设置子控件的位置 // color: Colors.brown, child: new Text('这是一个Container', textAlign:TextAlign.center ,//设置文字居中 style: new TextStyle( color: Colors.white, ), ),//显示的内容 padding: EdgeInsets.all(0.0),//设置子控件padding margin: EdgeInsets.all(50.0),//设置子控件margin // foregroundDecoration:, 设置子控件上面的装饰 // constraints: 设置子控件尺寸约束的条件 比如 宽高 decoration: new BoxDecoration(//设置子控件背后的装饰 color: Colors.blue, //和Container下面的color会冲突 具体原因不详 border: new Border.all(//添加边框 width: 10.0,//边框宽度 color: Colors.green,//边框颜色 ), borderRadius: new BorderRadius.all(Radius.circular(20.0)),//设置圆角 boxShadow: <BoxShadow>[//设置阴影 // new BoxShadow( // color: Colors.black, // blurRadius: 20.0, // ), new BoxShadow( color: Colors.red,//阴影颜色 blurRadius: 20.0,//阴影大小 ), // new BoxShadow( // color: Colors.yellowAccent, // blurRadius: 20.0, // ), // new BoxShadow( // color: Colors.blue, // blurRadius: 30.0, // ) ] ), ); } }
1:Container
它是一个结合了绘制(painting)、定位(positioning)以及尺寸(sizing)widget的widget。
Container({ Key key, this.alignment, this.padding, Color color, Decoration decoration, this.foregroundDecoration, double width, double height, BoxConstraints constraints, this.margin, this.transform, this.child, })
alignment:控制child的对齐方式
decoration:绘制在child后面的装饰
2:padding/margin
控件加入内/外边距。属性如下:
EdgeInsets.all(12),// 指定四个方向相同的值
EdgeInsets.fromLTRB(10, 20, 30, 40),// 分别指定四个方向的值
EdgeInsets.only(left: 10,right: 30),// 指定任意个方向的值EdgeInsets.symmetric(vertical: 10,horizontal: 50),//指定水平(left right)或垂直方向(top,bottom)的值。
二:线性布局Row、Column
1:row继承自Flex。可将children中的widget在水平方向上进行排列。与css的flex(flex-direction:row;)布局相似
Row和Column分别是Flutter中的水平和垂直布局,对应Android里面的LinearLayout的水平垂直方向布局。
MainAxisAlignment (主轴)属性:
enum MainAxisAlignment { //将子控件放在主轴的开始位置 start, //将子控件放在主轴的结束位置 end, //将子控件放在主轴的中间位置 center, //将主轴空白位置进行均分,排列子元素,手尾没有空隙 spaceBetween, //将主轴空白区域均分,使中间各个子控件间距相等,首尾子控件间距为中间子控件间距的一半 spaceAround, //将主轴空白区域均分,使各个子控件间距相等 spaceEvenly, }
CrossAxisAlignment(交叉轴)属性:
enum CrossAxisAlignment { //将子控件放在交叉轴的起始位置 start, //将子控件放在交叉轴的结束位置 end, //将子控件放在交叉轴的中间位置 center, //使子控件填满交叉轴 stretch, //将子控件放在交叉轴的上,并且与基线相匹配(不常用) baseline, }
配合Expanded使用,使Expanded中的child充满空白区域。Expanded组件必须用在Row、Column、Flex内。
Expanded( flex: 1, child: Text(""), )
属性:
crossAxisAlignment:交叉轴子组件对齐方式
mainAxisAlignment:主轴子组件排列方式
mainAxisSize:Main 轴大小,相当于match_parent,wrap_content
verticalDirection:从下向上或从上向下摆放子组件
import 'package:flutter/material.dart'; class RowDemo extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.max, textDirection: TextDirection.ltr, verticalDirection: VerticalDirection.down, textBaseline: TextBaseline.ideographic, children: <Widget>[ Container( width: 80.0, height: 200.0, color: Colors.cyanAccent, ), Container( width: 80.0, color: Colors.red, ), Container( width: 80.0, height: 170.0, color: Colors.green, ), Container( width: 80.0, height: 600.0, color: Colors.orange, ), ], ); } }
1:MainAxisAlignment.star
从开始位置开始,textDirection若为TextDirection.rtl则为右对齐
2:同样继承自Flex。children中的widget会在竖直方向上进行排列。相关属性与Row类似,只不过Column的主轴是竖直方向,副轴为水平方向
mport 'package:flutter/material.dart'; class ColumnDemo extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.center, textDirection: TextDirection.rtl, verticalDirection: VerticalDirection.down, children: <Widget>[ Container( height: 100.0, width: 90.0, color: Colors.redAccent, ), Container( height: 100.0, width: 190.0, color: Colors.purpleAccent, ), Container( height: 100.0, color: Colors.cyanAccent, ), Container( height: 100.0, width: 200.0, color: Colors.greenAccent, ), Container( height: 100.0, width: 290.0, color: Colors.yellow, ), ], ); } }
三:层叠布局Stack: Align和Positioned结合使用形成相对布局
参数详解
属性 | 说明 |
alignment |
布局定位 默认 AlignmentDirectional.topStart 我们也可以直接传入参数,自定义位置(值在1与-1之间),如:alignment: Alignment(0.5,0.5), |
textDirection |
正反排序 TextDirection.ltr TextDirection.rtl |
fit | 默认StackFit.loose |
overflow | 默认Overflow.clip |
children | 子元素 |
属性 | 说明 |
left | 左边距 |
top | 上边距 |
right | 右边距 |
bottom | 下边距 |
width | 子元素宽 |
height | 子元素高 |
child | 子元素 |
3.1: 只使用Stack 层叠组件
mport 'package:flutter/material.dart'; class StackDemo extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return Stack( alignment: AlignmentDirectional.topStart, textDirection: TextDirection.ltr, fit: StackFit.loose, overflow: Overflow.clip, children: <Widget>[ Container( width: 120, height: 120, color: Colors.red, ), Container( width: 100, height: 100, color: Colors.green, ), Container( width: 80, height: 80, color: Colors.yellow, ), ], ); } }
3.2:Stack与Align
Stack中的children都是层层进行叠加的,通过Align或Positioned可将这些层叠组件分开,实现视觉上的分离:
class StackAlignDemo extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return ConstrainedBox( constraints: BoxConstraints.expand(), child: Stack( children: <Widget>[ Align( alignment: Alignment.topLeft, child: Icon(Icons.settings, size: 30,), ), Align( alignment: Alignment.topRight, child: Icon(Icons.add_location, size: 30,), ), Align( alignment: Alignment(-1, 0.9), child: Icon(Icons.forward, size: 30,), ), Align( alignment: Alignment(1, 0.9), child: Icon(Icons.home, size: 30,), ), Align( alignment: Alignment.center, child: Icon(Icons.center_focus_strong, size: 40,), ) ], ), ); } }
3.3: Stack与Positioned
class StackPositionedDemo extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Stack( children: <Widget>[ Positioned( top: 0, left: 0, child: Icon(Icons.settings, size: 30,), ), Positioned( top: 0, right: 0, child: Icon(Icons.add_location, size: 30,), ), Positioned( left: 0, bottom: 20, child: Icon(Icons.forward, size: 30,), ), Positioned( right: 0, bottom: 20, child: Icon(Icons.home, size: 30,), ), Positioned.fill( // 居中 child: Icon(Icons.center_focus_strong, size: 40,), ) ], ); } }
Positioned构造器
const Positioned({ Key key, this.left, this.top, this.right, this.bottom, this.width, this.height, @required Widget child, }) : assert(left == null || right == null || width == null), assert(top == null || bottom == null || height == null), super(key: key, child: child);
四:弹性布局Flex
弹性布局允许子widget按照一定比例来分配父容器空间,Flutter中的弹性布局主要通过Flex和Expanded来配合实现。
Flex可沿着水平或竖直方向对widget进行布局。
1:构造函数
Flex({ Key key, @required this.direction, // 主轴方向:Axis.horizontal水平方向为主轴,即为Row;Axis.vertical垂直方向为主轴,即为Column this.mainAxisAlignment = MainAxisAlignment.start, this.mainAxisSize = MainAxisSize.max, this.crossAxisAlignment = CrossAxisAlignment.center, this.textDirection, this.verticalDirection = VerticalDirection.down, this.textBaseline, List<Widget> children = const <Widget>[], }) : assert(direction != null), assert(mainAxisAlignment != null), assert(mainAxisSize != null), assert(crossAxisAlignment != null), assert(verticalDirection != null), assert(crossAxisAlignment != CrossAxisAlignment.baseline || textBaseline != null), super(key: key, children: children);
2:示例代码
import 'package:flutter/material.dart'; class FlexDemo extends StatelessWidget{ @override Widget build(BuildContext context) { Widget c1 = Container( width: 80.0, height: 150.0, color: Colors.yellow, child: Center( child: Text( 'container1', style: TextStyle( color: Colors.white ), ), ), ); Widget c2 = Container( width: 100.0, height: 100.0, color: Colors.cyanAccent, child: Center( child: Text( 'container2', style: TextStyle( color: Colors.white ), ), ), ); Widget c3 = Container( width: 160.0, height: 100.0, color: Colors.pinkAccent, child: Center( child: Text( 'container3', style: TextStyle( color: Colors.white ), ), ), ); Widget flexForRow = Flex( direction: Axis.horizontal, mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.max, textDirection: TextDirection.ltr, verticalDirection: VerticalDirection.down, textBaseline: TextBaseline.ideographic, children: <Widget>[c1, c2, c3], ); Widget flexForColumn = Flex( direction: Axis.vertical, mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.max, textDirection: TextDirection.ltr, verticalDirection: VerticalDirection.down, children: <Widget>[c1, c2, c3], ); // TODO: implement build return flexForRow; } }
flexForRow效果:
flexForColumn效果:
五:Expanded:扩展
用在Flex、Column、Row中,用来占满主轴方向上的剩余空间。
利用上面示例,将flexForRow中的c1改为Expanded(child: c1,),你会发现水平方向上的空白没有了,c1将剩余空间占满,效果如下:
效果
Expanded中还有一个flex属性,该属性大于0时表示会自动占满剩余空间,默认为1。如果多个Expanded同时指定flex,flex的值就是这几个Expanded所占空间的比例。将上面flexForColumn的children改为如下:
children: <Widget>[ Expanded( child: c1, flex:1, ), Expanded( child: c2, flex:1, ), Expanded( child: c3, flex:1, ), ]
效果
这三个Container高度被平均分配,这是因为flex默认值为1,所以这三个的高度比为1:1:1,如果设置flex分别为1、2、3,则这三个Container的高度比为1:2:3。
六: Wrap:换行
熟悉flex布局的同学肯定知道flex布局会有wrap属性,就是换行,而Flex、Column、Row都是不可以换行的。那么如果我们需要换行怎么办呢?Flutte提供了Wrap来解决这一需求,Wrap与Flex具有相似的属性
Wrap({ Key key, this.direction = Axis.horizontal, // 主轴方向 this.alignment = WrapAlignment.start, //主轴方向上的对齐方式 this.spacing = 0.0, // 主轴方向上children的间隔 this.runAlignment = WrapAlignment.start, // children如何放置在交叉轴上 this.runSpacing = 0.0, // 交叉轴上children的间隔 this.crossAxisAlignment = WrapCrossAlignment.start, // children之间在交叉轴方向上的对齐方式 this.textDirection, // 水平开始的方向 this.verticalDirection = VerticalDirection.down, // 竖直的方向 List<Widget> children = const <Widget>[], }) : super(key: key, children: children);
示例
import 'package:flutter/material.dart'; class WrapDemo extends StatelessWidget{ @override Widget build(BuildContext context) { Widget c1 = Container( height: 130.0, width: 130.0, color: Colors.redAccent, ); Widget c2 = Container( height: 50.0, width: 50.0, color: Colors.lightBlueAccent, ); Widget c3 = Container( height: 80.0, width: 80.0, color: Colors.yellow, ); Widget c4 = Container( height: 100.0, width: 100.0, color: Colors.greenAccent, ); Widget c5 = Container( height: 170.0, width: 170.0, color: Colors.grey, ); // TODO: implement build return Container( width: double.infinity, height: double.infinity, child: Wrap( direction: Axis.horizontal, spacing: 10.0, runSpacing: 20.0, alignment: WrapAlignment.center, runAlignment: WrapAlignment.start, crossAxisAlignment: WrapCrossAlignment.start, verticalDirection: VerticalDirection.down, textDirection: TextDirection.ltr, children: <Widget>[c1, c2, c3, c4, c5], ), ); } }
Wrap的相关属性大部分与Flex类似,如alignment与Flex的mainAxisAlignment类似,都决定了在主轴方向上的对齐方式;crossAlignment与Flex的crossAxisAlignment相似,都决定了children在交叉轴上的对齐方式。这里主要对runAlignment与crossAlignment属性进行说明,runAlignment属性决定了children整体如何放置在交叉轴上,而crossAlignment属性决定了children之间在交叉轴方向上的对齐方式。详细效果参考如下:
runAlignment值
WrapAlignment.start
七:flow:换行
Flow可自定义children的排列布局,相对Wrap使用比较复杂。Flow中的delegate属性是自定义布局的关键,具体使用参考下面例子。
import 'package:flutter/material.dart'; class FlowDemo extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return Flow( delegate: TestFlowDelegate(margin:EdgeInsets.only(top: 10.0, left: 10.0)), children: <Widget>[ Container(width: 80.0, height:80.0, color: Colors.red,), Container(width: 80.0, height:80.0, color: Colors.green,), Container(width: 80.0, height:80.0, color: Colors.blue,), Container(width: 80.0, height:80.0, color: Colors.yellow,), Container(width: 80.0, height:80.0, color: Colors.brown,), Container(width: 80.0, height:80.0, color: Colors.purple,), ], ) ; } } class TestFlowDelegate extends FlowDelegate{ EdgeInsets margin = EdgeInsets.zero; TestFlowDelegate({this.margin}); @override void paintChildren(FlowPaintingContext context) { double x = margin.left; double y = margin.top; for (int i = 0; i < context.childCount; i++) { double w = context.getChildSize(i).width + x + margin.right; if (w < context.size.width) { context.paintChild(i, transform: new Matrix4.translationValues(x, y, 0.0)); x = w + margin.left; } else { x = margin.left; y += context.getChildSize(i).height + margin.top + margin.bottom; context.paintChild(i, transform: new Matrix4.translationValues(x, y, 0.0)); x += context.getChildSize(i).width + margin.left + margin.right; } } } @override bool shouldRepaint(FlowDelegate oldDelegate) { // TODO: implement shouldRepaint return oldDelegate != this; } }
注意
引用