前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter 布局备忘录 -- 多图警告,干货建议收藏

Flutter 布局备忘录 -- 多图警告,干货建议收藏

作者头像
Jimmy_is_jimmy
发布2022-08-30 15:22:20
2.8K0
发布2022-08-30 15:22:20
举报
文章被收录于专栏:call_me_R

你是否需要了解 Flutter 布局的案例?

这里我将展示我在使用 Flutter 布局的代码片段。我将通过精美的代码片段结合可视化的图形来举例。

本文注重 Flutter 部件中比较有用的一些来展示,而不是走马观花展示一大推的部件内容。

本文是翻译的文章,采用意译的方式

Row and Column

行(Row)和列(Column)的布局

MainAxisAlignment
MainAxisAlignment_row.png
MainAxisAlignment_row.png

|

MainAxisAlignment_column.png
MainAxisAlignment_column.png
代码语言:javascript
复制
Row /*or Column*/(
  mainAxisAlignment: MainAxisAlignment.start,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),

MainAxisAlignment_center_row.png
MainAxisAlignment_center_row.png

|

MainAxisAlignment_center_column.png
MainAxisAlignment_center_column.png
代码语言:javascript
复制
Row /*or Column*/(
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),

MainAxisAlignment_end_row.png
MainAxisAlignment_end_row.png

|

MainAxisAlignment_end_column.png
MainAxisAlignment_end_column.png
代码语言:javascript
复制
Row /*or Column*/(
  mainAxisAlignment: MainAxisAlignment.end,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),

MainAxisAlignment_spaceBetween_row.png
MainAxisAlignment_spaceBetween_row.png

|

MainAxisAlignment_spaceBetween_column.png
MainAxisAlignment_spaceBetween_column.png
代码语言:javascript
复制
Row /*or Column*/(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),

MainAxisAlignment_spaceEvenly_row.png
MainAxisAlignment_spaceEvenly_row.png

|

MainAxisAlignment_spaceEvenly_column.png
MainAxisAlignment_spaceEvenly_column.png
代码语言:javascript
复制
Row /*or Column*/(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),

MainAxisAlignment_spaceAround_row.png
MainAxisAlignment_spaceAround_row.png

|

MainAxisAlignment_spaceAround_column.png
MainAxisAlignment_spaceAround_column.png
代码语言:javascript
复制
Row /*or Column*/(
  mainAxisAlignment: MainAxisAlignment.spaceAround,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),

CrossAxisAlignment.baseline.png
CrossAxisAlignment.baseline.png

如果你需要文本是针对基线对齐,那么你应该使用 CrossAxisAlignment.baseline

代码语言:javascript
复制
Row(
  crossAxisAlignment: CrossAxisAlignment.baseline,
  textBaseline: TextBaseline.alphabetic,
  children: <Widget>[
    Text(
      'Baseline',
      style: Theme.of(context).textTheme.headline2,
    ),
    Text(
      'Baseline',
      style: Theme.of(context).textTheme.bodyText2,
    ),
  ],
),

CrossAxisAlignment.start_row.png
CrossAxisAlignment.start_row.png

|

CrossAxisAlignment.start_column.png
CrossAxisAlignment.start_column.png
代码语言:javascript
复制
Row /*or Column*/(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 200),
    Icon(Icons.star, size: 50),
  ],
),

CrossAxisAlignment.center_row.png
CrossAxisAlignment.center_row.png

|

CrossAxisAlignment.center_column.png
CrossAxisAlignment.center_column.png
代码语言:javascript
复制
Row /*or Column*/(
  crossAxisAlignment: CrossAxisAlignment.center,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 200),
    Icon(Icons.star, size: 50),
  ],
),

CrossAxisAlignment.end_row.png
CrossAxisAlignment.end_row.png

|

CrossAxisAlignment.end_column.png
CrossAxisAlignment.end_column.png
代码语言:javascript
复制
Row /*or Column*/(
  crossAxisAlignment: CrossAxisAlignment.end,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 200),
    Icon(Icons.star, size: 50),
  ],
),

