[Flutter学徒] 2 - Hello Flutter

554 阅读19分钟

本文由 简悦 SimpRead 转码, 原文地址www.raywenderlich.com

在本章中,您将从头开始构建您的第一个Flutter应用程序,并掌握基本原理.

现在你已经对Flutter有了基本的认识,那么你已经准备好开始你的Flutter学习生涯了。你的第一个任务是从头开始建立一个基本的应用程序,让您能够掌握基本工具的使用和Flutter应用的基本结构。你会动手修改这个应用并了解如何使用一些常用的组件,如ListView和Slider。

创建一个简单的应用程序将让你看到用Flutter构建跨平台的应用程序是多么的快速和容易--你很快就能掌握它。

在本章结束时,你将建立一个轻量级的菜谱应用程序。由于你刚刚开始学习Flutter,这个应用程序将提供一个写死的食谱列表数据,你需要使用滑块根据份数重新设置总数。

下面是你将要完成的应用程序最终的样子:

开始这一章之前,你所需要的是将Flutter环境设置好。如果flutter doctor的结果没有错误,你就可以开始了。否则,请回到上一章,重新配置flutter环境。

创建一个新的应用程序 有两种简单的方法来启动一个新的Flutter应用程序。在上一章中,你通过IDE创建了一个新的应用程序项目。你也可以用flutter命令创建一个应用程序。这里你将使用第二个选项。

打开一个终端窗口,然后进入到你想要创建工程的地方。例如,你可以在本课程资料的文件夹下创建工程,跳转到flta-materials/02-hello-flutter/projects/starter/。

创建一个新项目是很简单。在终端上运行

flutter create recipes

这个命令在一个新的文件夹中创建一个新的应用程序,文件夹和应用程序的名字都叫recipes。项目中有基本的演示demo,支持在iOS和Android上运行,正如你在前一章看到的那样。

使用你的IDE,通过open an existing project选项打开recipes文件夹。

构建并运行,你会看到与第一章 "入门 "中相同的示例应用程序。

点击 "+"按钮可以增加计数器。

现在开始学习编写自己的程序吧

这个示例程序很不错 ,因为flutter create命令为它创建了完成的模板代码,使您能够启动和运行。但这并不是你自己的应用程序。正如你在main.dart的顶部看到的那样,它就是MyApp。

class MyApp extends StatelessWidget {

这定义了一个名为MyApp的Dart类,它继承(extends)或者说拓展(inherits)自StatelessWidget。在Flutter中,用户界面上几乎所有的东西都是Widget。一个StatelessWidget组件在你构建它之后就不会改变。在下一节中,你会学到更多关于Widget和State的知识。现在,就把MyApp看作是应用程序的容器。

由于你正在建立一个食谱应用程序,你的主类命名为MyApp就不合适了,把它命名为RecipeApp吧。

你可以手动改变类的名字,但通过使用IDE的重命名操作,可以避免在复制和粘贴的过程中出错,这个命令在会同时修改所有调用这个类的地方。

在Android Studio中,你可以在Refactor ▸ Rename菜单项下找到这个功能,或者在MyApp类中右击MyApp...并导航到Refactor ▸ Rename。把MyApp重命名为RecipeApp。结果会是这样的。

void main() {
  runApp(RecipeApp());
}
class RecipeApp extends StatelessWidget {

main()是应用程序启动时的代码入口。 runApp()告诉Flutter哪个是应用程序的顶级部件。

热重载对main函数不起作用,所以要想看到UI有没有变化需要重新构建和运行。 :]

注意:正如第1章 "入门 "中提到的,当你保存你的修改时,热重载会自动运行并更新UI。如果不起作用,检查你的IDE中的Flutter设置,确认保存时热重载这个选项是启用状态。

如果你更新了改变应用程序样式的代码,你只要进行热重启就可以了。但如果你做了重大改变,你必须停止并重启应用程序并重新构建。

改变应用程序的样子

为了继续把它变成一个新的应用程序,接下来你将定制你的小部件的UI。将RecipeApp的build()改为。

// 1
@override
Widget build(BuildContext context) {
  // 2
  return MaterialApp(
    // 3
    title: 'Recipe Calculator',
    theme:ThemeData(
      // 4
      primaryColor: Colors.white,
      accentColor: Colors.black
    ),
    // 5
    home:MyHomePage(title: 'Recipe Calculator');
  );
}

这段代码改变了应用程序的样子:

