(两百四十)学习Flutter开发文档 - Flutter 中的布局

学习 https://flutter.cn/docs/development/ui/layout

目录

Flutter 中的布局

布局 widget

1. 选择一个布局 widget

2. 创建一个可见 widget

3. 将可见 widget 添加到布局 widget

4. 将布局 widget 添加到页面

非 Material apps

横向或纵向布局多个 widgets

对齐 widgets

调整 widgets 大小

组合 widgets

嵌套行和列


 

Flutter 中的布局

要点

  • Widgets 是用于构建 UI 的类。

  • Widgets 可以用于布局和展示 UI 元素。

  • 通过组合简单的 widgets 来构建复杂的 widgets。

Flutter 布局的核心机制是 widgets。在 Flutter 中,几乎所有东西都是 widget —— 甚至布局模型都是 widgets。你在 Flutter 应用程序中看到的图像,图标和文本都是 widgets。此外不能直接看到的也是 widgets,例如用来排列、限制和对齐可见 widgets 的行、列和网格。

你可以通过组合 widgets 来构建更复杂的 widgets 来创建布局。比如,下面第一个截图上有 3 个图标,每个图标下面都有一个标签:

第二个截图显示了可视布局,可以看到有一排三列,其中每列包含一个图标和一个标签。

备忘

本教程中的大多数截图都是将 debugPaintSizeEnabled 设置为 true 以后的效果,因此你可以看到可视布局。更多信息可以查看文档中的 可视化调试,它是 调试 Flutter 应用 中的一节。

以下是这个 UI 的 widget 树形图:

图上大部分应该和你预想的一样,但你可能会疑惑 containers(图上粉色显示的)是什么。Container 是一个 widget,允许你自定义其子 widget。举几个例子,如果要添加 padding、margin、边框或背景颜色,你就可以用上 Container 了。

在这个例子中,每个 Text widget 都被放在一个 Container 以添加 padding。整个 Row 也被放在一个 Container 中,以便添加 padding。

这个例子其余部分的 UI 由属性控制。通过 Iconcolor 属性来设置它的颜色,通过 Text.style 属性来设置文字的字体、颜色、字重等等。列和行有一些属性可以让你指定子项垂直或水平的对齐方式以及子项应占用的空间大小。

感觉和Android 普通的布局概念差不多,就是大布局套小布局,然后垂直和水平布局,加点属性设定

 

布局 widget

如何在 Flutter 中布局单个 widget?本节将介绍如何创建和显示单个 widget。本节还包括一个简单的 Hello World app 的完整代码。

在 Flutter 中,只需几步就可以在屏幕上显示文本、图标或图像。

1. 选择一个布局 widget

根据你想要对齐或限制可见 widget 的方式从各种 layout widgets 中进行选择,因为这些特性通常会传递它所给包含的 widget。

本例使用将其内容水平和垂直居中的 Center

2. 创建一个可见 widget

举个例子,创建一个 Text widget:

Text('Hello World'),

创建一个 Image widget:

Image.asset(
  'images/lake.jpg',
  fit: BoxFit.cover,
),

创建一个 Icon widget:

Icon(
  Icons.star,
  color: Colors.red[500],
),

3. 将可见 widget 添加到布局 widget

所有布局 widgets 都具有以下任一项:

  • 一个 child 属性,如果它们只包含一个子项 —— 例如 CenterContainer

  • 一个 children 属性,如果它们包含多个子项 —— 例如 RowColumnListViewStack

Text widget 添加进 Center widget:

Center(
  child: Text('Hello World'),
),

4. 将布局 widget 添加到页面

一个 Flutter app 本身就是一个 widget,大多数 widgets 都有一个 build() 方法,在 app 的 build() 方法中实例化和返回一个 widget 会让它显示出来。

对于 Material app,你可以使用 Scaffold widget,它提供默认的 banner、背景颜色,还有用于添加抽屉、提示条和底部列表弹窗的 API。你可以将 Center widget 直接添加到主页 body 的属性中。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter layout demo',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter layout demo'),
        ),
        body: Center(
          child: Text('Hello World'),
        ),
      ),
    );
  }
}

备忘

Material 库 实现了一些遵循 Material Design 原则的 widgets。在设计 UI 时,你可以只使用标准 widgets 库 中的 widgets,也可以使用 Material library 中的 widgets。你可以混合来自两个库的 widgets,可以自定义现有 widgets,也可以构建自己的一组自定义 widgets。

 