CrossAxisAlignment.stretch_row.png
CrossAxisAlignment.stretch_row.png

|

CrossAxisAlignment.stretch_column.png
CrossAxisAlignment.stretch_column.png
代码语言:javascript
复制
Row /*or Column*/(
  crossAxisAlignment: CrossAxisAlignment.stretch,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 200),
    Icon(Icons.star, size: 50),
  ],
),

MainAxisSize.max_row.png
MainAxisSize.max_row.png

|

MainAxisSize.max_column.png
MainAxisSize.max_column.png
代码语言:javascript
复制
Row /*or Column*/(
  mainAxisSize: MainAxisSize.max,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),

MainAxisSize.min_row.png
MainAxisSize.min_row.png

|

MainAxisSize.min_column.png
MainAxisSize.min_column.png
代码语言:javascript
复制
Row /*or Column*/(
  mainAxisSize: MainAxisSize.min,
  children: <Widget>[
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),

IntrinsicWidth and IntrinsicHeight

在行列布局中,如何使得所有的部件跟宽度/高度最大的部件同宽/同高呢?如下:

我们假有下面的布局:

IntrinsicWidth_init.png
IntrinsicWidth_init.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('IntrinsicWidth')),
    body: Center(
      child: Column(
        children: <Widget>[
          RaisedButton(
            onPressed: () {},
            child: Text('Short'),
          ),
          RaisedButton(
            onPressed: () {},
            child: Text('A bit Longer'),
          ),
          RaisedButton(
            onPressed: () {},
            child: Text('The Longest text button'),
          ),
        ],
      ),
    ),
  );
}

那么,你想所有的按钮的宽度都跟最宽的按钮那么宽,那就使用IntrinsicWidth

代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('IntrinsicWidth')),
    body: Center(
      child: IntrinsicWidth(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            RaisedButton(
              onPressed: () {},
              child: Text('Short'),
            ),
            RaisedButton(
              onPressed: () {},
              child: Text('A bit Longer'),
            ),
            RaisedButton(
              onPressed: () {},
              child: Text('The Longest text button'),
            ),
          ],
        ),
      ),
    ),
  );
}

同理,如果你想所有的部件的高度跟最高的部件一样高,你需要结合 IntrinsicHeightRow 来实现。

Stack

Stack 很适合小部件相互叠加。

Stack_01.png
Stack_01.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  Widget main = Scaffold(
    appBar: AppBar(title: Text('Stack')),
  );

  return Stack(
    fit: StackFit.expand,
    children: <Widget>[
      main,
      Banner(
        message: "Top Start",
        location: BannerLocation.topStart,
      ),
      Banner(
        message: "Top End",
        location: BannerLocation.topEnd,
      ),
      Banner(
        message: "Bottom Start",
        location: BannerLocation.bottomStart,
      ),
      Banner(
        message: "Bottom End",
        location: BannerLocation.bottomEnd,
      ),
    ],
  );
}

使用自己的部件,你需要将它们放在Positioned部件中。

Stack_02.png
Stack_02.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Stack')),
    body: Stack(
      fit: StackFit.expand,
      children: <Widget>[
        Material(color: Colors.yellowAccent),
        Positioned(
          top: 0,
          left: 0,
          child: Icon(Icons.star, size: 50),
        ),
        Positioned(
          top: 340,
          left: 250,
          child: Icon(Icons.call, size: 50),
        ),
      ],
    ),
  );
}

如果你不想猜测顶部/底部的值,你可以使用 LayoutBuilder 部件来检索它们的值。

Stack_03.png
Stack_03.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  const iconSize = 50;
  return Scaffold(
    appBar: AppBar(title: Text('Stack with LayoutBuilder')),
    body: LayoutBuilder(
      builder: (context, constraints) =>
        Stack(
          fit: StackFit.expand,
          children: <Widget>[
            Material(color: Colors.yellowAccent),
            Positioned(
              top: 0,
              child: Icon(Icons.star, size: iconSize),
            ),
            Positioned(
              top: constraints.maxHeight - iconSize,
              left: constraints.maxWidth - iconSize,
              child: Icon(Icons.call, size: iconSize),
            ),
          ],
        ),
    ),
  );
}

Expanded

Expanded 配合 Flex\Flexbox 布局实现,它对于多项目分配空间很棒。

Expanded.png
Expanded.png
代码语言:javascript
复制
Row(
  children: <Widget>[
    Expanded(
      child: Container(
        decoration: const BoxDecoration(color: Colors.red),
      ),
      flex: 3,
    ),
    Expanded(
      child: Container(
        decoration: const BoxDecoration(color: Colors.green),
      ),
      flex: 2,
    ),
    Expanded(
      child: Container(
        decoration: const BoxDecoration(color: Colors.blue),
      ),
      flex: 1,
    ),
  ],
),

ConstrainedBox

默认的,很多部件多尽量使用小空间,比如:

no_constraints.png
no_constraints.png
代码语言:javascript
复制
Card(
  child: const Text('Hello World!'),
  color: Colors.yellow,
),

ConstrainedBox允许小部件根据需要使用剩下的空间。

BoxConstraints.expand.png
BoxConstraints.expand.png
代码语言:javascript
复制
ConstrainedBox(
  constraints: BoxConstraints.expand(),
  child: const Card(
    child: const Text('Hello World!'),
    color: Colors.yellow,
  ),
),

使用 BoxConstraints,你可以指定一个小部件可以有多少空间,你可以指定高度/宽度的最小/最大值。

除非指定值,否则 BoxConstraints.expand 使用无限的空间量(也就是使用剩下的所有空间):

expand_height_specify.png
expand_height_specify.png
代码语言:javascript
复制
ConstrainedBox(
  constraints: BoxConstraints.expand(height: 300),
  child: const Card(
    child: const Text('Hello World!'),
    color: Colors.yellow,
  ),
),

上面👆的写法等同下面👇的写法:

代码语言:javascript
复制
ConstrainedBox(
  constraints: BoxConstraints(
    minWidth: double.infinity,
    maxWidth: double.infinity,
    minHeight: 300,
    maxHeight: 300,
  ),
  child: const Card(
    child: const Text('Hello World!'),
    color: Colors.yellow,
  ),
),

Align

有时候,我们很难设置我们的小部件到正确的大小 -- 比如,它们自由伸展,但是这不是你想要的。

without_align.png
without_align.png

当你在 Column 中使用 CrossAxisAlignment.stretch 的时候,上面的现象就会发生,而你想要的是这个按钮不伸展。

with_align.png
with_align.png
代码语言:javascript
复制
Column(
  crossAxisAlignment: CrossAxisAlignment.stretch,
  children: <Widget>[
    Align(
      child: RaisedButton(
        onPressed: () {},
        child: const Text('Button'),
      ),
    ),
  ],
),

当你的小部件并不受限你设定的约束时,那么你可以尝试使用 Align 部件包裹它。

Container

Container 是最常用的部件之一 -- 有如下的好处:

Container as a layout tool

当你没有指定 Container 的高度 height 或者宽度 width 的时候,它会自动适配 child 子部件的大小。

container_as_a_layout.png
container_as_a_layout.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Container as a layout')),
    body: Container(
      color: Colors.yellowAccent,
      child: Text("Hi"),
    ),
  );
}

如果你想伸展Container来适配它的父部件,请为属性高度height或宽度width设定值double.infinity

double_infinity.png
double_infinity.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Container as a layout')),
    body: Container(
      height: double.infinity,
      width: double.infinity,
      color: Colors.yellowAccent,
      child: Text("Hi"),
    ),
  );
}

Container as decoration

你可以使用 Containercolor 属性来更改其背景颜色,但是你也可以使用 decorationforegroundDecoration 来更改。(使用这两个属性,你完全可以更改 Container 的样子,这个我们迟点说)。

decoration 总是在 child 属性的后面,而 foregroundDecoration 总是在 child 属性的后面。(这也不一定)

container_decoration.png
container_decoration.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Container.decoration')),
    body: Container(
      height: double.infinity,
      width: double.infinity,
      decoration: BoxDecoration(color: Colors.yellowAccent),
      child: Text("Hi"),
    ),
  );
}

