Flutter中构建布局 顶

你将学到什么?

  • Flutter的布局机制如何工作。
  • 如何垂直和水平布局小部件。
  • 如何构建一个Flutter布局。

这是在Flutter中构建布局的指南。 您将构建以下屏幕截图的布局:

然后本指南回过头来解释Flutter的布局方法,并说明如何在屏幕上放置一个小部件。 在讨论如何水平和垂直放置小部件之后,会介绍一些最常见的布局小部件。

建立布局

  • 第0步:设置
  • 第1步:绘制布局图
  • 第2步:实施标题行
  • 第3步:实现按钮行
  • 第4步:实现文本部分
  • 第5步:实现图像部分
  • 第6步:把它放在一起

Flutter的布局方法 布置一个小部件 垂直和水平放置多个小部件

  • 对齐小部件
  • 调整小部件
  • 包装小部件
  • 嵌套行和列

常见的布局小部件

  • 标准小部件
  • 材料组件

资源

建立布局

如果您想要了解布局机制的“全貌”,请从Flutter的布局方法开始。

第0步:设置

首先,获取代码:

接下来,将图像添加到示例中:

  • 在项目顶部创建一个images目录。
  • 添加lake.jpg。 (请注意,wget无法保存此二进制文件。)
  • 更新pubspec.yaml文件以包含assets标签。 这会使图像可用于您的代码。

第1步:绘制布局图

第一步是将布局打破成其基本要素:

  • 识别行和列。
  • 布局是否包含网格?
  • 有重叠的元素吗?
  • 用户界面是否需要选项卡?
  • 注意需要对齐,填充或边框的区域。

首先,确定更大的元素。 在这个例子中,四个元素排列成一列:一个图像,两行和一个文本块。

接下来,绘制每一行。 第一行称为标题部分,有三个孩子:一列文字,一个星形图标和一个数字。 它的第一个孩子,列,包含2行文字。 第一列占用大量空间,所以它必须包装在扩展小部件中。

第二行称为按钮部分,也有3个子项:每个子项都是一个包含图标和文本的列。

一旦布局结束,最简单的就是采取自下而上的方法来实现它。 为了最大限度地减少深度嵌套布局代码的视觉混淆,将一些实现放置在变量和函数中。

第2步:实现标题行

首先,您将在标题部分构建左栏。 将列放入扩展窗口小部件中会拉伸该列以使用该行中的所有剩余空闲空间。 将crossAxisAlignment属性设置为CrossAxisAlignment.start可将列置于行的开始位置。

将第一行文本放入Container中可以添加填充。 列中的第二个子项(也是文本)显示为灰色。

标题行中的最后两项是一个红色的星形图标和文字“41”。 将整行放在容器中,并沿着每个边缘填充32像素。

这是实现标题行的代码。

注意:如果有什么问题,对照GitHub上的lib/main.dart检查代码 

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    Widget titleSection = new Container(
      padding: const EdgeInsets.all(32.0),
      child: new Row(
        children: [
          new Expanded(
            child: new Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                new Container(
                  padding: const EdgeInsets.only(bottom: 8.0),
                  child: new Text(
                    'Oeschinen Lake Campground',
                    style: new TextStyle(
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ),
                new Text(
                  'Kandersteg, Switzerland',
                  style: new TextStyle(
                    color: Colors.grey[500],
                  ),
                ),
              ],
            ),
          ),
          new Icon(
            Icons.star,
            color: Colors.red[500],
          ),
          new Text('41'),
        ],
      ),
    );
  //...
}

提示:将代码粘贴到应用程序中时,缩进可能会变形。 您可以通过右键单击Dart代码并选择使用Reformat with Dart Style来在IntelliJ中修复此问题。 或者,在命令行中,您可以使用dartfmt

提示:为了获得更快的开发体验,请尝试使用Flutter的热重新加载功能。 热重新加载允许您修改代码并查看更改,而无需完全重新启动应用程序。 IntelliJ的Flutter插件支持热重载,或者您可以从命令行触发。 有关更多信息,请参阅Hot Reloads与完整应用程序重新启动

第3步:实现按钮行

按钮部分包含3列,它们使用相同的布局 - 一行文本上的图标。 此行中的列均匀分布,文本和图标用主颜色绘制,在应用程序的build()方法中将其设置为蓝色:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //...

    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),

    //...
}