非 Material apps

对于非 Material app,你可以将 Center widget 添加到 app 的 build() 方法里:

lib/main.dart (MyApp)

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(color: Colors.white),
      child: Center(
        child: Text(
          'Hello World',
          textDirection: TextDirection.ltr,
          style: TextStyle(
            fontSize: 32,
            color: Colors.black87,
          ),
        ),
      ),
    );
  }
}

 

 

横向或纵向布局多个 widgets

最常见的布局模式之一是垂直或水平 widgets。你可以使用 Row widget 水平排列 widgets,使用 Column widget 垂直排列 widgets。

要点

  • Row 和 Column 是两种最常用的布局模式。

  • Row 和 Column 每个都有一个子 widgets 列表。

  • 一个子 widget 本身可以是 Row、Column 或其他复杂 widget。

  • 可以指定 Row 或 Column 如何在垂直和水平方向上对齐其子项。

  • 可以拉伸或限制特定的子 widgets。

  • 可以指定子 widgets 如何占用 Row 或 Column 的可用空间。

要在 Flutter 中创建行或列,可以将子 widgets 列表添加到 RowColumn widget 中。反过来,每个子项本身可以是一行或一列,依此类推。以下示例演示了如何在行或列中嵌套行或列。

这个布局被组织为 Row。这一行包含两个子项:左侧的列和右侧的图像:

你将在 嵌套行和列 中实现蛋糕介绍示例的一些布局代码。

备忘

Row 和 Column 是水平和垂直布局的基本原始 widgets —— 这些低级 widgets 允许最大程度的自定义。 Flutter 还提供专门的、更高级别的 widgets,可能可以直接满足需求。例如,和 Row 相比你可能更喜欢 ListTile,这是一个易于使用的 widget,有属性可以设置头尾图标,最多可以显示 3 行文本;和 Column 相比你也可能更喜欢 ListView,这是一种类似于列的布局,但如果其内容太长导致可用空间不够容纳时会自动滚动。更多信息可以查看 通用布局 widgets

对齐 widgets

你可以使用 mainAxisAlignmentcrossAxisAlignment 属性控制行或列如何对齐其子项。对于一行来说,主轴水平延伸,交叉轴垂直延伸。对于一列来说,主轴垂直延伸,交叉轴水平延伸。

Diagram showing the main axis and cross axis for a rowDiagram showing the main axis and cross axis for a column

MainAxisAlignmentCrossAxisAlignment 类为控制对齐提供了各种常量。

备忘

当你将图像添加到项目中时,你需要更新 pubspec.yaml 文件来访问它们 —— 本例使用 Image.asset 来显示图像。更多信息可以查看本例的 pubspec.yaml 文件,或文档:添加资源和图片。如果你正在使用 Image.network 引用在线图像,则不需要这些操作。

看了下pubspec.yaml中也有相关注释

# To add assets to your application, add an assets section, like this:
# assets:
#  - images/a_dot_burr.jpeg
#  - images/a_dot_ham.jpeg

# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.

# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages

在以下示例中,3 个图像每个都是是 100 像素宽。渲染框(在本例中是整个屏幕)宽度超过 300 像素,因此设置主轴对齐方式为 spaceEvenly 会将空余空间在每个图像之间、之前和之后均匀地划分。

Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    Image.asset('images/pic1.jpg'),
    Image.asset('images/pic2.jpg'),
    Image.asset('images/pic3.jpg'),
  ],
);

Row with 3 evenly spaced images

App 源码: row_column

列的工作方式与行的工作方式相同。以下示例展示了包含 3 个图像的列,每个图像的高度为 100 像素。渲染框(在本例中是整个屏幕)高度超过 300 像素,因此设置主轴对齐方式为 spaceEvenly 会将空余空间在每个图像之间、之上和之下均匀地划分。

Column(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    Image.asset('images/pic1.jpg'),
    Image.asset('images/pic2.jpg'),
    Image.asset('images/pic3.jpg'),
  ],
);

App 源码: row_column

Column showing 3 images spaced evenly

 

本地执行代码报错了。。。