container_foreGroundDecoration.png
container_foreGroundDecoration.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Container.foregroundDecoration')),
    body: Container(
      height: double.infinity,
      width: double.infinity,
      decoration: BoxDecoration(color: Colors.yellowAccent),
      foregroundDecoration: BoxDecoration(
        color: Colors.red.withOpacity(0.5),
      ),
      child: Text("Hi"),
    ),
  );
}
Container as Transform

如果你不想使用 Transform 部件来更改布局,你可以直接使用 Container 中的 transform 属性。

container_transform.png
container_transform.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Container.transform')),
    body: Container(
      height: 300,
      width: 300,
      transform: Matrix4.rotationZ(pi / 4),
      decoration: BoxDecoration(color: Colors.yellowAccent),
      child: Text(
        "Hi",
        textAlign: TextAlign.center,
      ),
    ),
  );
}

BoxDecoration

decoration 通常用于更改 Container 部件的外观。

image: DecorationImage

图片作为背景:

DecorationImage.png
DecorationImage.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('image: DecorationImage')),
    body: Center(
      child: Container(
        height: 200,
        width: 200,
        decoration: BoxDecoration(
          color: Colors.yellow,
          image: DecorationImage(
            fit: BoxFit.fitWidth,
            image: NetworkImage(
              'https://flutter.dev/images/catalog-widget-placeholder.png', // 地址已经无效
            ),
          ),
        ),
      ),
    ),
  );
}
border: Border

指定 Container 的边框看起来该怎样。

border_Border.png
border_Border.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('border: Border')),
    body: Center(
      child: Container(
        height: 200,
        width: 200,
        decoration: BoxDecoration(
          color: Colors.yellow,
          border: Border.all(color: Colors.black, width: 3),
        ),
      ),
    ),
  );
}
borderRadius: BorderRadius

使得边框角变圆。

如果装饰中 shape 属性的值是 BoxShape.circle,那么 borderRadius 不会起作用。

border_radius.png
border_radius.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('borderRadius: BorderRadius')),
    body: Center(
      child: Container(
        height: 200,
        width: 200,
        decoration: BoxDecoration(
          color: Colors.yellow,
          border: Border.all(color: Colors.black, width: 3),
          borderRadius: BorderRadius.all(Radius.circular(18)),
        ),
      ),
    ),
  );
}
shape: BoxShape

BoxDecoration 可以是矩形/正方形或者椭圆/圆形。

对于其他形状,你可以使用 ShapeDecoration 代替 BoxDecoration

Border_Radius_borderRadius.png
Border_Radius_borderRadius.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('shape: BoxShape')),
    body: Center(
      child: Container(
        height: 200,
        width: 200,
        decoration: BoxDecoration(
          color: Colors.yellow,
          shape: BoxShape.circle,
        ),
      ),
    ),
  );
}
boxShadow: List

Container 添加阴影。

这个参数是一个列表,你可以指定多个不同的阴影并将它们合并在一起。

boxShadow_list.png
boxShadow_list.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('boxShadow: List<BoxShadow>')),
    body: Center(
      child: Container(
        height: 200,
        width: 200,
        decoration: BoxDecoration(
          color: Colors.yellow,
          boxShadow: const [
            BoxShadow(blurRadius: 10),
          ],
        ),
      ),
    ),
  );
}
gradient

有三种类型的渐变:LinearGradientRadialGradientSweepGradient

LinearGradient.png
LinearGradient.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('gradient: LinearGradient')),
    body: Center(
      child: Container(
        height: 200,
        width: 200,
        decoration: BoxDecoration(
          gradient: LinearGradient(
            colors: const [
              Colors.red,
              Colors.blue,
            ],
          ),
        ),
      ),
    ),
  );
}

RadialGradient.png
RadialGradient.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('gradient: RadialGradient')),
    body: Center(
      child: Container(
        height: 200,
        width: 200,
        decoration: BoxDecoration(
          gradient: RadialGradient(
            colors: const [Colors.yellow, Colors.blue],
            stops: const [0.4, 1.0],
          ),
        ),
      ),
    ),
  );
}

