Flutter入门08 -- Widget之多个子元素的布局类
2022-02-22 本文已影响0人
YanZi_33
- 涉及多个子元素布局类的Widget有:Flex,Row、Column、Stack、IndexedStack、Wrap、Flow、Table、ListBody、CustomMultiChildLayout、LayoutBuilder、Flexible,Expanded
Flex(弹性布局)
-
Flex(弹性布局)
:内部按照一定的方式排列子组件,是Row与Column的基类
,其构造函数如下:
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,
this.clipBehavior = Clip.hardEdge,
List<Widget> children = const <Widget>[],
})
-
direction
:内部子组件的排列方向,属于Axis
类型,其值有如下:- Axis.horizontal:水平方向排布,与Row等价;
- Axis.vertical:垂直方向排布,与Column等价;
-
children
:child子组件数组; -
mainAxisAlignment
:表示在主轴方向上的child对齐方式,属于MainAxisAlignment
类型,具体值有如下:- start:主轴的开始位置依次摆放元素;
- end:主轴的结束位置依次摆放元素;
- center:主轴中心点对齐;
- spaceBetween:左右间距为0,其他元素之间间距平分;
- spaceAround:左右间距是其他元素之间间距的一半;
- spaceEvenly:所有间距平分;
-
crossAxisAlignment
:表示在交叉轴方向上的child对齐方式,属于CrossAxisAlignment
类型,具体值有如下:- start:交叉轴的开始位置依次摆放元素;
- end:交叉轴的结束位置依次摆放元素;
- center:交叉轴中心点对齐;
- textBaseline:基线对齐(必须有文本内容才有效果);
- stretch:将所有子元素,拉伸到最大;
-
mainAxisSize
:控制Flex在主轴方向的尺寸大小,属于MainAxisSize
类型,有两种数值;- MainAxisSize.min:主轴方向上自适应子child的大小;
- MainAxisSize.min:主轴方向上占据整个屏幕大小;
-
textBaseline
:文本内容按照基线进行对齐; - 案例代码如下:
import 'package:flutter/material.dart';
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: SFHomePage(),
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("Flutter布局"),
),
body: SFHomeBody(),
);
}
}
class SFHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
color: Colors.orange,
child: Flex(
children: [
Container(
width: 50,
height: 50,
color: Colors.green,
),
Container(
width: 50,
height: 60,
color: Colors.green,
),
Container(
width: 50,
height: 70,
color: Colors.green,
),
Container(
width: 50,
height: 80,
color: Colors.green,
)
],
direction: Axis.horizontal, //指明主轴方向为水平方向 与Row等价
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
)
);
}
}
- 效果图如下:
Row(水平布局)
-
Row(水平布局)
:在水平方向上,排列子组件,其主轴方向为水平方向
,构造函数如下:
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>[],
})
- 案例代码如下:
import 'package:flutter/material.dart';
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: SFHomePage(),
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("Flutter布局"),
),
body: SFHomeBody(),
);
}
}
class SFHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
color: Colors.orange,
child: Row(
children: [
Container(
width: 50,
height: 60,
color: Colors.green,
),
Container(
width: 50,
height: 70,
color: Colors.green,
),
Container(
width: 50,
height: 80,
color: Colors.green,
),
Container(
width: 50,
height: 90,
color: Colors.green,
)
],
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
textBaseline: TextBaseline.alphabetic,
),
);
}
}
- 效果图如下:
Column
-
Column:在垂直方向上,排列子组件,其·
主轴方向为垂直方向`,构造方法与Row(水平布局)完全一样,主要区别在于主轴方向的不同;
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>[],
})
- 案例代码如下:
import 'package:flutter/material.dart';
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: SFHomePage(),
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("Flutter布局"),
),
body: SFHomeBody(),
);
}
}
class SFHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
color: Colors.orange,
child: Column(
children: [
Container(
width: 50,
height: 50,
color: Colors.green,
),
Container(
width: 60,
height: 50,
color: Colors.green,
),
Container(
width: 70,
height: 50,
color: Colors.green,
),
Container(
width: 80,
height: 50,
color: Colors.green,
)
],
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
textBaseline: TextBaseline.alphabetic,
),
);
}
}
- 效果图如下:
- 如何给
Row和Column设置背景颜色
,可给Row和Column外层套一个Container组件
,Container在不设置宽高的情况下,其内容大小就是内部子child的尺寸大小;
Flexible与Expanded
- Flexible是Expanded的父类,这两者可以
让Row、Column、Fiex等子组件在其主轴上方向展开
并填充可用的剩余空间
; - Flexible的构造方法如下:
const Flexible({
Key key,
this.flex = 1,
this.fit = FlexFit.loose,
@required Widget child,
})
- Expanded的构造方法如下:
class Expanded extends Flexible {
/// Creates a widget that expands a child of a [Row], [Column], or [Flex]
/// so that the child fills the available space along the flex widget's
/// main axis.
const Expanded({
Key key,
int flex = 1,
@required Widget child,
}) : super(key: key, flex: flex, fit: FlexFit.tight, child: child);
}
- 可以看出Flexible与Expanded的区别在于属性
fit
的设值不同,FlexFit.tight
和FlexFit.loose
- Flexible,
FlexFit.loose
,尽可能大的填满剩余空间,但是可以不填满; - Expanded,
FlexFit.tight
,必须(强制)填满剩余空间; - Flexible与Expanded必须使用在Row、Column、Fiex的内部;
-
flex
:是针对剩余空间的分配比例; - 案例代码如下:
import 'package:flutter/material.dart';
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: SFHomePage(),
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("Flutter布局"),
),
body: SFHomeBody(),
);
}
}
class SFHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
color: Colors.orange,
child: Row(
children: [
Container(
width: 50,
height: 50,
color: Colors.green,
),
Container(
width: 50,
height: 60,
color: Colors.green,
),
Flexible(
flex: 1,
child: Container(height: 120,color: Colors.red),
),
Expanded(
flex: 1,
child: Container(height: 80,color: Colors.green),
),
Expanded(
flex: 1,
child: Container(
height: 100,
color: Colors.purple,
),
)
],
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
)
);
}
}
- 效果图如下:
Stack组件
- 在开发中,我们多个组件可能需要
重叠显示
,在Android中使用Frame布局
实现,而在Flutter中我们需要使用层叠布局Stack
来实现; - 内部子组件默认从左上角开始排布;
- Stack默认的大小是包裹内容的尺寸大小;
- 构造函数如下:
Stack({
Key key,
this.alignment = AlignmentDirectional.topStart,
this.textDirection,
this.fit = StackFit.loose,
this.overflow = Overflow.clip,
this.clipBehavior = Clip.hardEdge,
List<Widget> children = const <Widget>[],
}) : assert(clipBehavior != null), super(key: key, children: children);
-
children
:内部子组件数组; -
alignment
:表示从什么位置开始排布所有的子组件,只作用于没有使用Positioned或部分定位的子组件,属于Alignment
类型; -
fit
:子组件如何去适应Stack的大小,只作用于没有使用Positioned或部分定位的子组件,属于StackFit
类型;- StackFit.expand:扩伸到Stack的大小,只作用于没有使用Positioned的子组件;
- StackFit.loose:使用子组件的大小,只作用于没有使用Positioned的子组件;
- StackFit.passthrough:传递使用Stack的父级约束;
-
overflow
:对于超出父组件区域的子组件的显示处理,属于Overflow
类型;- Overflow.flip:超出部分被裁减;
- Overflow.visible:超出部分依然显示;
- 案例代码如下:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: SFHomePage());
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("基础widget")), body: SFHomeContent());
}
}
class SFHomeContent extends StatefulWidget {
@override
State<StatefulWidget> createState() {
print("createState");
return _SFHomeContentState();
}
}
class _SFHomeContentState extends State<SFHomeContent> {
@override
Widget build(BuildContext context) {
return StackDemo1();
}
}
class StackDemo2 extends StatelessWidget {
const StackDemo2({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
children: [
Container(
height: 200,
child: Image.asset("asset/images/180.png", fit: BoxFit.fill),
width: double.infinity),
Positioned(
child: Container(
child: Row(
children: [
Text("这是一行文本", style: TextStyle(fontSize: 17, color: Colors.white)),
IconButton(icon: Icon(Icons.favorite),color: Colors.white,onPressed: (){
})
],
mainAxisAlignment: MainAxisAlignment.spaceBetween,
),
color: Color.fromARGB(150, 0, 0, 0),
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 8),
),
left: 0,
right: 0,
bottom: 0,
)
],
);
}
}
class StackDemo1 extends StatelessWidget {
const StackDemo1({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
children: [
Image.asset("asset/images/180.png"),
Positioned(
child: Container(
width: 100,
height: 100,
color: Colors.green,
),
right: 0,
),
Positioned(
child: Text("推客图标", style: TextStyle(fontSize: 20)),
left: 0,
)
],
alignment: AlignmentDirectional.bottomStart,
overflow: Overflow.visible,
);
}
}
- StackDemo1与StackDemo2的效果图如下:
IndexedStack
-
IndexedStack
:层叠布局,继承自Stack,其构造函数如下:
IndexedStack({
Key key,
AlignmentGeometry alignment = AlignmentDirectional.topStart,
TextDirection textDirection,
StackFit sizing = StackFit.loose,
this.index = 0,
List<Widget> children = const <Widget>[],
})
-
alignment
:属于AlignmentDirectional
类型; -
textDirection
:文本方向; -
sizing
:子组件如何去适应IndexedStack的大小,就是Stack的fit属性,但是在这里sizing属性不会对子组件大小有影响; -
index
= 0:显示第几个子组件; -
children
:内部子组件数组; -
IndexedStack只能够显示 children列表 中单个子组件
,默认第0个,可切换显示;
Wrap
-
Wrap
:可实现流式布局,比如商品列表,瀑布流、标签页等等; - 子组件排列时,超出屏幕的宽高后,会自动换行,换列;
- 其构造函数如下:
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,
this.clipBehavior = Clip.hardEdge,
List<Widget> children = const <Widget>[],
})
-
children
:子组件数组; -
direction
:主轴方向,默认水平方向; -
spacing
:主轴方向每个 item之间的间距; -
runSpacing
:交叉轴方向item之间的间距; - 案例代码如下:
import 'package:YYShop/core/extension/color_extesion.dart';
import 'package:YYShop/ui/common/appsize_fit.dart';
import 'package:flutter/material.dart';
class SFMinePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Wrap(
direction: Axis.horizontal,
children: createItems(),
spacing: 5,
runSpacing: 10,
);
}
List<Widget> createItems() {
List<Widget> widgets = new List();
for (int i = 0; i < 10; i++) {
widgets.add(SFItem());
}
return widgets;
}
}
class SFItem extends StatelessWidget {
const SFItem({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
double width = (SFAppSizeFit.screenWidth - 5) / 2;
return Container(
width: width,
height: 100,
color: SFColor.randomColor(),
);
}
}
- 效果图如下: