前言
Flexible和 Expanded组件, 是开发者工作过程中, 经常打交道的组件。使用他们, 可以非常方便的实现flex布局。那么, 它们之间有什么联系和区别呢?接下来, 我们通过一个小demo来看一下。
假如有以下三个按钮, 横向排列。分别用两种不同的方式实现。
Expanded实现
class _APageState extends State<APage> {
@override
void initState() {
// TODO: implement initState
super.initState();
print('------a, initState');
}
@override
Widget build(BuildContext context) {
super.build(context);
print('------a----build');
return Container(
width: double.infinity,
height: double.infinity,
color: Colors.grey,
child: Center(
// child: Text('aaaaaaaaaaaaaaaaaaaaaa'),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new RaisedButton(
onPressed: () {
print('点击红色按钮事件');
},
color: const Color(0xffcc0000),
child: new Text('红色按钮'),
),
new Expanded(
flex: 1,
child: new RaisedButton(
onPressed: () {
print('点击黄色按钮事件');
},
color: const Color(0xfff1c232),
child: new Text('黄色按钮呀呀呀呀呀呀呀呀呀'),
),
),
new RaisedButton(
onPressed: () {
print('点击粉色按钮事件');
},
color: const Color(0xffea9999),
child: new Text('粉色按钮'),
),
],
),
));
}
}
效果图如下:
可以看到, 使用Expanded组件包裹中间的黄色按钮, 并设置flex: 1时, 黄色按钮撑大了, 并且整个横向布局被充满了。
那么, 使用Flexible组件会怎样呢?
Flexible实现
class APage extends StatefulWidget {
const APage({Key? key}) : super(key: key);
@override
_APageState createState() => _APageState();
}
class _APageState extends State<APage> {
@override
void initState() {
// TODO: implement initState
super.initState();
print('------a, initState');
}
@override
Widget build(BuildContext context) {
super.build(context);
print('------a----build');
return Container(
width: double.infinity,
height: double.infinity,
color: Colors.grey,
child: Center(
// child: Text('aaaaaaaaaaaaaaaaaaaaaa'),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new RaisedButton(
onPressed: () {
print('点击红色按钮事件');
},
color: const Color(0xffcc0000),
child: new Text('红色按钮'),
),
new Flexible(
flex: 1,
child: new RaisedButton(
onPressed: () {
print('点击黄色按钮事件');
},
color: const Color(0xfff1c232),
child: new Text('黄色按钮呀呀呀呀呀呀呀呀呀'),
),
),
new RaisedButton(
onPressed: () {
print('点击粉色按钮事件');
},
color: const Color(0xffea9999),
child: new Text('粉色按钮'),
),
],
),
));
}
}
效果图如下:
可以发现, 使用Flexible包裹中间的黄色按钮, 并设置flex: 1时, 整个横向布局并没有被充满。由此, 我们可以得出如下结论:
Flexible和Expanded可以使Row、Column、Flex在主轴方向有填充可用空间的能力Expanded强制子组件充满可用空间Flexible不强制组件充满可用空间
那么, 为啥会有这些区别呢? 我们去源码看看。
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);
}
可以看到, Expanded组件继承Flexible, 跟Flexible组件不同的是fit属性的设置。Expanded的fit=FlexFit.tight,Flexible的fit = FlexFit.loose。
Flexible源码
class Flexible extends ParentDataWidget<FlexParentData> {
/// Creates a widget that controls how a child of a [Row], [Column], or [Flex]
/// flexes.
const Flexible({
Key? key,
this.flex = 1,
this.fit = FlexFit.loose,
required Widget child,
}) : super(key: key, child: child);
/// The flex factor to use for this child.
///
/// If null or zero, the child is inflexible and determines its own size. If
/// non-zero, the amount of space the child's can occupy in the main axis is
/// determined by dividing the free space (after placing the inflexible
/// children) according to the flex factors of the flexible children.
final int flex;
/// How a flexible child is inscribed into the available space.
///
/// If [flex] is non-zero, the [fit] determines whether the child fills the
/// space the parent makes available during layout. If the fit is
/// [FlexFit.tight], the child is required to fill the available space. If the
/// fit is [FlexFit.loose], the child can be at most as large as the available
/// space (but is allowed to be smaller).
final FlexFit fit;
@override
void applyParentData(RenderObject renderObject) {
assert(renderObject.parentData is FlexParentData);
final FlexParentData parentData = renderObject.parentData! as FlexParentData;
bool needsLayout = false;
if (parentData.flex != flex) {
parentData.flex = flex;
needsLayout = true;
}
if (parentData.fit != fit) {
parentData.fit = fit;
needsLayout = true;
}
if (needsLayout) {
final AbstractNode? targetParent = renderObject.parent;
if (targetParent is RenderObject)
targetParent.markNeedsLayout();
}
}
@override
Type get debugTypicalAncestorWidgetClass => Flex;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(IntProperty('flex', flex));
}
}
根据解释
/// How a flexible child is inscribed into the available space.
///
/// If [flex] is non-zero, the [fit] determines whether the child fills the
/// space the parent makes available during layout. If the fit is
/// [FlexFit.tight], the child is required to fill the available space. If the
/// fit is [FlexFit.loose], the child can be at most as large as the available
/// space (but is allowed to be smaller).
final FlexFit fit;
关键区别在这句话but is allowed to be smaller
所以Expanded是会强制占剩余空间、Flexible是允许更小的,如果实际内容没有那么大就按实际大小展示.