由于构建每一行的代码几乎是相同的,因此创建一个嵌套函数(如buildButtonColumn()(它接受一个Icon和Text)并返回一个列以其主要颜色绘制的小部件的效率最高。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //...

    Column buildButtonColumn(IconData icon, String label) {
      Color color = Theme.of(context).primaryColor;

      return new Column(
        mainAxisSize: MainAxisSize.min,
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          new Icon(icon, color: color),
          new Container(
            margin: const EdgeInsets.only(top: 8.0),
            child: new Text(
              label,
              style: new TextStyle(
                fontSize: 12.0,
                fontWeight: FontWeight.w400,
                color: color,
              ),
            ),
          ),
        ],
      );
    }
  //...
}

构建函数将图标直接添加到列中。 将文本放入容器以在文本上方添加填充,将其与图标分开。

通过调用函数并传递特定于该列的图标和文本来构建包含这些列的行。 使用MainAxisAlignment.spaceEvenly沿着主轴对齐列,以在每列之前,之间和之后均匀排列空闲空间。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //...

    Widget buttonSection = new Container(
      child: new Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          buildButtonColumn(Icons.call, 'CALL'),
          buildButtonColumn(Icons.near_me, 'ROUTE'),
          buildButtonColumn(Icons.share, 'SHARE'),
        ],
      ),
    );
  //...
}

第4步:实现文本部分

将相当长的文本部分定义为变量。 将文本放入容器中,以便沿每条边添加32像素的填充。 softwrap属性指示文本是否应在软换行符(如句点或逗号)上断开。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //...

    Widget textSection = new Container(
      padding: const EdgeInsets.all(32.0),
      child: new Text(
        '''
Lake Oeschinen lies at the foot of the Blüemlisalp in the Bernese Alps. Situated 1,578 meters above sea level, it is one of the larger Alpine Lakes. A gondola ride from Kandersteg, followed by a half-hour walk through pastures and pine forest, leads you to the lake, which warms to 20 degrees Celsius in the summer. Activities enjoyed here include rowing, and riding the summer toboggan run.
        ''',
        softWrap: true,
      ),
    );
  //...
}

第5步:实现图像部分

四列元素中的三个现在完成,只留下图像。 该图片可以在Creative Commons许可下在线获得,但是它大而且缓慢。 在步骤0中,您将该图像包含在项目中并更新了pubspec文件,以便现在可以从代码中引用它:

body: new ListView(
  children: [
    new Image.asset(
      'images/lake.jpg',
      height: 240.0,
      fit: BoxFit.cover,
    ),
    // ...
  ],
)

BoxFit.cover告诉框架,图像应尽可能小,但覆盖整个渲染框。

第6步:把它放在一起

在最后一步,你将这些碎片组装在一起。 这些小部件安排在ListView中,而不是列中,因为在小设备上运行应用程序时,ListView会自动滚动。

//...
body: new ListView(
  children: [
    new Image.asset(
      'images/lake.jpg',
      width: 600.0,
      height: 240.0,
      fit: BoxFit.cover,
    ),
    titleSection,
    buttonSection,
    textSection,
  ],
),
//...

Dart代码main.dart Imageimages Pubspecpubspec.yaml

而已! 当您重新加载应用程序时,应该会看到截图中显示的相同布局。 您可以通过将交互添加到您的Flutter应用中来为此布局添加交互功能。

Flutter的布局方法

重点是什么?

  • 小部件是用于构建UI的类。
  • 小部件用于布局和UI元素。
  • 撰写简单的小部件来构建复杂的小部件。

Flutter的布局机制的核心是小部件。 在Flutter中,几乎所有东西都是一个小部件 - 甚至布局模型都是小部件。 您在Flutter应用中看到的图像,图标和文本都是小部件。 但是你看不到的东西也是小部件,例如排列,约束和对齐可见小部件的行,列和网格。

您可以通过构建小部件来创建布局来构建更复杂的小部件。 例如,左边的屏幕截图显示了3个图标,每个图标下有一个标签:

第二个屏幕截图显示可视布局,显示一列3列,其中每列包含一个图标和一个标签。