SweepGradient.png
SweepGradient.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('gradient: SweepGradient')),
    body: Center(
      child: Container(
        height: 200,
        width: 200,
        decoration: BoxDecoration(
          gradient: SweepGradient(
            colors: const [
              Colors.blue,
              Colors.green,
              Colors.yellow,
              Colors.red,
              Colors.blue,
            ],
            stops: const [0.0, 0.25, 0.5, 0.75, 1.0],
          ),
        ),
      ),
    ),
  );
}

backgroundBlendMode

backgroundBlendModeBoxDecoration 中最复杂的属性之一。

它负责将 BoxDecoration 中颜色/渐变,以及 BoxDecoration 上的任何内容混合一起。

使用 backgroundBlendMode, 你可以使用 BlendMode 枚举中指定的一长串算法。

首先,让我们将 BoxDecoration 设置为 foregroundDecoration,它被绘制在 Container 子部件之上(而 decoration 会绘制在子部件之后)。

backgroundBlendMode01.png
backgroundBlendMode01.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('backgroundBlendMode')),
    body: Center(
      child: Container(
        height: 200,
        width: 200,
        foregroundDecoration: BoxDecoration(
          backgroundBlendMode: BlendMode.exclusion,
          gradient: LinearGradient(
            colors: const [
              Colors.red,
              Colors.blue,
            ],
          ),
        ),
        child: Image.network(
          'https://flutter.io/images/catalog-widget-placeholder.png', // 图片 404
        ),
      ),
    ),
  );
}

backgroundBlendMode 不仅仅影响它所在的 Container

backgroundBlendMode 改变其所在 Container 及其一下部件树的内容的颜色。

下面的代码又一个父部件 Container 来绘制一个 image,然后有一个子部件 Container 来使用 backgroundBlendMode,但是你还是获取到和之前的效果。

backgroundBlendMode02.png
backgroundBlendMode02.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('backgroundBlendMode')),
    body: Center(
      child: Container(
        decoration: BoxDecoration(
          image: DecorationImage(
            image: NetworkImage(
              'https://flutter.io/images/catalog-widget-placeholder.png', // 404
            ),
          ),
        ),
        child: Container(
          height: 200,
          width: 200,
          foregroundDecoration: BoxDecoration(
            backgroundBlendMode: BlendMode.exclusion,
            gradient: LinearGradient(
              colors: const [
                Colors.red,
                Colors.blue,
              ],
            ),
          ),
        ),
      ),
    ),
  );
}

Material

有切角的边框。

BeveledRectangleBorder.png
BeveledRectangleBorder.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('shape: BeveledRectangleBorder')),
    body: Center(
      child: Material(
        shape: const BeveledRectangleBorder(
          borderRadius: BorderRadius.all(Radius.circular(20)),
          side: BorderSide(color: Colors.black, width: 4),
        ),
        color: Colors.yellow,
        child: Container(
          height: 200,
          width: 200,
        ),
      ),
    ),
  );
}

Slivers

SliverFillRemaining

即便没有足够的空间,当你想要将内容居中,这个部件也是不可替代的。

SliverFillRemaining.png
SliverFillRemaining.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('SliverFillRemaining')),
    body: CustomScrollView(
      slivers: [
        SliverFillRemaining(
          hasScrollBody: false,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: const [
              FlutterLogo(size: 200),
              Text(
                'This is some longest text that should be centered'
                'together with the logo',
                textAlign: TextAlign.center,
              ),
            ],
          ),
        ),
      ],
    ),
  );
}

如果居中的内容没有足够的空间,SliverFillRemaining将变为可滚动。

with_SliverFillRemaining.png
with_SliverFillRemaining.png

如果没使用 SliverFillRemaining,内容将会像下面这样溢出:

without_SliverFillRemaining.png
without_SliverFillRemaining.png
Filling the remaining space

除了对内容居中有用之外,SliverFillRemaining 还会填充剩余视口的可用空间。为此,此部件必须放置在 CustomScrollView 中,并且必须是最后一个 sliver

