目录
子 widget 按照垂直方向排列,继承自 flex
Column({
Key key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
MainAxisSize mainAxisSize = MainAxisSize.max,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
TextDirection textDirection,
VerticalDirection verticalDirection = VerticalDirection.down,
TextBaseline textBaseline,
List<Widget> children = const <Widget>[],
}) : super(
children: children,
key: key,
direction: Axis.vertical,
mainAxisAlignment: mainAxisAlignment,
mainAxisSize: mainAxisSize,
crossAxisAlignment: crossAxisAlignment,
textDirection: textDirection,
verticalDirection: verticalDirection,
textBaseline: textBaseline,
);
/**
* @des Column Widget
* @author liyongli 20190422
* */
class ColumnWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() => new _ColumnState();
}
/**
* @des Column Widget State
* @author liyongli 20190422
* */
class _ColumnState extends State<ColumnWidget>{
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text("Row Widget"),
),
body: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
verticalDirection: VerticalDirection.up,
children: <Widget>[
new Text("我是一只小小鸟 ",
style: new TextStyle(
color: Color(0xff2196F3),
height: 3.0
)),
new Text("飞着飞着",
style: new TextStyle(
color: Color(0xff2196F3),
height: 2.0
)),
new Text("我更有劲了",
style: new TextStyle(
color: Color(0xff2196F3),
height: 4.0
))
],
),
),
);
}
}
子 widget 按照水平方向排列,继承自 flex
Row({
Key key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
MainAxisSize mainAxisSize = MainAxisSize.max,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
TextDirection textDirection,
VerticalDirection verticalDirection = VerticalDirection.down,
TextBaseline textBaseline,
List<Widget> children = const <Widget>[],
}) : super(
children: children,
key: key,
direction: Axis.horizontal,
mainAxisAlignment: mainAxisAlignment,
mainAxisSize: mainAxisSize,
crossAxisAlignment: crossAxisAlignment,
textDirection: textDirection,
verticalDirection: verticalDirection,
textBaseline: textBaseline,
);
import 'package:flutter/material.dart';
/**
* @des Row Widget
* @author liyongli 20190422
* */
class RowWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() => new _RowState();
}
/**
* @des Row Widget State
* @author liyongli 20190422
* */
class _RowState extends State<RowWidget>{
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text("Row Widget"),
),
body: new Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
verticalDirection: VerticalDirection.up,
children: <Widget>[
new Text("我是一只小小鸟 ",
style: new TextStyle(
color: Color(0xff2196F3),
height: 3.0
)),
new Text(" 翅膀硬了",
style: new TextStyle(
color: Color(0xff2196F3),
height: 2.0
)),
new Text(" 不停的飞",
style: new TextStyle(
color: Color(0xff2196F3),
height: 4.0
))
],
),
),
);
}
}
flex 可以按水平或垂直方向排列子 widget,并且允许子 widget 按照比例分配父 widget 的空间,row 和 column 均继承自 flex
Flex({
Key key,
@required this.direction,
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>[],
})
可以按照设定的比例值扩展/扩大在 row、column、flex 布局中 widget 的所用空间
Expanded({
flex:1, // 当 flex = 0 时为占用空间不扩展
child: Container(
height: 30.0
),
})
/**
* @des Flex Widget
* @author liyongli 20190422
* */
class FlexWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() => new _FlexState();
}
/**
* @des Flex Widget State
* @author liyongli 20190422
* */
class _FlexState extends State<FlexWidget>{
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text("Row Widget"),
),
body: new Column(
children: <Widget>[
new Flex(
direction: Axis.horizontal,
children: <Widget>[
Expanded(
flex: 1,
child: Container(
color: Color(0xff333333),
height: 20.0,
),
),
Expanded(
flex: 2,
child: Container(
color: Color(0xff999999),
height: 20.0,
),
),
Expanded(
flex: 2,
child: Container(
color: Color(0xff666666),
height: 20.0,
),
)
],
)
],
),
),
);
}
}
/**
* @des Flex Widget
* @author liyongli 20190422
* */
class FlexWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() => new _FlexState();
}
/**
* @des Flex Widget State
* @author liyongli 20190422
* */
class _FlexState extends State<FlexWidget>{
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text("Flex Widget"),
),
body: new Center(
child: new SizedBox(
width: 20,
child: new Flex(
direction: Axis.vertical,
children: <Widget>[
// 第一部分占五分之一
Expanded(
flex: 1,
child: Container(
color: Color(0xff333333),
height: 20,
),
),
// 第二部分占五分之二
Expanded(
flex: 2,
child: Container(
color: Color(0xff999999),
height: 20,
),
),
// 第三部分占五分之二
Expanded(
flex: 2,
child: Container(
color: Color(0xff666666),
height: 20,
),
),
],
),
)
)
),
);
}
}
若布局中包含的 widget 超出屏幕范围,且需要自动折行展示,那么你需要使用流式布局 wrap 来实现,wrap 的构成与 flex + row + column 相似
Wrap({
Key key,
this.direction = Axis.horizontal,
this.alignment = WrapAlignment.start,
this.spacing = 0.0,
this.runAlignment = WrapAlignment.start,
this.runSpacing = 0.0,
this.crossAxisAlignment = WrapCrossAlignment.start,
this.textDirection,
this.verticalDirection = VerticalDirection.down,
List<Widget> children = const <Widget>[],
}) : super(key: key, children: children);
(key,alignment,crossAxisAlignment,textDirection,verticalDirection,children 参数意义与 flex / row / column 相同)
/**
* @des Wrap Widget
* @author liyongli 20190423
* */
class WrapWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() => new _WrapState();
}
/**
* @des Wrap Widget State
* @author liyongli 20190423
* */
class _WrapState extends State<WrapWidget>{
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text("Wrap Widget"),
),
body: new Center(
child: new Wrap(
spacing: 20.0, // 主轴(水平)方向间距
runSpacing: 5.0, // 纵轴(垂直)方向间距
alignment: WrapAlignment.center, //沿主轴方向居中
children: <Widget>[
new RaisedButton(
child: new Text("A"),
color: Colors.blue ,
textColor: Colors.white,
onPressed: _BtnClick,
),
new RaisedButton(
child: new Text("B"),
color: Colors.blue ,
textColor: Colors.white,
onPressed: _BtnClick,
),
new RaisedButton(
child: new Text("C"),
color: Colors.blue ,
textColor: Colors.white,
onPressed: _BtnClick,
),
new RaisedButton(
child: new Text("D"),
color: Colors.blue ,
textColor: Colors.white,
onPressed: _BtnClick,
),
new RaisedButton(
child: new Text("E"),
color: Colors.blue ,
textColor: Colors.white,
onPressed: _BtnClick,
),
],
)
)
),
);
}
// 按钮点击监听
void _BtnClick(){
print("不设置点击事件按钮会是灰色的!");
}
}
可灵活实现自定义需求布局,且性能较好,但是使用方式复杂
flow 官方介绍是一个对 child 尺寸以及位置调整非常高效的控件,主要是得益于其FlowDelegate。另外 flow 在用转换矩阵(transformation matrices)对child进行位置调整的时候进行了优化
Flow之所以高效,是因为其在定位过后,如果使用FlowDelegate中的paintChildren改变child的尺寸或者位置,只是重绘,并没有实际调整其位置
Flow({
Key key,
@required this.delegate,
List<Widget> children = const <Widget>[],
}) : assert(delegate != null),
super(key: key, children: RepaintBoundary.wrapAll(children));
/**
* @des Flow Widget
* @author liyongli 20190423
* */
class FlowWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() => new _FlowState();
}
/**
* @des Flow Widget State
* @author liyongli 20190423
* */
class _FlowState extends State<FlowWidget>{
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text("Flow Widget"),
),
body: new Flow(
delegate: TestFlowDelegate(margin: EdgeInsets.all(10.0)),
children: <Widget>[
new Container(width: 80.0, height:80.0, color: Colors.blue,),
new Container(width: 80.0, height:80.0, color: Colors.black,),
new Container(width: 80.0, height:80.0, color: Colors.amber,),
new Container(width: 80.0, height:80.0, color: Colors.brown,),
new Container(width: 80.0, height:80.0, color: Colors.cyanAccent,),
new Container(width: 80.0, height:80.0, color: Colors.deepOrange,),
],
),
),
);
}
}
/**
* @des Flow Widget Delegate
* @author liyongli 20190423
* */
class TestFlowDelegate extends FlowDelegate {
EdgeInsets margin = EdgeInsets.zero;
TestFlowDelegate({this.margin});
@override
void paintChildren(FlowPaintingContext context) {
var x = margin.left;
var y = margin.top;
//计算所有子 widget 位置
for (int i = 0; i < context.childCount; i++) {
var 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;
}
}
}
getSize(BoxConstraints constraints){
return Size(double.infinity,200.0);
}
@override
bool shouldRepaint(FlowDelegate oldDelegate) {
return oldDelegate != this;
}
}
stack 与 Android 中 Frame、Web 中绝对定位类似,子 widget 根据父 widget 的四个顶点确定位置。stack 布局允许子 widget 堆叠绘制
Stack({
Key key,
this.alignment = AlignmentDirectional.topStart,
this.textDirection,
this.fit = StackFit.loose,
this.overflow = Overflow.clip,
List<Widget> children = const <Widget>[],
}) : super(key: key, children: children);
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);
/**
* @des Stack Widget
* @author liyongli 20190423
* */
class StackWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() => new _StackState();
}
/**
* @des Stack Widget State
* @author liyongli 20190423
* */
class _StackState extends State<StackWidget>{
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text("Stack Widget"),
),
body: ConstrainedBox(
constraints: BoxConstraints.expand(),
child: Stack(
alignment:Alignment.center , //指定无定位或部分定位的子 widget 的对齐方式
children: <Widget>[
Container(
child: Text("我是一只小小鸟", style: new TextStyle(color: Colors.white),),
color: Colors.blue,
),
Positioned(
left: 18.0,
child: Text("想飞飞飞"),
),
Positioned(
top: 18.0,
child: Text("飞的挺高"),
)
],
),
),
),
);
}
}
此时,在原基础上给 stack 设置 fit = StackFit.expand (子 widget 没有指定定位时,此参数将指定子 widget 以怎样的方式适应 stack)
/**
* @des Stack Widget
* @author liyongli 20190423
* */
class StackWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() => new _StackState();
}
/**
* @des Stack Widget State
* @author liyongli 20190423
* */
class _StackState extends State<StackWidget>{
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text("Stack Widget"),
),
body: ConstrainedBox(
constraints: BoxConstraints.expand(),
child: Stack(
alignment:Alignment.center , //指定未定位或部分定位widget的对齐方式
fit: StackFit.expand, // expand = 未定位的子 widget 占满 stack 的可用空间
children: <Widget>[
Positioned(
left: 18.0,
child: Text("想飞飞飞"),
),
Container(
child: Text("我是一只小小鸟", style: new TextStyle(color: Colors.white),),
color: Colors.blue,
),
Positioned(
top: 18.0,
child: Text("飞的挺高"),
)
],
),
),
),
);
}
}
本篇到此完结,更多 Flutter 跨平台移动端开发 原创内容持续更新中~