注意:本教程中的大多数屏幕截图均以debugPaintSizeEnabled设置为true显示,以便您可以看到可视布局。 有关更多信息,请参阅可视化调试,这是调试Flutter应用程序中的一部分

以下是此UI的部件树图:

大部分应该看起来像你所期望的,但你可能想知道容器(以粉红色显示)。 容器是一个小部件,允许您自定义其子部件。 如果要添加填充,边距,边框或背景色,请使用容器来命名其某些功能。

在这个例子中,每个文本小部件放置在容器中以添加边距。 整个行也被放置在容器中以在行的周围添加填充。

本例中的其余UI由属性控制。 使用其color属性设置图标的颜色。 使用文本的style属性来设置字体,颜色,重量等等。 列和行的属性允许您指定他们的孩子如何垂直或水平对齐,以及儿童应该占据多少空间。

布置一个小部件

重点是什么?

  • 即使应用程序本身也是一个小部件。
  • 创建一个小部件并将其添加到布局小部件很容易。
  • 要在设备上显示小部件,请将布局小部件添加到应用小部件。
  • 使用Scaffold是最容易的,它是Material Components库中的一个小部件,它提供了一个默认横幅,背景颜色,并且具有添加抽屉,小吃店和底部表单的API。
  • 如果您愿意,可以构建仅使用小部件库中的标准小部件的应用程序。

如何在Flutter中布置单个小部件? 本节介绍如何创建一个简单的小部件并将其显示在屏幕上。 它还显示了一个简单的Hello World应用程序的完整代码。

在Flutter中,只需几个步骤即可在屏幕上放置文本,图标或图像。

1.选择一个布局小部件来保存该对象。 根据您想要对齐或约束可见窗口小部件的方式,从各种布局窗口小部件中进行选择,因为这些特性通常会传递到包含的窗口小部件。 这个例子使用Center,它将内容水平和垂直居中。

2.创建一个小部件来容纳可见对象。

注意:Flutter应用程序是用Dart语言编写的。 如果您了解Java或类似的面向对象编程语言,Dart会感到非常熟悉。 如果不是的话,你可以试试DartPad,一个可以在任何浏览器上使用的交互式Dart练习。 语言游览提供了Dart语言功能的概述。

例如,创建一个文本小部件:

new Text('Hello World', style: new TextStyle(fontSize: 32.0))

创建一个图像小部件:

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

创建一个图标小部件:

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

3.将可见小部件添加到布局小部件。 如果所有布局小部件带有一个子元素(例如Center或Container),则它们具有一个child属性,如果它们带有小部件列表(例如Row,Column,ListView或Stack),则它们具有children属性。

将文本小部件添加到中心小部件:

new Center(
  child: new Text('Hello World', style: new TextStyle(fontSize: 32.0))

4.将布局小部件添加到页面。 Flutter应用本身就是一个小部件,大部分小部件都有一个build()方法。 在应用程序的构建方法中声明小部件会在设备上显示小部件。

对于Material应用程序,您可以将Center小部件直接添加到主页的body属性。

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: new Text('Hello World', style: new TextStyle(fontSize: 32.0)),
      ),
    );
  }
}

注意:Material Components库实现了遵循Material Design原则的小部件。 在设计用户界面时,您可以专门使用标准小部件库中的小部件,也可以使用材质部件中的小部件。 您可以混合使用两个库中的小部件,您可以自定义现有的小部件,也可以构建自己的一组定制小部件。

对于非Material应用程序,您可以将Center小部件添加到应用程序的build()方法中:

// This app doesn't use any Material Components, such as Scaffold.
// Normally, an app that doesn't use Scaffold has a black background
// and the default text color is black. This app changes its background
// to white and its text color to dark grey to mimic a Material app.
import 'package:flutter/material.dart';

void main() {
  runApp(new MyApp());
}

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

请注意,默认情况下,非Material应用程序不包含AppBar,标题或背景颜色。 如果您想在非Material应用程序中使用这些功能,您必须自己构建它们。 此应用程序将背景颜色更改为白色,将文本更改为深灰色以模仿Material应用程序。

而已! 当你运行这个应用时,你应该看到:

Dart code (Material app): main.dart Dart code (widgets-only app): main.dart

垂直和水平放置多个小部件

最常见的布局模式之一是垂直或水平排列小部件。 您可以使用“行”小部件水平排列小部件,并使用“列”小部件垂直排列小部件。

