Flexible和 Expanded的区别

944 阅读3分钟

前言

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('粉色按钮'),
              ),
            ],
          ),
        ));
  }
}

效果图如下:

Simulator Screen Shot - iPhone 12 Pro Max - 2021-12-09 at 19.30.34.png

可以看到, 使用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('粉色按钮'),
              ),
            ],
          ),
        ));
  }
}

效果图如下:

Simulator Screen Shot - iPhone 12 Pro Max - 2021-12-09 at 19.34.19.png

可以发现, 使用Flexible包裹中间的黄色按钮, 并设置flex: 1时, 整个横向布局并没有被充满。由此, 我们可以得出如下结论:

  • FlexibleExpanded可以使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属性的设置。Expandedfit=FlexFit.tight,Flexiblefit = 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是允许更小的,如果实际内容没有那么大就按实际大小展示.