  1. 一个widget的build()方法可以将多个其他Widget组合在一起形成一个新的组件
  2. MaterialApp是一个使用Material Design的应用程序的顶级widget。
  3. 该应用的标题是设备用来区分各个app的。UI界面上不会展示。
  4. 主题决定了app的主体样式和颜色等等。在这里,主色(the primary color)是Colors.white,重点色(the accent color)是Colors.black。
  5. 这仍然使用与之前相同的MyHomePage部件,但现在,你已经更新了标题,设备上app的名称已经改了。

当你现在重新启动应用程序时,你会看到同样的小工具,但它们的风格更加复杂。

通过定制 "MaterialApp "主体,你已经迈出了使应用程序成为你自己的第一步。你将在下一节中完成对原有示例应用程序的清理。

清理应用程序

你已经设计了应用程序的主题,但它仍然显示着计数器的演示。清理屏幕是你的下一个步骤。首先,将_MyHomePageState类替换为。

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    // 1
    return Scaffold(
      // 2
      appBar: AppBar(
        title:Text(widget.title),
      ),
      // 3
      body: SafeArea(
        // 4
        child:Container();
        ),
    );
  }
}

快速看一下这显示了什么。

  1. Scaffold为一个应用程序提供了整体结构,在这里,我们使用了它的两个属性。
  2. AppBar 通过title字段设置了一个 "Text "组件的标题属性,该组件引用了上一步 "homeMyHomePage(title: 'Recipe Calculator') "中传入的 "title"属性。
  3. bodySafeArea,它使应用程序不会和一些系统组件冲突,如系统状态栏。
  4. SafeArea有一个child小组件,这是一个空的Container小组件。

一个热重载后,你就会留下一个干净的应用程序。

建立一个recipe list

一个空荡荡的recipe应用没什么意义。它应该有一个漂亮的食谱列表供用户滚动浏览。但是在你展示列表之前,你需要数据来填充用户界面。

添加数据模型

你将使用Recipe作为这个应用程序中食谱的主要数据结构。

lib文件夹中创建一个新的Dart文件,名为recipe.dart

在该文件中添加以下类。

class Recipe {
  String label;
  String imageUrl;

  Recipe(this.label, this.imageUrl);
}

这是一个带有标签和图片链接的数据模型。

你还需要为应用程序提供一些数据来显示。在一个正常的应用程序中,你一般从本地数据库或网络中获取这些数据。然而,为了简单起见,当你开始使用Flutter时,你将在本章中使用写死(hard-coded)的数据。

Recipe添加以下代码。

static List<Recipe> samples = [
  Recipe('Spaghetti and Meatballs',
      'assets/2126711929_ef763de2b3_w.jpg'),
  Recipe('Tomato Soup',
      'assets/27729023535_a57606c1be.jpg'),
  Recipe('Grilled Cheese',
      'assets/3187380632_5056654a19_b.jpg'),
  Recipe('Chocolate Chip Cookies',
      'assets/15992102771_b92f4cc00a_b.jpg'),
  Recipe('Taco Salad',
      'assets/8533381643_a31a99e8a6_c.jpg'),
  Recipe('Hawaiian Pizza',
      'assets/15452035777_294cefced5_c.jpg'),
];

这个static List<Recipe>是写死的。你以后会添加更多属性,但现在,它只是一个名字和图片的List。

