Flutter中构建布局

你将学到什么?html

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

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

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

创建布局github

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

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

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

常见的布局小部件编程

  • 标准小部件
  • 材料组件

资源浏览器

创建布局

若是您想要了解布局机制的“全貌”,请从Flutter的布局方法开始。网络

第0步:设置

首先,获取代码:app

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

  • 在项目顶部建立一个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,这是一种列状布局,若是其内容太长而没法适应可用空间,则会自动滚动。 有关更多信息,请参阅通用布局小部件

对齐小部件

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

MainAxisAlignmentCrossAxisAlignment类提供了用于控制对齐的各类常量。

注意:将图像添加到项目中时,须要更新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.dart
Images: 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

资源

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

相关文章
相关标签/搜索