重点是什么?

  • 行和列是两种最常用的布局模式。
  • 行和列分别获取子窗口小部件的列表。
  • 子小部件本身可以是行,列或其他复杂小部件。
  • 您可以指定行或列如何在垂直和水平方向上对齐其子项。
  • 您可以拉伸或限制特定的子部件。
  • 您可以指定子窗口小部件如何使用行或列的可用空间。

内容

  • 对齐小部件
  • 调整小部件
  • 包装小部件
  • 嵌套行和列

要在Flutter中创建行或列,可以将一个子窗口小部件列表添加到RowColumn窗口小部件中。 反过来,每个孩子本身可以是一排或一列,依此类推。 以下示例显示如何在行或列内嵌套行或列。

此布局按行组织。 该行包含两个孩子:左侧的一列和右侧的图片:

左列的小部件树嵌套行和列。

您将在嵌套行和列中实现一些Pavlova的布局代码。

注意:行和列是水平和垂直布局的基本原始小部件 - 这些低级小部件允许最大化的自定义。 Flutter还提供专门的,更高级别的小部件,可能足以满足您的需求。 例如,您可能更喜欢ListTile,而不是Row,而ListTile是一个易于使用的小部件,具有前导和尾随图标属性以及最多3行文本。 您可能更喜欢ListView,而不是列,您可能更喜欢ListView,这是一种列状布局,如果其内容太长而无法适应可用空间,则会自动滚动。 有关更多信息,请参阅通用布局小部件

对齐小部件

您可以使用mainAxisAlignment和crossAxisAlignment属性控制行或列的排列方式。 对于一排,主轴水平延伸,横轴垂直延伸。 对于一列,主轴垂直运行,横轴水平运行。

MainAxisAlignment和CrossAxisAlignment类提供了用于控制对齐的各种常量。

注意:将图像添加到项目中时,需要更新pubspec文件才能访问它们 - 此示例使用Image.asset来显示图像。 有关更多信息,请参阅此示例的pubspec.yaml文件,或在Flutter中添加资源和图像。 如果您使用Image.network来引用联机图像,则不需要执行此操作。

在以下示例中,3个图像中的每一个都是100像素宽。 渲染框(在这种情况下,整个屏幕)宽度超过300像素,因此将主轴对齐设置为spaceEvenly在每个图像之间,之前和之后均匀分配自由水平空间。