注意: List是一个有序的数据集合;在一些编程语言中,它被称为数组。`List'的索引从0开始。

你已经创建了一个带有图片的List,但是你的项目中还没有任何图片。要添加它们,需要在Finder中把你项目资料中的02-hello-flutter下的assets文件夹复制到你的项目的根目录中。当你完成以后,assets应该与lib文件夹处于同一级别。这样,当你运行应用程序时,它就能找到这些图片。

你会注意到,在Finder中复制粘贴之后,文件夹和图片会自动显示在Android Studio项目列表中。

但是仅仅把资产添加到项目中并不能在应用程序中显示它们。要让应用程序找到这些图片,请打开Recipes项目根文件夹中的pubspec.yaml

在 "# To add assets to your application,.... "下添加以下几行。

assets:
  - assets/

这些行指定**assets/**是一个资产文件夹,必须包含在应用程序中。确保这里的第一行与上面的uses-material-design: true行对齐。

显示列表

在数据准备好后,你的下一步是为数据创建一个展示的地方。

main.dart中,你需要导入数据文件,这样main.dart中的代码才能找到它。在文件的顶部,在其他导入行下添加以下内容。

import 'recipe.dart';

接下来,在_MyHomePageState SafeArea的子程序中,用以下代码替换掉child:Container():

// 4
child:ListView.builder(
  // 5
  itemCount: Recipe.samples.length,
  // 6
  itemBuilder:(BuildContext context, int index) {
    // 7
    return Text(Recipe.samples[index].label);
  },
),

这段代码做了以下工作。

  1. 使用ListView建立一个列表。
  2. itemCount决定了列表的行数。在本例中,length是Recipe.samples列表中对象的数量。 itemBuilder为每一行建立widget树。
  3. 一个文本部件显示配方的名称。
  4. 现在执行热重载,你会看到下面的列表。

将列表放入卡片

你现在显示的是真实的数据,这很好,但这几乎不是一个应用程序。为了使事情变得更有趣,你需要添加图片来配合标题。

为了做到这一点,你将使用一个Card组件。在Material Design中,Card组件是用户界面的一个区域,在这个区域中,你可以展示一个特定实体的相关信息。例如,在一个音乐应用中的卡片可能会有专辑名称、艺术家和发行日期的标签,以及专辑封面的图片,也许还有一个用星星来评分的控件。

你的菜谱卡片将有菜谱的标签和图片。卡片的widget树有以下结构。

在main.dart中,在_MyHomePageState的底部,添加以下内容,使用buildRecipeCard()创建一个自定义的widget。

Widget buildRecipeCard(Recipe recipe) {
  // 1
  return Card(
    // 2
      child: Column(
        // 3
        children: <Widget>[
          // 4
          Image(image: AssetImage(recipe.imageUrl)),
          // 5
          Text(recipe.label),
        ],
      ),
  );
}

这里是你如何定义你的新的自定义卡片部件:

  1. 你从buildRecipeCard()返回一个Card。
  2. Card的child是一个Column。Column是一个定义垂直布局的widget。
  3. Column有两个child。
  4. 第一个child是一个图像部件。AssetImage说明图片是从pubspec.yaml中定义的本地资产包中获取的。
  5. 第二个child是一个文本小组件。它将包含recipe.label的值。

要使用该Card,请到_MyHomePageState,并将ListView itemBuilder的返回语句更新为这样。

return buildRecipeCard(Recipe.samples[index])。

这就指示itemBuilder为样本列表中的每个配方使用这个自定义卡片部件。

hot restart应用程序以看到图片和文本卡片。

请注意,卡片的底部默认并不是一个直角。Material Design提供了一个标准的圆角和阴影。

观察小部件树

现在是思考整个应用的widget树的好时机。你还记得它是从main()的RecipeApp开始的吗?

RecipeApp建立了一个MaterialApp,它又使用MyHomePage作为它的主页。这构建了一个带有AppBar和ListView的Scaffold。然后你更新了ListView的构建器,为每个items做了一个Card。

思考widget树有助于理解应用程序,因为布局变得越来越复杂,而且你会增加互动性。幸运的是,你不需要每次都手绘图表。

在Android Studio中,当你的应用程序运行时,从View ▸ Tool Windows ▸ Flutter Inspector菜单中打开Flutter Inspector。这将打开一个强大的UI调试工具。

这个视图向你显示屏幕上的所有小部件以及它们的组成方式。当你滚动时,你可以刷新树状图。你可能会注意到卡片的数量在变化。这是因为列表并没有把每一个项目都同时保留在内存中以提高性能。你会在后面的章节中详细介绍这一点。

让它看起来更漂亮

默认的卡片看起来还不错,但它们并不像它们可以做到的那样漂亮。有了一些额外的东西,你可以使卡片变得更漂亮。这包括用布局部件(如Padding)包裹部件,或指定额外的样式参数。

开始时,用buildRecipeCard()代替。

Widget buildRecipeCard(Recipe recipe) {
  return Card(
    // 1
    elevation: 2.0,
    // 2
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(10.0)),
    // 3
    child: Padding(
      padding: const EdgeInsets.all(16.0),
      // 4
      child: Column(
        children: <Widget>[
          Image(image: AssetImage(recipe.imageUrl)),
          // 5
          const SizedBox(
            height: 14.0,
          ),
          // 6
          Text(
            recipe.label,
            style: const TextStyle(
              fontSize: 20.0,
              fontWeight: FontWeight.w700,
              fontFamily: 'Palatino',
            ),
          )
        ],
      ),
    ),
  );
}

这有几个更新要看。

  1. 卡片的高度决定了卡片在屏幕上的高度,影响它的阴影。
  2. shape处理卡片的形状。这段代码定义了一个角半径为10.0的圆角。
  3. Padding通过指定的padding值嵌套其子节点的内容。
  4. padding的子节点仍然是Column。
  5. 在图像和文本之间是一个SizedBox。这是一个有固定尺寸的空白视图。
  6. 你可以用一个样式对象来定制文本部件。在这个例子中,你已经指定了一个Palatino字体,大小为20.0,字体粗细度为w700。

Hot reload,你会看到一个更有风格的列表。

你可以修改这些属性值,使列表看起来 "恰到好处"。通过热重载,你可以很容易地做出改变,并立即看到它们对运行中的应用程序的影响。

使用 Widget inspector,你会看到添加的Padding和SizedBox小部件。当你选择一个部件,比如SizedBox,它会在一个单独的窗格中显示它的所有实时属性,其中包括你明确设置的属性和那些被继承的或默认设置的属性。

选择一个小组件也会突出显示它的源码。

注意:你可能需要点击刷新树按钮来重新加载检查器中的小部件结构。详情见第4章,"了解小组件"。

添加一个配方的详细页面

你现在有一个漂亮的列表,但这个应用程序无法和用户互动。让它变得更好的是,当用户点击卡片时,向他们展示关于食谱的细节。你将通过让卡片对点击作出反应来开始实现这个功能。

添加点击事件

在_MyHomePageState中,找到ListView.builder()。将itemBuilder的返回语句替换为以下内容。

// 7
return GestureDetector(
  // 8
  onTap: () {
    // 9
    Navigator.push(context,
      MaterialPageRoute(
        builder: (context) {
        // 10
        return Text('Detail page');
      },
    ),
   );
  },
  // 11
  child: buildRecipeCard(Recipe.samples[index]),
);

这引入了一些新的Widget和概念。一个一个地看这几行。

  1. 引入了一个GestureDetector部件,顾名思义,它可以检测手势。
  2. 实现了一个onTap函数,当小组件被点击时,它被调用回调。
  3. Navigator小组件管理着一个页面栈。用MaterialPageRoute调用push()会把一个新的Material Page推到栈中。第三节,"Navigating Between Screens",将更详细地介绍Navigator。
  4. builder创建目标页面。
  5. GestureDetector的child定义了手势的活动区域。

Hot reload加载应用程序,现在每个卡片都可以被点击了。

创建一个实际的目标页面

点击产生的的页面显然只是一个占位符。它不仅难看,而且因为它没有所有正常的页面特征,用户现在被困在这里,特别在没有后退按钮的iOS设备上。不过别担心,你可以解决这个问题

在lib中,创建一个名为recipe_detail.dart的新Dart文件。

现在,将这段代码添加到该文件中。

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

class RecipeDetail extends StatefulWidget {
  final Recipe recipe;

  const RecipeDetail({Key? key, required this.recipe}) : super(key: key);

  @override
  _RecipeDetailState createState() {
    return _RecipeDetailState();
  }
}

这将创建一个新的StatefulWidget,它有一个初始化器,接收要显示的Recipe细节。这是一个StatefulWidget,因为你以后会在这个页面上添加一些交互式状态。

你需要_RecipeDetailState来构建这个小部件,所以接下来添加这个。

class _RecipeDetailState extends State<RecipeDetail> {

  @override
  Widget build(BuildContext context) {
    // 1
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.recipe.label),
      ),
      // 2
      body: SafeArea(
        // 3
        child: Column(
          children: <Widget>[
            // 4
            SizedBox(
              height: 300,
              width: double.infinity,
              child: Image(image:
              AssetImage(widget.recipe.imageUrl)),
            ),
            // 5
            const SizedBox(
              height: 4,
            ),
            // 6
            Text(
              widget.recipe.label,
              style: const TextStyle(fontSize: 18),
            ),
          ],
        ),
      ),
    );
  }
}

小部件的主体与你已经看到的一样。这里有几件事需要注意。

  1. Scaffold定义了页面的整体结构。
  2. 在body中,有一个SafeArea,包含一个Column,Column里面有一个包含两个SizedBox和一个Text的 children。
  3. SafeArea使应用程序不至于离操作系统组件太近,例如大多数iPhone的凹槽或互动区。
  4. 这里有一个新的Widget是包裹图像的SizedBox,它定义了图像的尺寸。在这里,高度固定为300,宽度会调整以保证长宽比不变。Flutter中的单位是逻辑像素(不是物理像素)。
  5. 有一个间隔的SizedBox。
  6. 标签的文本与Card中的文本的Style不一样,以显示你有多少可定制性。 接下来,回到main.dart,在文件的顶部添加以下一行。
import 'recipe_detail.dart';

然后,在_MyHomePageState里面,找到GestureDetector的onTap参数,把MaterialPageRoute的返回语句替换成。

return RecipeDetail(recipe: Recipe.samples[index]);

从菜单中选择 Run ▸ Flutter Hot Restart来执行热重启,将应用状态设置为原始列表。点击一个配方卡现在会显示RecipeDetail页面。

注意:你需要在这里使用热重启,因为热重载在你更新状态后不会更新UI。

因为您现在有一个带有appBar的支架,Flutter会自动包含一个返回按钮,让用户回到主列表。

添加成分

为了完成详细页面,你需要向配方类添加额外的细节。在这之前,你必须给菜谱添加一个配料表。

打开recipe.dart,添加以下类。

class Ingredient {
  double quantity;
  String measure;
  String name;

  Ingredient(this.quantity, this.measure, this.name);
}

这是一个成分的简单数据容器。它有一个名称、一个计量单位--如 "杯 "或 "汤匙"--和一个数量。

在Recipe类的顶部,添加以下内容。

int servings;
List<Ingredient> ingredients;

这就增加了一些属性,指定该菜可供多少人实用,而配料是一个简单的列表。

为了使用这些新的属性,在Recipe类中进入你的样本列表,将Recipe的构造函数从。

Recipe(this.label, this.imageUrl);

改为:

Recipe(this.label, this.imageUrl, this.servings, this.ingredients);

你会看到你的部分代码下有红色的斜线,因为servings和 ingredients的值还没有被设置。接下来你会解决这个问题。

为了包括新的servings和 ingredients属性,用下面的代码替换现有的samples代码。

static List<Recipe> samples = [
  Recipe(
    'Spaghetti and Meatballs',
    'assets/2126711929_ef763de2b3_w.jpg',
    4,
    [
      Ingredient(1, 'box', 'Spaghetti'),
      Ingredient(4, '', 'Frozen Meatballs'),
      Ingredient(0.5, 'jar', 'sauce'),
    ],
  ),
  Recipe(
    'Tomato Soup',
    'assets/27729023535_a57606c1be.jpg',
    2,
    [
      Ingredient(1, 'can', 'Tomato Soup'),
    ],
  ),
  Recipe(
    'Grilled Cheese',
    'assets/3187380632_5056654a19_b.jpg',
    1,
    [
      Ingredient(2, 'slices', 'Cheese'),
      Ingredient(2, 'slices', 'Bread'),
    ],
  ),
  Recipe(
    'Chocolate Chip Cookies',
    'assets/15992102771_b92f4cc00a_b.jpg',
    24,
    [
      Ingredient(4, 'cups', 'flour'),
      Ingredient(2, 'cups', 'sugar'),
      Ingredient(0.5, 'cups', 'chocolate chips'),
    ],
  ),
  Recipe(
    'Taco Salad',
    'assets/8533381643_a31a99e8a6_c.jpg',
    1,
    [
      Ingredient(4, 'oz', 'nachos'),
      Ingredient(3, 'oz', 'taco meat'),
      Ingredient(0.5, 'cup', 'cheese'),
      Ingredient(0.25, 'cup', 'chopped tomatoes'),
    ],
  ),
  Recipe(
    'Hawaiian Pizza',
    'assets/15452035777_294cefced5_c.jpg',
    4,
    [
      Ingredient(1, 'item', 'pizza'),
      Ingredient(1, 'cup', 'pineapple'),
      Ingredient(8, 'oz', 'ham'),
    ],
  ),
];


这就为这些项目填写了一个配料表。请不要在家里做这些,这些只是例子。]

现在热重新加载应用程序。没有任何变化是可见的,但它应该能成功构建。

显示成分

一份食谱如果没有配料,就没有什么用处。现在,你准备添加一个小部件来显示它们。

在recipe_detail.dart中,在最后一个文本后的栏目中添加以下内容。

// 7
Expanded(
  // 8
  child: ListView.builder(
    padding: const EdgeInsets.all(7.0),
    itemCount: widget.recipe.ingredients.length,
    itemBuilder: (BuildContext context, int index) {
      final ingredient = widget.recipe.ingredients[index];
      // 9
      return Text(
          '${ingredient.quantity} ${ingredient.measure} ${ingredient.name}');
    },
  ),
),


这段代码增加了。

  1. 一个Expand小组件,它可以填补剩下的所有空间。这样一来,配料表就会占据其他部件没有填充的空间。
  2. 一个ListView,每个原料有一行。
  3. 一个Text,使用string interpolation,用运行时的值填充一个字符串。你在字符串字面内使用${expression}的语法来表示这些。 通过选择 Run ▸ Flutter Hot Restart,并导航到一个详细的页面来查看成分。

干得好,现在屏幕上显示了配方名称和成分。接下来,你将添加一个功能,让用户可以互动。

添加一个服务滑块

你现在显示的是建议食用的成分。如果你能改变所需的数量,并让配料的数量自动更新,那不是很好吗?

你将通过添加一个Slider组件来实现这一点,允许用户调整份量。

首先,在_RecipeDetailState的顶部创建一个实例变量来存储滑块的值。

int _sliderVal = 1;

在Column内部,Expand部件之后,添加以下Slider部件。

Slider(
  // 10
  min: 1,
  max: 10,
  divisions: 10,
  // 11
  label: '${_sliderVal * widget.recipe.servings} servings',
  // 12
  value: _sliderVal.toDouble(),
  // 13
  onChanged: (newValue) {
    setState(() {
      _sliderVal = newValue.round();
    });
  },
  // 14
  activeColor: Colors.green,
  inactiveColor: Colors.black,
),

Slider呈现一个圆形的拇指,可以沿着轨道拖动来改变一个值。下面是它的工作原理。

  1. 你用min、max和divisions来定义滑块的移动方式。在这个例子中,它在1和10的值之间移动,有10个不连续的停止点。也就是说,它只能有1、2、3、4、5、6、7、8、9或10的值。
  2. 标签随着_sliderVal的变化而更新,并显示一个扩大展示serving数量。
  3. 滑块默认用double工作,所以这就转换成int变量。
  4. 反之,当滑块发生变化时,这里使用round()将double型的滑块值转换为int,然后将其保存在_sliderVal中。
  5. 这将滑块的颜色设置为更突出的颜色。activeColor是最小值和游标之间的部分,而inactiveColor代表其余部分。

Hot reload 加载应用程序,调整滑块并观察反映在指标上的值。

更新配方

看到改变后的值反映在滑块上是很好的,但现在,它并不影响配方本身。

要做到这一点,你只需要改变扩展成分itemBuilder的返回语句,将_sliderVal的当前值作为每个成分的一个因素。

return Text('${ingredient.quantity * _sliderVal} '
                      '${ingredient.measure} '
                      '${ingredient.name}');

hot reload后,你会看到,当你移动滑块时,食谱的成分会发生变化。

就这样。你现在已经建立了一个很酷的、交互式的Flutter应用程序,并且它在在iOS和Android上的工作方式是一样的。

在接下来的几节中,您将继续探索部件和状态如何工作。你还会了解到一些重要的功能,如网络。

关键点

  • 用flutter创建一个新的应用程序。
  • 使用widget,用控件和布局组成一个屏幕。
  • 使用widget参数进行造型。
  • 一个MaterialApp部件指定了应用程序,而Scaffold指定了一个特定屏幕的高级结构。 状态允许交互式部件。
  • 当状态改变时,你通常需要热重启应用程序,而不是热重载。在某些情况下,你可能还需要完全重建和重启应用程序。

接下来该怎么做?

恭喜你,你已经写出了你的第一个应用程序

为了了解所有可用的widget选项,api.flutter.dev/ 的文档应该是你的起点。特别是,Material库api.flutter.dev/flutter/mat… 和Widget库api.flutter.dev/flutter/wid… 它们将涵盖大部分你可以放在屏幕上的东西。这些页面列出了所有的参数,并且经常有浏览器内的互动部分,你可以进行实验。

第3章,"基本部件",是关于使用部件的所有内容,第4章,"理解部件",更详细地介绍了部件背后的理论。未来的章节将更深入地讨论本章中简要介绍的其他概念。


www.deepl.com 翻译