Performing hot restart...
Syncing files to device Redmi K20 Pro Premium Edition...
Restarted application in 1,430ms.
I/flutter (22990): ══╡ EXCEPTION CAUGHT BY IMAGE RESOURCE SERVICE ╞════════════════════════════════════════════════════
I/flutter (22990): The following _Exception was thrown resolving an image codec:
I/flutter (22990): Exception: Could not instantiate image codec.
I/flutter (22990): 
I/flutter (22990): When the exception was thrown, this was the stack:
I/flutter (22990): #0      _futurize (dart:ui/painting.dart:4304:5)
I/flutter (22990): #1      instantiateImageCodec (dart:ui/painting.dart:1682:10)
I/flutter (22990): #2      PaintingBinding.instantiateImageCodec (package:flutter/src/painting/binding.dart:88:12)
I/flutter (22990): #3      AssetBundleImageProvider._loadAsync (package:flutter/src/painting/image_provider.dart:487:24)
I/flutter (22990): <asynchronous suspension>
I/flutter (22990): #4      AssetBundleImageProvider.load (package:flutter/src/painting/image_provider.dart:469:14)
I/flutter (22990): #5      ImageProvider.resolve.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:flutter/src/painting/image_provider.dart:327:17)
I/flutter (22990): #6      ImageCache.putIfAbsent (package:flutter/src/painting/image_cache.dart:160:22)
I/flutter (22990): #7      ImageProvider.resolve.<anonymous closure>.<anonymous closure> (package:flutter/src/painting/image_provider.dart:325:84)
I/flutter (22990): (elided 13 frames from package dart:async)
I/flutter (22990): 
I/flutter (22990): Image provider: AssetImage(bundle: null, name: "images/pic1.jpg")
I/flutter (22990): Image key: AssetBundleImageKey(bundle: PlatformAssetBundle#759b2(), name: "images/pic1.jpg", scale:
I/flutter (22990):   1.0)
I/flutter (22990): ════════════════════════════════════════════════════════════════════════════════════════════════════
I/flutter (22990): Another exception was thrown: Exception: Could not instantiate image codec.
I/flutter (22990): Another exception was thrown: Exception: Could not instantiate image codec.
D/FlutterView(22990): Attaching to a FlutterEngine: io.flutter.embedding.engine.FlutterEngine@c8e317f

调整 widgets 大小

当某个布局太大而超出屏幕时,受影响的边缘会出现黄色和黑色条纹图案。这里有一个行太宽的 例子

Overly-wide row

通过使用 Expanded widget,可以调整 widgets 的大小以适合行或列。要修复上一个图像行对其渲染框来说太宽的示例,可以用 Expanded widget 把每个图像包起来。

content_copy

Row(
  crossAxisAlignment: CrossAxisAlignment.center,
  children: [
    Expanded(
      child: Image.asset('images/pic1.jpg'),
    ),
    Expanded(
      child: Image.asset('images/pic2.jpg'),
    ),
    Expanded(
      child: Image.asset('images/pic3.jpg'),
    ),
  ],
);

Row of 3 images that are too wide, but each is constrained to take only 1/3 of the space

App 源码: sizing

也许你想要一个 widget 占用的空间是兄弟项的两倍。为了达到这个效果,可以使用 Expanded widget 的 flex 属性,这是一个用来确定 widget 的弹性系数的整数。默认的弹性系数为 1。以下代码将中间图像的弹性系数设置为 2:

content_copy

Row(
  crossAxisAlignment: CrossAxisAlignment.center,
  children: [
    Expanded(
      child: Image.asset('images/pic1.jpg'),
    ),
    Expanded(
      flex: 2,
      child: Image.asset('images/pic2.jpg'),
    ),
    Expanded(
      child: Image.asset('images/pic3.jpg'),
    ),
  ],
);

Row of 3 images with the middle image twice as wide as the others

App 源码: sizing

组合 widgets

默认情况下,行或列沿其主轴会占用尽可能多的空间,但如果要将子项紧密组合在一起,请将其 mainAxisSize 设置为 MainAxisSize.min。以下示例使用此属性将星形图标组合在一起。

content_copy

Row(
  mainAxisSize: MainAxisSize.min,
  children: [
    Icon(Icons.star, color: Colors.green[500]),
    Icon(Icons.star, color: Colors.green[500]),
    Icon(Icons.star, color: Colors.green[500]),
    Icon(Icons.star, color: Colors.black),
    Icon(Icons.star, color: Colors.black),
  ],
)

Row of 5 stars, packed together in the middle of the row

A

导入源码的时候需要根据提示在项目目录下执行下flutter create .(有个点,说是生成一个manifest)