appBar: new AppBar(
  title: new Text(widget.title),
),
body: new Center(
  child: new Row(
    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
    children: [
      new Image.asset('images/pic1.jpg'),

Dart code: main.dart Images: images Pubspec: pubspec.yaml

列的工作方式与行相同。 以下示例显示了一列3个图像,每个图像高100个像素。 渲染盒(在这种情况下,整个屏幕)的高度大于300像素,因此将主轴对齐设置为spaceEvenly将自由垂直空间均匀分配在每个图像之间,之上和之下。

appBar: new AppBar(
  title: new Text(widget.title),
),
body: new Center(
  child: new Column(
    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
    children: [
      new Image.asset('images/pic1.jpg'),

Dart code: main.dart Images: images Pubspec: pubspec.yaml

注意:如果布局太大而不适合设备,则会在受影响的边缘出现红色条纹。 例如,以下截图中的行对于设备的屏幕来说太宽:

通过使用“扩展”窗口小部件,可以将窗口小部件的大小设置为适合行或列,这在下面的“调整窗口小部件”部分进行了描述。

调整小部件

也许你想要一个小部件占据其兄弟姐妹两倍的空间。 您可以将行或列的子项放置在扩展小部件中,以控制沿着主轴的小部件大小。 扩展小部件具有flex属性,它是一个整数,用于确定小部件的弹性因子。 扩展小部件的默认弹性因子是1。

例如,要创建一个由三个小部件组成的行,其中中间小部件的宽度是其他两个小部件的两倍,请将中间小部件的弹性系数设置为2:

appBar: new AppBar(
  title: new Text(widget.title),
),
body: new Center(
  child: new Row(
    crossAxisAlignment: CrossAxisAlignment.center,
    children: [
      new Expanded(
        child: new Image.asset('images/pic1.jpg'),
      ),
      new Expanded(
        flex: 2,
        child: new Image.asset('images/pic2.jpg'),
      ),
      new Expanded(

Dart code: main.dart Images: images Pubspec: pubspec.yaml

要修复上一节中的示例,其中3行图像的行对于其渲染框太宽,并且导致红色条带,请使用扩展小部件包装每个小部件。 默认情况下,每个小部件的弹性因子为1,将行的三分之一分配给每个小部件。

appBar: new AppBar(
  title: new Text(widget.title),
),
body: new Center(
  child: new Row(
    crossAxisAlignment: CrossAxisAlignment.center,
    children: [
      new Expanded(
        child: new Image.asset('images/pic1.jpg'),
      ),
      new Expanded(
        child: new Image.asset('images/pic2.jpg'),
      ),
      new Expanded(

Dart code: main.dart Images: images Pubspec: pubspec.yaml

包装小部件

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    var packedRow = new Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        new Icon(Icons.star, color: Colors.green[500]),
        new Icon(Icons.star, color: Colors.green[500]),
        new Icon(Icons.star, color: Colors.green[500]),
        new Icon(Icons.star, color: Colors.black),
        new Icon(Icons.star, color: Colors.black),
      ],
    );

  // ...
}

Dart code: main.dart Icons: Icons class Pubspec: pubspec.yaml

嵌套行和列

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

概述部分实现为两行。 评级行包含五颗星和评论数量。 图标行包含三列图标和文本。

评级行的小部件树:

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    //...

    var ratings = new Container(
      padding: new EdgeInsets.all(20.0),
      child: new Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          new Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              new Icon(Icons.star, color: Colors.black),
              new Icon(Icons.star, color: Colors.black),
              new Icon(Icons.star, color: Colors.black),
              new Icon(Icons.star, color: Colors.black),
              new Icon(Icons.star, color: Colors.black),
            ],
          ),
          new Text(
            '170 Reviews',
            style: new TextStyle(
              color: Colors.black,
              fontWeight: FontWeight.w800,
              fontFamily: 'Roboto',
              letterSpacing: 0.5,
              fontSize: 20.0,
            ),
          ),
        ],
      ),
    );
    //...
  }
}

提示:为了最大限度地减少由嵌套严重的布局代码导致的视觉混淆,可以在变量和函数中实现UI的各个部分。

评级行下方的图标行包含3列; 每个列都包含一个图标和两行文本,您可以在其小部件树中看到:

iconList变量定义图标行:

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    // ...

    var descTextStyle = new TextStyle(
      color: Colors.black,
      fontWeight: FontWeight.w800,
      fontFamily: 'Roboto',
      letterSpacing: 0.5,
      fontSize: 18.0,
      height: 2.0,
    );

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

leftColumn变量包含评分和图标行,以及描述Pavlova的标题和文本:

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    //...

    var leftColumn = new Container(
      padding: new EdgeInsets.fromLTRB(20.0, 30.0, 20.0, 20.0),
      child: new Column(
        children: [
          titleText,
          subTitle,
          ratings,
          iconList,
        ],
      ),
    );
    //...
  }
}

左列放置在容器中以约束其宽度。 最后,用Card的整个行(包含左列和图像)构建UI。

Pavlova图片来自Pixabay,可以在Creative Commons许可下使用。 您可以使用Image.network从网络中嵌入图像,但对于此示例,图像将保存到项目中的图像目录中,添加到pubspec文件并使用Images.asset访问。 有关更多信息,请参阅在Flutter中添加资产和图像

body: new Center(
  child: new Container(
    margin: new EdgeInsets.fromLTRB(0.0, 40.0, 0.0, 30.0),
    height: 600.0,
    child: new Card(
      child: new Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          new Container(
            width: 440.0,
            child: leftColumn,
          ),
          mainImage,
        ],
      ),
    ),
  ),
),

Dart code: main.dart Images: images Pubspec: pubspec.yaml

提示:Pavlova示例在广泛的设备(如平板电脑)上水平运行效果最佳。 如果您在iOS模拟器中运行此示例,则可以使用Hardware > Device菜单选择其他设备。 对于这个例子,我们推荐iPad Pro。 您可以使用Hardware > Rotate将其方向更改为横向模式。 您还可以使用Window > Scale更改模拟器窗口的大小(不更改逻辑像素的数量)。

常见的布局小部件

Flutter拥有丰富的布局小部件库,但这里有一些最常用的布局部件。 其目的是尽可能快地启动并运行,而不是让您完整列出。 有关其他可用小部件的信息,请参阅小部件概述,或使用API参考文档中的搜索框。 此外,API文档中的小部件页面经常会提供有关可能更适合您需求的类似小部件的建议。

以下小部件分为两类:小部件库中的标准小部件和材质组件库中的专用小部件。 任何应用程序都可以使用小部件库,但只有Material应用程序可以使用Material Components库。

标准小部件

  • Container: 向边框添加填充,边距,边框,背景颜色或其他装饰。
  • GridView: 放置小部件作为可滚动的网格。
  • ListView: 将小部件列为可滚动列表。
  • Stack: 将小部件重叠在另一个小部件之上。

Material Components

  • Card: 将相关信息组织成带有圆角和投影的盒子。
  • ListTile: 将最多3行文本,以及可选的前导和训练图标组合成一行。

Container

许多布局会自由使用Container来使用填充分隔小部件,或者添加边框或边距。 您可以通过将整个布局放入Container并更改其背景颜色或图像来更改设备的背景。

容器概要:

  • 添加填充,边距,边框
  • 更改背景颜色或图像
  • 包含单个子部件,但该子部件可以是Row,Column,甚至是部件树的根部

容器示例: 除了下面的例子之外,本教程中的许多示例都使用Container。 您还可以在Flutter Gallery中找到更多容器示例。

该布局由两列组成,每列包含2个图像。 每个图像使用一个Container来添加一个圆形的灰色边框和边距。 包含图像行的列使用容器将背景颜色更改为浅灰色。

Dart code: main.dart,下面的代码段 Images: images Pubspec: pubspec.yaml

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {

    var container = new Container(
      decoration: new BoxDecoration(
        color: Colors.black26,
      ),
      child: new Column(
        children: [
          new Row(
            children: [
              new Expanded(
                child: new Container(
                  decoration: new BoxDecoration(
                    border: new Border.all(width: 10.0, color: Colors.black38),
                    borderRadius:
                        const BorderRadius.all(const Radius.circular(8.0)),
                  ),
                  margin: const EdgeInsets.all(4.0),
                  child: new Image.asset('images/pic1.jpg'),
                ),
              ),
              new Expanded(
                child: new Container(
                  decoration: new BoxDecoration(
                    border: new Border.all(width: 10.0, color: Colors.black38),
                    borderRadius:
                        const BorderRadius.all(const Radius.circular(8.0)),
                  ),
                  margin: const EdgeInsets.all(4.0),
                  child: new Image.asset('images/pic2.jpg'),
                ),
              ),
            ],
          ),
          // ...
          // See the definition for the second row on GitHub:
          // https://raw.githubusercontent.com/flutter/website/master/_includes/code/layout/container/main.dart
        ],
      ),
    );
    //...
  }
}