fill_remianing_space01.png
fill_remianing_space01.png

如果没有足够的空间,部件将变为可滚动。

fill_remianing_space02.png
fill_remianing_space02.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('SliverFillRemaining')),
    body: CustomScrollView(
      slivers: [
        SliverList(
          delegate: SliverChildListDelegate(const [
            ListTile(title: Text('First item')),
            ListTile(title: Text('Second item')),
            ListTile(title: Text('Third item')),
            ListTile(title: Text('Fourth item')),
          ]),
        ),
        SliverFillRemaining(
          hasScrollBody: false,
          child: Container(
            color: Colors.yellowAccent,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: const [
                FlutterLogo(size: 200),
                Text(
                  'This is some longest text that should be centered'
                  'together with the logo',
                  textAlign: TextAlign.center,
                ),
              ],
            ),
          ),
        ),
      ],
    ),
  );
}

SizedBox

SizedBox 是最简单但是最常用的小部件之一。

SizedBox as ConstrainedBox

SizedBox 工作方式跟 ConstrainedBox 有些类似。

sizebox_expand.png
sizebox_expand.png
代码语言:javascript
复制
SizedBox.expand(
  child: Card(
    child: Text('Hello World!'),
    color: Colors.yellowAccent,
  ),
),
SizedBox as padding

当需要添加内边距和外边距的时候,你可以选择 PaddingContainer 小部件。但是,它们可以比添加 Sizedbox 更冗长且可读性更低。

sizebox_as_padding.png
sizebox_as_padding.png
代码语言:javascript
复制
Column(
  children: <Widget>[
    Icon(Icons.star, size: 50),
    const SizedBox(height: 100),
    Icon(Icons.star, size: 50),
    Icon(Icons.star, size: 50),
  ],
),
SizedBox as an Invisible Object

很多时候,你想通过设置一个 bool 值来隐藏/展示小部件。

is_visible_true.png
is_visible_true.png

|

is_visible_false.png
is_visible_false.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  bool isVisible = true; // true or false
  return Scaffold(
    appBar: AppBar(
      title: Text('isVisible = $isVisible'),
    ),
    body: isVisible 
      ? Icon(Icons.star, size: 150) 
      : const SizedBox(),
  );
}

因为 SizedBox 有一个 const 构造函数,所以使用 const SizeBox() 真的很便宜。一种更便宜的解决方案是使用 Opacity 小部件,将其 opacity 值更改为 0.0。这种解决方案的缺点是给定的小部件只是不可见,但是还是占用空间。

SafeArea

在不同的平台,有些特定的区域,比如安卓的状态栏或者 iPhone X 的刘海区块,我们不应该在其下面绘制内容。

解决方案是使用 SafeArea 小部件。(下面截图是没使用/使用 SafeArea

without_safeArea.png
without_safeArea.png

|

with_safeArea.png
with_safeArea.png
代码语言:javascript
复制
Widget build(BuildContext context) {
  return Material(
    color: Colors.blue,
    child: SafeArea(
      child: SizedBox.expand(
        child: Card(color: Colors.yellowAccent),
      ),
    ),
  );
}

本文翻译自Flutter Layout Cheat Sheet。代码已经验证,需要留意RaisedButton已经被ElevatedButton替代,在现实使用中需要留意。本文重点是其布局思路和技巧。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-06-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Row and Column
    • MainAxisAlignment
    • IntrinsicWidth and IntrinsicHeight
    • Stack
    • Expanded
    • ConstrainedBox
    • Align
    • Container
      • Container as a layout tool
        • Container as decoration
          • Container as Transform
          • BoxDecoration
          • image: DecorationImage
            • border: Border
              • borderRadius: BorderRadius
                • shape: BoxShape
                  • boxShadow: List
                    • gradient
                      • backgroundBlendMode
                      • Material
                      • Slivers
                        • SliverFillRemaining
                          • Filling the remaining space
                          • SizedBox
                            • SizedBox as ConstrainedBox
                              • SizedBox as padding
                                • SizedBox as an Invisible Object
                                • SafeArea
                                领券
                                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档