要开始使用 Flutter,你需要对 Dart 编程语言(Flutter 应用程序是用它编写的)和 widgets(Flutter UI 的构建块)有所了解。本页将介绍这两者,但你将在本系列的整个过程中继续学习它们。本页提供了额外的资源,但你不需要成为这两个主题的专家即可继续学习。

组件

#

关于 Flutter,你经常会听到“一切皆 widget”。Widgets 是 Flutter 应用程序用户界面的构建块,每个 widget 都是用户界面一部分的不可变声明。Widgets 用于描述用户界面的所有方面,包括文本和按钮等物理方面,以及填充和对齐等布局效果。

Widgets 基于组合形成一个层次结构。每个 widget 都嵌套在其父级中,并可以从父级接收上下文。这个结构一直延伸到根 widget,如下面的简单示例所示:

dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp( // Root widget
      home: Scaffold(
        appBar: AppBar(
          title: const Text('My Home Page'),
        ),
        body: Center(
          child: Builder(
            builder: (context) {
              return Column(
                children: [
                  const Text('Hello, World!'),
                  const SizedBox(height: 20),
                  ElevatedButton(
                    onPressed: () {
                      print('Click!');
                    },
                    child: const Text('A button'),
                  ),
                ],
              );
            },
          ),
        ),
      ),
    );
  }
}

在前面的代码中,所有实例化类都是 widgets:MaterialAppScaffoldAppBarTextCenterBuilderColumnSizedBoxElevatedButton

Widget 的组合

#

如前所述,Flutter 强调 widget 作为组合单元。Widgets 通常由许多其他小型的、单一用途的 widgets 组成,这些 widgets 结合起来可以产生强大的效果。

有布局 widgets,如 PaddingAlignmentRowColumnGrid。这些布局 widgets 没有自己的视觉表示。相反,它们唯一的目的是控制另一个 widget 布局的某些方面。Flutter 还包括利用这种组合方法的实用 widgets。例如,Container 是一个常用的 widget,它由多个负责布局、绘制、定位和大小调整的 widgets 组成。一些 widgets 具有视觉表示,如前面示例中的 ElevatedButtonText,以及 IconImage 等 widgets。

如果你运行前面示例中的代码,Flutter 会绘制一个按钮,上面写着“Hello, World!”,居中显示在屏幕上,垂直布局。为了定位这些元素,有一个 Center widget,它将其子级定位在可用空间的中心,以及一个 Column widget,它将其子级一个接一个地垂直布局。

A diagram that shows widget composition with a series of lines and nodes.

在本系列的下一页中,你将学习更多关于 Flutter 中的布局。

构建 widgets

#

要在 Flutter 中创建用户界面,你需要覆盖 widget 对象上的 build 方法。所有 widget 都必须有一个 build 方法,并且它必须返回另一个 widget。例如,如果你想在屏幕上添加一些带有填充的文本,你可以这样编写:

dart
class PaddedText extends StatelessWidget {
  const PaddedText({super.key});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: const Text('Hello, World!'),
    );
  }
}

当这个 widget 创建时以及当这个 widget 的依赖项发生变化时(例如传递给 widget 的状态),框架会调用 build 方法。这个方法可能会在每一帧中调用,并且除了构建 widget 之外不应该有任何副作用。要了解更多关于 Flutter 如何渲染 widgets 的信息,请查看 Flutter 架构概述

Widget 状态

#

框架引入了两大类 widget:无状态和有状态 widgets。

没有可变状态(它们没有随时间变化的类属性)的 widgets 继承自 StatelessWidget。许多内置 widgets 都是无状态的,例如 PaddingTextIcon。当你创建自己的 widgets 时,大部分时间都会创建 Stateless widgets。

另一方面,如果 widget 的独特特性需要根据用户交互或其他因素而改变,那么该 widget 就是有状态的。例如,如果一个 widget 有一个计数器,每当用户点击一个按钮时它就会递增,那么计数器的值就是该 widget 的状态。当该值改变时,widget 需要重新构建以更新其 UI 的一部分。这些 widgets 继承自 StatefulWidget,并且(因为 widget 本身是不可变的)它们将可变状态存储在一个单独的类中,该类继承自 StateStatefulWidgets 没有 build 方法;相反,它们的 UI 是通过它们的 State 对象构建的,如下面的示例所示。

dart
class CounterWidget extends StatefulWidget {
  @override
  State<CounterWidget> createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
  
  @override
  Widget build(BuildContext context) {
    return Text('$_counter');
  }
}

无论何时你修改 State 对象(例如,通过递增计数器),你都必须调用 setState 来通知框架通过再次调用 Statebuild 方法来更新用户界面。

将状态与 widget 对象分离,可以让其他 widgets 以完全相同的方式处理无状态和有状态 widgets,而无需担心状态丢失。父级无需为了保留子级状态而持有子级,而是可以随时创建子级的新实例而不会丢失子级的持久状态。框架会在适当的时候完成查找和重用现有状态对象的所有工作。

本系列稍后将在状态管理课程中提供有关 StatefulWidget 对象的更多信息。

需要了解的重要 widgets

#

Flutter SDK 包含许多内置 widgets,从最小的 UI 片段(如 Text)到布局 widgets,以及为你的应用程序设置样式的 widgets。以下 widgets 是你在进入学习路径的下一课时需要了解的最重要的 widgets。

Widget 预览器

#

你可以实时查看你的 widgets 渲染,而无需完整的应用程序。要了解更多信息,请参阅 Flutter Widget 预览器指南。

下一页:布局

#

本页介绍了 Flutter 的基础概念,例如 widgets,并帮助你熟悉阅读 Flutter 和 Dart 代码。如果你对遇到的所有主题都不清楚也没关系,因为在此之后的每一页都会深入探讨特定主题。在下一节中,你将通过在 Flutter 中创建更复杂的布局来开始构建更有趣的用户界面。

如果你想练习本页学到的信息,可以阅读 使用 Flutter 构建用户界面

反馈

#

由于网站的这一部分正在不断发展,我们欢迎你的反馈