GridView

使用GridView将小部件放置为二维列表。 GridView提供了两个预制列表,或者您可以构建自己的自定义网格。 当GridView检测到其内容太长而不适合渲染框时,它会自动滚动。

GridView摘要:

  • 在网格中放置小部件
  • 检测列内容何时超过渲染框并自动提供滚动
  • 构建您自己的自定义网格,或使用提供的网格之一:
  • GridView.count允许你指定列数
  • GridView.extent允许你指定一个tile的最大像素宽度

注意:显示二维列表时,重要的是单元格占用哪一行和一列(例如,它是“avocado”行的“calorie”列中的条目),请使用TableDataTable

GridView示例:

使用GridView.extent创建一个最大宽度为150像素的网格。

Dart code: main.dartImages: images Pubspec: pubspec.yaml

// The images are saved with names pic1.jpg, pic2.jpg...pic30.jpg.
// The List.generate constructor allows an easy way to create
// a list when objects have a predictable naming pattern.
List<Container> _buildGridTileList(int count) {

  return new List<Container>.generate(
      count,
      (int index) =>
          new Container(child: new Image.asset('images/pic${index+1}.jpg')));
}

Widget buildGrid() {
  return new GridView.extent(
      maxCrossAxisExtent: 150.0,
      padding: const EdgeInsets.all(4.0),
      mainAxisSpacing: 4.0,
      crossAxisSpacing: 4.0,
      children: _buildGridTileList(30));
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: buildGrid(),
      ),
    );
  }
}

