1.Stack
Stack 这个是Flutter中布局用到的组件,可以叠加的现实View.
Stack({
Key key,
this.alignment = AlignmentDirectional.topStart,
this.textDirection,
this.fit = StackFit.loose,
this.overflow = Overflow.clip,
List<Widget> children = const <Widget>[],
})
- alignment : 指的是子Widget的对其方式,默认情况是以左上角为开始点 ,这个属性是最难理解的,它区分为使用了Positioned和未使用Positioned定义两种情况,没有使用Positioned情况还是比较好理解的.
- fit :用来决定没有Positioned方式时候子Widget的大小,StackFit.loose 指的是子Widget 多大就多大,StackFit.expand使子Widget的大小和父组件一样大.
- overflow :指子Widget 超出Stack时候如何显示,默认值是Overflow.clip,子Widget超出Stack会被截断,Overflow.visible超出部分还会显示的.
stack组件的使用
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'title',
home: Scaffold(
appBar: AppBar(title: Text('title'),),
body: Stack(
children: <Widget>[
Container(width: 300,height: 300,color: Colors.cyan,),
Container(width: 200,height: 200,color: Colors.red,),
Container(width: 100,height: 100,color: Colors.yellow,),
],
),
),
);
}
}
fit 属性使用
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'title',
home: Scaffold(
appBar: AppBar(
title: Text("stack title"),
actions: <Widget>[
RaisedButton(
onPressed: () {
},
color: Colors.blue,
child: Icon(Icons.add),
),
],
),
body: Stack(
fit: StackFit.expand,
children: <Widget>[
Container(
width: 100,
height: 100,
color: Colors.red,
),
Container(
width: 90,
height: 90,
color: Colors.blue,
),
Container(
width: 80,
height: 80,
color: Colors.green,
),
],
),
),
);
}
}
如果指定是StackFit.expand,所以的子组件会和Stack一样大的.
如果指定是StackFit.loose,所以子Widget 多大就多大.
2. Position
这个使用控制Widget的位置,通过他可以随意摆放一个组件,有点像绝对布局.
Positioned({
Key key,
this.left,
this.top,
this.right,
this.bottom,
this.width,
this.height,
@required Widget child,
})
left、top 、right、 bottom分别代表离Stack左、上、右、底四边的距离
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'title',
home: Scaffold(
appBar: AppBar(
title: Text("Postion Title"),
),
body: Stack(
children: <Widget>[
Positioned(
top: 100.0,
child: Container(
color: Colors.blue,
child: Text("第一个组件"),
),
),
Positioned(
top: 200,
right: 100,
child: Container(
color: Colors.yellow,
child: Text("第二个组件"),
),
),
Positioned(
left: 100.0,
child: Container(
color: Colors.red,
child: Text("第三个组件"),
),
),
],
),
),
);
}
}
这个地方有注意地方,例如说第一个组件我指定距离左边0个距离,距离右边0个距离,那么这个组件的宽度就是屏幕这么宽,因为你指定的左右间距都是0,也就是没有间距.
如果指定了left&&right&&top&bottom都是0的情况,那么这个组件就是和Stack大小一样填满它.
3. GridTile
GridTile 是一个Flutter 提供的组件的,用来在GridView中给Item 增加更丰富的展示用的,GridTile 的布局方式就是Stack,在源代码中就到Positioned 来进行位置控制,主要提供三个Widget的展示分别为child、header、footer,我们看一下源代码:
class GridTile extends StatelessWidget {
/// Creates a grid tile.
///
/// Must have a child. Does not typically have both a header and a footer.
const GridTile({
Key key,
this.header,
this.footer,
@required this.child,
}) : assert(child != null),
super(key: key);
/// The widget to show over the top of this grid tile.
///
/// Typically a [GridTileBar].
final Widget header;
/// The widget to show over the bottom of this grid tile.
///
/// Typically a [GridTileBar].
final Widget footer;
/// The widget that fills the tile.
///
/// {@macro flutter.widgets.child}
final Widget child;
@override
Widget build(BuildContext context) {
if (header == null && footer == null)
return child;
final List<Widget> children = <Widget>[
Positioned.fill(
child: child,
),
];
if (header != null) {
children.add(Positioned(
top: 0.0,
left: 0.0,
right: 0.0,
child: header,
));
}
if (footer != null) {
children.add(Positioned(
left: 0.0,
bottom: 0.0,
right: 0.0,
child: footer,
));
}
return Stack(children: children);
}
}
4. alignment
A.没有使用postioned定位的情况:
在第一个stack组件的代码中使用的就是positioned定位的情况,默认的子组件的对齐方式就是以左上角为基准开始排列的,我们再来了解一下其他的对齐方式.
①.AlignmentDirectional.bottomEnd
AlignmentDirectional.bottomEnd的方式是从右下角为基准开始对齐:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'title',
home: Scaffold(
appBar: AppBar(
title: Text("Postion Title"),
),
body: Stack(
// fit: StackFit.expand,
alignment: AlignmentDirectional.bottomEnd,
children: <Widget>[
Container(
width: 100,
height: 100,
color: Colors.red,
),
Container(
width: 90,
height: 90,
color: Colors.blue,
),
Container(
width: 80,
height: 80,
color: Colors.green,
),
]),
),
);
}
}
②.AlignmentDirectional.topEnd
AlignmentDirectional.bottomEnd的方式是从左上角为基准开始对齐:
③.AlignmentDirectional.center
AlignmentDirectional.center的方式是以stack的中心位置:
④.AlignmentDirectional.centerEnd
所有的Widget 在Stack的中心位置并且右边跟stack右边挨着
⑤.AlignmentDirectional.centerStart
所有的Widget 在Stack的中心位置并且左边跟stack左边挨着
B.使用postioned定位的情况:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'title',
home: Scaffold(
appBar: AppBar(
title: Text("Postion Title"),
),
body: Stack(
// alignment: AlignmentDirectional.bottomEnd,
overflow: Overflow.visible,
children: <Widget>[
Positioned.fill(
child: Container(
color: Colors.black45,
),
),
Positioned(
top: 100.0,
left: 0,
right: 20,
child: Container(
color: Colors.blue,
child: Text("第一个组件"),
),
),
Positioned(
top: 200,
bottom: 20,
child: Container(
color: Colors.yellow,
child: Text("第二个组件"),
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
color: Colors.red,
child: Text("第三个组件"),
),
),
],
),
),
);
}
}
这种情况是alignment 是默认值的效果,下面我们修改一下alignment的对应的值.
①.AlignmentDirectional.bottomEnd
bottomEnd是子Widget的底部和Stack底部对齐,并且子Widget的右边和Stack右边对齐.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'title',
home: Scaffold(
appBar: AppBar(
title: Text("Postion Title"),
),
body: Stack(
alignment: AlignmentDirectional.bottomEnd,
overflow: Overflow.visible,
children: <Widget>[
Positioned.fill(
child: Container(
color: Colors.black45,
),
),
Positioned(
top: 100.0,
left: 0,
right: 20,
child: Container(
color: Colors.blue,
child: Text("第一个组件"),
),
),
Positioned(
top: 200,
bottom: 20,
child: Container(
color: Colors.yellow,
child: Text("第二个组件"),
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
color: Colors.red,
child: Text("第三个组件"),
),
),
],
),
),
);
}
}
会发现这个图的效果和上一个图的效果唯一区别就是黄色的第二个组件的位置有变化,这是为什么呢?
先说第一个组件和第三组件的位置为什么没有改变:
Positioned(
top: 100.0,
left: 0,
right: 20,
child: Container(
color: Colors.blue,
child: Text("第一个组件"),
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
color: Colors.red,
child: Text("第三个组件"),
),
),
第一个组件top是100,说明这个组件距离顶部的距离是固定的,虽然Stack的aligment=AlignmentDirectional.bottomEnd,是不生效的,当这两个属性冲突时,以Positioned的距离为主,为什么第一组件右边也没有Stack的右边对齐呢?因为right=20,第一个组件右边距离已经可以确认了,所以也不受到aligment=AlignmentDirectional.bottomEnd的影响.
第三个组件也是一样的,第三个组件的宽度是Stack的宽度,高度取决于Text组件的高度,最关键的是它的bottom=0,也就是第三个组件要和Stack组件的低边界对齐,所以它的效果和上面的图是没有变化的.
Positioned(
top: 200,
bottom: 20,
child: Container(
color: Colors.yellow,
child: Text("第二个组件"),
),
),
第二个组件为什么会跑到右边呢?
因为第二个组件的高度是可以确认出来的,top=200,bottom=20,设置这两个属性就能推断出第二组的高度是多大,但是第二个组件的宽度取决于Text("第二个组件") 的宽度,显然是水平方向上是不能填满Stack的,这个时候AlignmentDirectional.bottomEnd属性起作用了,bottom的距离已经确定了,所以底部的对齐方式是不会变化了,但是第二组件右边的对齐方式是可以收到AlignmentDirectional.bottomEnd影响的,所以第二组件展示的位置就是图片上展示的位置.