(1)\flutter.cn-master\examples\layout\pavlova>flutter create .

 

嵌套行和列

布局框架允许你根据需要在行和列内嵌套行和列。让我们看看以下布局的概述部分的代码:

Screenshot of the pavlova app, with the ratings and icon rows outlined in red

概述的部分实现为两行,评级一行包含五颗星和评论的数量,图标一行包含由图标与文本组成的三列。

以下是评级行的 widget 树形图:

Ratings row widget tree

ratings 变量创建了一个行,其中包含较小的由 5 个星形图标和文本组成的一行:

content_copy

var stars = Row(
  mainAxisSize: MainAxisSize.min,
  children: [
    Icon(Icons.star, color: Colors.green[500]),
    Icon(Icons.star, color: Colors.green[500]),
    Icon(Icons.star, color: Colors.green[500]),
    Icon(Icons.star, color: Colors.black),
    Icon(Icons.star, color: Colors.black),
  ],
);

final ratings = Container(
  padding: EdgeInsets.all(20),
  child: Row(
    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
    children: [
      stars,
      Text(
        '170 Reviews',
        style: TextStyle(
          color: Colors.black,
          fontWeight: FontWeight.w800,
          fontFamily: 'Roboto',
          letterSpacing: 0.5,
          fontSize: 20,
        ),
      ),
    ],
  ),
);

 小提示

为了最大限度地减少高度嵌套的布局代码可能导致的视觉混乱,可以在变量和函数中实现 UI 的各个部分。

评级行下方的图标行包含 3 列,每列包含一个图标和两行文本,你可以在其 widget 树中看到:

Icon widget tree

iconList 变量定义了图标行:

content_copy

final descTextStyle = TextStyle(
  color: Colors.black,
  fontWeight: FontWeight.w800,
  fontFamily: 'Roboto',
  letterSpacing: 0.5,
  fontSize: 18,
  height: 2,
);

// DefaultTextStyle.merge() allows you to create a default text
// style that is inherited by its child and all subsequent children.
final iconList = DefaultTextStyle.merge(
  style: descTextStyle,
  child: Container(
    padding: EdgeInsets.all(20),
    child: Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        Column(
          children: [
            Icon(Icons.kitchen, color: Colors.green[500]),
            Text('PREP:'),
            Text('25 min'),
          ],
        ),
        Column(
          children: [
            Icon(Icons.timer, color: Colors.green[500]),
            Text('COOK:'),
            Text('1 hr'),
          ],
        ),
        Column(
          children: [
            Icon(Icons.restaurant, color: Colors.green[500]),
            Text('FEEDS:'),
            Text('4-6'),
          ],
        ),
      ],
    ),
  ),
);

leftColumn 变量包含评级和图标行,以及蛋糕介绍的标题和文本:

content_copy

final leftColumn = Container(
  padding: EdgeInsets.fromLTRB(20, 30, 20, 20),
  child: Column(
    children: [
      titleText,
      subTitle,
      ratings,
      iconList,
    ],
  ),
);

左列放置在 Container 中以限制其宽度。最后,UI 由 Card 内的整行(包含左列和图像)构成。

蛋糕图片 来自 Pixabay。你可以使用 Image.network() 从网络上引用图像,但是在本例图像将保存到项目中的一个图像目录中,添加到 pubspec 文件,并使用 Images.asset() 访问。更多信息可以查看文档中关于 添加资源和图片 这一章。

content_copy

body: Center(
  child: Container(
    margin: EdgeInsets.fromLTRB(0, 40, 0, 30),
    height: 600,
    child: Card(
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
            width: 440,
            child: leftColumn,
          ),
          mainImage,
        ],
      ),
    ),
  ),
),

 小提示

蛋糕介绍的例子在宽屏设备(如平板电脑)上横向运行效果最佳。如果在 iOS 模拟器中运行这个示例,可以使用 Hardware > Device 菜单选择不同的设备。在本例中,我们推荐 iPad Pro。你可以使用 Hardware > Rotate 将其方向更改为横向模式。你还可以使用 Window > Scale 更改模拟器窗口的大小(不改变逻辑像素的数量)。

App 源码: pavlova

这个在手机上执行效果比较差,屏幕太小了

 

 

 

 

相关推荐
©️2020 CSDN 皮肤主题: 鲸 设计师:meimeiellie 返回首页