使用GridView.count在纵向模式下创建2个宽度的网格,在横向模式下创建3个宽度的网格。 标题是通过设置每个GridTile的页脚属性创建的。

Dart code:来自Flutter Gallerygrid_list_demo.dart

ListView

ListView是一个类似列的小部件,它的内容对于其渲染框太长时会自动提供滚动。

ListView摘要:

  • 专门用于组织框列表的列
  • 可以水平或垂直放置
  • 检测它的内容何时不适合并提供滚动
  • 比Column更少配置,但更易于使用并支持滚动

ListView示例:

使用ListView显示使用ListTiles的业务列表。 分隔线将餐厅与餐厅分开。

Dart code: main.dart, Icons: Icons class Pubspec: pubspec.yaml

List<Widget> list = <Widget>[
  new ListTile(
    title: new Text('CineArts at the Empire',
        style: new TextStyle(fontWeight: FontWeight.w500, fontSize: 20.0)),
    subtitle: new Text('85 W Portal Ave'),
    leading: new Icon(
      Icons.theaters,
      color: Colors.blue[500],
    ),
  ),
  new ListTile(
    title: new Text('The Castro Theater',
        style: new TextStyle(fontWeight: FontWeight.w500, fontSize: 20.0)),
    subtitle: new Text('429 Castro St'),
    leading: new Icon(
      Icons.theaters,
      color: Colors.blue[500],
    ),
  ),
  // ...
  // See the rest of the column defined on GitHub:
  // https://raw.githubusercontent.com/flutter/website/master/_includes/code/layout/listview/main.dart
];

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      // ...
      body: new Center(
        child: new ListView(
          children: list,
        ),
      ),
    );
  }
}

使用ListView显示特定ColorsMaterial Design面板中的颜色。 Dart代码:来自Flutter Gallerycolors_demo.dart

Stack

使用Stack来安排基础小部件顶部的小部件 - 通常是图像。 小部件可以完全或部分重叠基础小部件。

Stack摘要:

  • 用于与另一个小部件重叠的小部件
  • 子列表中的第一个小部件是基础小部件; 随后的子被覆盖在基础小部件的顶部
  • 堆栈的内容不能滚动
  • 您可以选择剪切超过渲染框的子项

Stack示例:

使用Stack叠加容器(在半透明的黑色背景上显示其文本),放置在Circle Avatar的顶部。Stack使用alignment属性和Alignments偏移文本。

Dart code: main.dart Image: images Pubspec: pubspec.yaml

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    var stack = new Stack(
      alignment: const Alignment(0.6, 0.6),
      children: [
        new CircleAvatar(
          backgroundImage: new AssetImage('images/pic.jpg'),
          radius: 100.0,
        ),
        new Container(
          decoration: new BoxDecoration(
            color: Colors.black45,
          ),
          child: new Text(
            'Mia B',
            style: new TextStyle(
              fontSize: 20.0,
              fontWeight: FontWeight.bold,
              color: Colors.white,
            ),
          ),
        ),
      ],
    );
    // ...
  }
}

使用Stack将渐变叠加到图像的顶部。 渐变确保工具栏的图标与图像不同。 Dart代码:Flutter Gallery中的contacts_demo.dart

Card

材料组件库中的卡片包含相关的信息块,可以由大多数任何小部件构成,但通常与ListTile一起使用。 卡片有一个孩子,但其孩子可以是支持多个孩子的列,行,列表,网格或其他小部件。 默认情况下,卡片将其大小缩小为0像素0。 您可以使用SizedBox来限制卡的大小。

在Flutter中,一张卡片具有稍微圆润的角落和阴影,使其具有3D效果。 更改卡片的elevation属性可让您控制投影效果。 例如,将标高设置为24.0,将卡片从视觉上抬离表面并使阴影变得更加分散。 有关支持的高程值的列表,请参见材料准则中的高程和阴影。 指定不支持的值将完全禁用投影。

Card摘要:

  • 实现材料设计卡片
  • 用于呈现相关信息的块
  • 接受单个孩子,但该孩子可以是Row,Column或其他包含子级列表的小部件
  • 显示圆角和阴影
  • 卡片的内容不能滚动
  • 来自材料组件库

卡片示例:

包含3个ListTiles并通过用SizedBox包装进行大小调整的卡片。 分隔符分隔第一个和第二个ListTiles。

Dart code: main.dart Icons: Icons class Pubspec: pubspec.yaml

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    var card = new SizedBox(
      height: 210.0,
      child: new Card(
        child: new Column(
          children: [
            new ListTile(
              title: new Text('1625 Main Street',
                  style: new TextStyle(fontWeight: FontWeight.w500)),
              subtitle: new Text('My City, CA 99984'),
              leading: new Icon(
                Icons.restaurant_menu,
                color: Colors.blue[500],
              ),
            ),
            new Divider(),
            new ListTile(
              title: new Text('(408) 555-1212',
                  style: new TextStyle(fontWeight: FontWeight.w500)),
              leading: new Icon(
                Icons.contact_phone,
                color: Colors.blue[500],
              ),
            ),
            new ListTile(
              title: new Text('costa@example.com'),
              leading: new Icon(
                Icons.contact_mail,
                color: Colors.blue[500],
              ),
            ),
          ],
        ),
      ),
    );
  //...
}

包含图像和文字的卡片。 Flutter代码:来自Flutter Gallerycards_demo.dart

ListTile

使用ListTile是Material Components库中的一个专门的行小部件,用于创建包含最多3行文本和可选的前导和尾随图标的行。 ListTile在Card或ListView中最常用,但可以在别处使用。

ListTile摘要:

  • 包含最多3行文本和可选图标的专用行
  • 比Row更不易配置,但更易于使用
  • 来自材料组件库

ListTile示例:

包含3个ListTiles的卡片。 Dart代码:查看卡片示例

使用ListTile列出3个下拉按钮类型。 飞镖代码:来自Flutter Gallerybuttons_demo.dart

资源

编写布局代码时以下资源可能会有所帮助。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏向治洪

Android硬件加速介绍与实现

概述 在手机客户端尤其是Android应用的开发过程中,我们经常会接触到“硬件加速”这个词。由于操作系统对底层软硬件封装非常完善,上层软件开发者往往对硬件加速的...

25560
来自专栏GIS讲堂

唐宋文学诗人分布展示

本文的数据是从搜韵获取而来的,地址为http://sou-yun.com/poetlife.html,为方便大家使用,将数据保存到了百度云盘,下载信息为:链接:...

13630
来自专栏小灰灰

zxing二维码生成服务之深度定制

二维码生成服务之深度定制 之前写了一篇二维码服务定制的博文,现在则在之前的基础上,再进一步,花样的实现深度定制的需求,我们的目标是二维码上的一切都是可以由用户...

56260
来自专栏Android机动车

Android中的Vector

随着 Android 的碎片化越来越严重,适配成为一个开发中一个痛点。如果 UI 只切一套图,但是在一些特定机型上难免会出现模糊或者变形的情况,如果切多套不同分...

16940
来自专栏hightopo

原 荐 快速开发 HTML5 WebGL 的

95130
来自专栏数据小魔方

绩效管理工具(一)——仪表盘风格图表!

今天教大家制作用于绩效管理的仪表盘风格图表! ▽ 看惯了普通的图表,偶尔换换口味,用类似汽车仪表盘(量表)风格的图表来展示数据,也能起到新人瞩目,令人印象深刻的...

40250
来自专栏阿炬.NET

FineUIMvc表格数据库分页,使用CYQ.Data组件

42780
来自专栏逍遥剑客的游戏开发

Introduction to RenderMonkey

300100
来自专栏AndroidTv

属性动画 ValueAnimator 运行原理全解析

好,废话不多说,之前我们已经分析过 View 动画 Animation 运行原理解析,那么这次就来学习下属性动画的运行原理。

45980
来自专栏老司机的简书

CoreText实现图文混排之尺寸估算及文本选择

回头看看,距离CoreText系列首发过去一年也多了,看到第一篇文章即将超越1.3W的点击量老司机也是压力越来越大,毕竟作为瞎逼逼杰出代表的老司机偶尔也要正经一...

12220

扫码关注云+社区

领取腾讯云代金券