---
description: Flutter
---

# Flutter App

<details>

<summary>Flutter度量转换 App</summary>

### 介绍

度量转换应用程序将允许您的用户选择度量（公制或英制）并将其转换为其他度量。 例如，他们可以将以英里为单位的距离转换为以千米为单位的距离，或者将以千克为单位的重量转换为以磅为单位的重量。

以下是您将在本文中构建的项目的最终布局：

### 创建项目

在您最喜欢的编辑器中，创建一个新应用。 将新应用命名为Unit Converter。

在main.dart文件中，删除示例代码并编写如下所示的代码：

您可能已经注意到，前面的代码使用了一个无状态小部件：

无状态小部件是扩展StatelessWidget的类。 扩展StatelessWidget类需要重写build（）方法。

在build（）方法中，您描述该方法返回的窗口小部件：

带有上下文并返回小部件的build（）方法：

#### 使用有状态小部件

现在，将MyApp类转换为有状态的小部件，以便您可以看到该类的不同实现：

您可以立即看到两个错误。 如果将鼠标悬停在MyApp类上，则看到的错误是“缺少StatefulWidget.createState的具体实现”，如果将鼠标悬停在build方法上，则会看到“该方法不会覆盖继承的方法。”

这些错误试图告诉我们：

1. 有状态窗口小部件需要createState（）方法。
2. 在有状态窗口小部件中，没有要覆盖的build（）方法。

解决方法是：

1. 添加必要的createState（）方法，该方法将返回MyAppState，稍后我们将创建该方法。 在MyApp类的定义下，编写以下代码：
2. 创建一个名为MyAppState的新类，该类扩展State，尤其是MyApp的State：
3. 为了解决第二个错误（“缺少State.build的具体实现”），剪切MyApp类中现在的build（）方法，并将其粘贴到MyAppState类中。 修改后的代码应如下所示：

概括起来，从语法的角度来看，无状态小部件和有状态小部件之间的区别在于，前者覆盖了build（）方法并返回了一个小部件，而有状态小部件覆盖了createState（）方法，而该方法返回了一个State 。 State类将覆盖build（）方法，并返回一个小部件。

从功能的角度来看，在我们编写的代码中，两者之间没有任何区别，因为在两种情况下，应用程序的外观和行为都完全相同。 因此，让我们添加一个需要有状态小部件的功能，而使用无状态小部件则无法实现。

在这里，您可以看到到目前为止的应用布局：

#### 从TextField读取用户输入

#### 代码诠释

#### 创建下拉小部件

#### 代码诠释

#### 更新下拉小部件

#### 代码诠释

### 源代码

</details>

<details>

<summary>Flutter作息定时器 app</summary>

### 介绍

在以下屏幕截图中，您可以看到我们将在第一部分中构建的布局。 为了使您更容易理解该布局需要做什么，我添加了边框，以显示小部件将如何放置在屏幕上：

我相信构建此布局的最简单方法是结合使用列和行小部件。 此屏幕中的主容器窗口小部件将是一列，将空间分成三部分，如下所示：

1. 顶部的三个按钮：“工作”，“短暂休息”和“长时间休息”
2. 中间的计时器
3. 底部的两个按钮：“停止”和“重新启动”

在您最喜欢的编辑器中，创建一个新应用。

将新应用命名为production\_timer

在main.dart文件中，删除示例代码。

输入以下代码：

这段代码创建了一个基本的脚手架，这是我们大多数屏幕的基本布局，并在AppBar（我的工作计时器）中放置了一个标题，并在主体的中央放置了一个文本（同样是My Work Timer）。 结果应类似于以下屏幕截图：

接下来，让我们在main.dart文件底部为屏幕布局创建一个类，而不仅仅是返回Text。 我们将其称为TimerHomePage（）。 如果您使用的是VS Code，Android Studio或IntelliJ IDEA，则还可以使用stless快捷方式使框架编写代码的一部分。 MyApp类结束后，只需键入stless。

至于类名，让我们选择TimerHomePage。 最终结果应如下：

在build（）方法中，不返回容器，而是从MyApp类中移动Scaffold：在appBar中，我们将显示应用程序的标题，在主体中将显示包含Column的Center小部件。 在TimerHomePage类中添加以下代码：

我们可以通过调用刚刚创建的新类来简化MyApp类的build（）方法上的代码，如下所示：

如果您立即尝试使用该应用程序，则应该仍然像以前一样看到一个空白屏幕，显示“我的工作计时器”应用程序栏标题。

现在，我们准备开始在屏幕上放置小部件。 由于我们需要构建五个具有非常相似功能的按钮小部件，因此最好为这些小部件创建一个新类，以保持其余代码的简洁性并节省一些键入操作。

让我们在应用程序的lib文件夹中创建一个名为widgets.dart的新文件，如下所示：

在这里，我们将创建一个名为ProductivityButton的新无状态小部件。 这将显示四个字段：颜色，文本，大小和Callback方法，以及用于设置值的构造函数。 小部件的代码如下：

您可能已经注意到，参数包含在大括号（{}）中，并带有@required批注。 这是因为我们在这里使用命名参数。 使用命名参数的目的是在调用函数并传递值时，还指定要设置的参数的名称。 例如，当创建ProductivityButton的实例时，可以使用语法ProductivityButton（颜色：Colors.blueAccent，文本：“ Hello World”，onPressed：doSomething，大小：150）。 由于命名参数是按名称引用的，因此可以按任何顺序使用它们。

命名参数是可选的，但是您可以使用@required注释对它们进行注释，以指示该参数是必需的。

现在，我们已经创建了通用按钮小部件，我们需要在屏幕上放置按钮的一些实例。

顶部按钮应放在屏幕顶部的单行中。 它们应占据所有可用的水平空间，并为边缘留出一些空间，并且它们应根据屏幕的大小和方向来改变其宽度。

创建一个临时的空方法，以使方法可以传递给按钮。 我们稍后将其删除。 在MyApp类的底部添加以下代码：

在MyApp类的顶部，让我们为要在屏幕上使用的默认填充声明一个常量，如下所示：

现在，让我们在屏幕上放置顶部按钮：我们需要在此处使用Row小部件，并将其作为Column小部件的第一个元素。 在Flutter中，实际上可以将“行”窗口小部件包含到“列”窗口小部件中，反之亦然。

我们希望按钮占用所有可用的水平空间。 为此，我们将使用扩展的小部件，该小部件在放置固定元素之后会占用列（或行）的所有可用空间。 每个按钮都有一个前导和尾随的填充，以在元素之间创建一些空间。 编写代码以将前三个按钮添加到屏幕，如下所示：

尝试该应用。 前面的代码的结果应类似于以下屏幕截图：

计时器应放置在屏幕中间，并在放置大小固定的顶部和底部行之后占用所有剩余空间。 现在，我们将仅在“列”小部件下使用“ Hello”文本作为占位符。 请注意，在这种情况下，在列而不是行中使用了Expanded，因此它占用了所有垂直可用空间，如以下代码段所示：

然后，我们将剩余的两个按钮“停止”和“重新启动”放置在屏幕底部，它们还将占据所有水平空间，除了它们和屏幕边框之间的一些填充外，如以下代码所示 块：

最终结果应类似于以下屏幕截图：

### 在您的应用中安装percent\_indicator软件包

### 在Flutter中使用流和异步编程

#### 在主屏幕上显示时间：StreamBuilder

#### 启用按钮

### 导航到设置路线

### 建立设置屏幕布局

### 使用shared\_preferences读写应用程序数据

### 源代码

</details>

<details>

<summary>Flutter爬取网页数据App</summary>

### 介绍

在本文中，我们将构建一个Movies应用程序。 用户打开它后，该应用程序将显示电影院中即将上映的电影列表。 用户还可以在同一屏幕上按标题搜索电影，如下所示：

如果他们点击其中一部电影，则该应用将显示第二个屏幕，该屏幕是电影的更详细视图，具有较大的图像和概述：

为了检索数据，该应用程序将使用一个开放的Web服务，即电影数据库API。

### 连接到Web服务并使用HTTP检索数据

很少有移动应用程序完全独立于外部数据：想想您用于天气预报，听音乐，阅读书籍，新闻或电子邮件的应用程序。 它们都有一个共同点：它们依赖于从外部来源获取的数据。 从移动（或任何客户端）应用程序获取数据的最常见来源称为Web服务或Web API。

发生的情况是，客户端应用程序连接到Web服务，发出获取数据的请求，如果请求是合法的，则Web服务通过将数据发送到该应用程序进行响应，然后该应用程序将解析数据以了解其功能。 这种方法的优势在于，开发人员只需要创建和维护一个数据源，即可拥有所需数量的客户端。 实际上，这种模式（客户端/服务器）并不是什么新鲜事物，但在设计应用程序时极为常见。

在下面，您可以看到显示此模式的图。 在中心，您有一个远程服务器，它是数据源，它周围是连接到服务器以检索数据的客户端，例如您的移动应用程序：

Web服务通常以两种格式公开数据：JSON或XML。 它们都是文本格式，几乎可以表示相同类型的数据，但是由于JSON更为紧凑，因此使用Web服务时，您可能会经常发现该格式。

在以下屏幕截图中，您可以看到每种格式的示例：

在这两种格式中，您都可以看到电影的示例，其中包括标题，电影的制作年份，类型和演员。 我们不需要深入研究这些格式； 请注意，两种格式都可以表达复杂的数据，在Flutter中，您可以轻松地检索和解析JSON和XML。 我们将在本章中使用的服务提供JSON。

特别是，我们将使用电影数据库API（[https://www.themoviedb.org](https://www.themoviedb.org)）。 这是一个由社区构建的数据库，其中包含大量数据，可提供多种语言的电影和电视信息。

首次连接数据库之前，您需要获取一个API密钥。 您可以通过在https://www.themoviedb.org/上创建一个帐户，然后单击帐户页面左侧栏中的API链接来获取API密钥。 这是免费的，但需要有效的电子邮件来激活帐户。 还需要具有API密钥才能遵循本章中的示例。

#### 创建应用程序并使用HTTP库连接到API

要做的第一件事是打开pubspec.yaml文件，并向HTTP库添加一个依赖关系，我们将使用该依赖关系进行HTTP请求。 请通过[https://pub.dev/packages/](https://pub.dev/packages/)查看最新的可用版本。 在flutter依赖项下添加http库，如下所示：

```dart
dependencies:
 flutter:
 sdk: flutter
 http: ^0.12.0+4

```

然后，我们创建一个名为http\_helper.dart的新文件，该文件将用于创建用于连接到Web服务的设置和方法。 在新文件中，让我们导入HTTP库：

```dart
import 'package:http/http.dart' as http;

```

使用as http命令，我们给库命名，因此，我们将通过http名称使用HTTP库的所有函数和类。

然后，我们创建一个新类，我们将其称为HttpHelper：

```dart
class HttpHelper {}

```

下一步是创建地址URL以连接到服务。 这将需要一些字符串，我们将根据需要将其连接起来以从服务中检索数据。 在HttpHelper类的顶部添加以下声明：

```dart
 final String urlKey = 'api_key=YOUR API KEY HERE';
 final String urlBase = '<https://api.themoviedb.org/3/movie>';
 final String urlUpcoming = '/upcoming?';
 final String urlLanguage = '&language=en-US';

```

为了更轻松地检索这些字符串的值，它们都以url前缀开头。 第一个字符串urlKey包含值api\_key =和您从电影数据库服务获得的API密钥。 urlBase字符串是我们将要使用的每个地址的开头。 urlUpcoming是特定于即将上映电影的URL的一部分。 最后，urlLanguage包含一个可选参数，该参数允许您指定要用于查询结果的语言

现在，我们准备编写此类的第一个方法，该方法将用于检索20部即将上映的电影的列表：

```dart
Future<String> getUpcoming() async { }

```

您可能会在这里注意到一个不熟悉的语法。 getUpcoming（）函数返回一个future并被签名为异步。 这两个元素都与异步编程有关。

让我们从Web服务检索一些数据：

在getUpcoming方法中，让我们添加一个字符串来创建将在连接期间使用的URL：

```dart
final String upcoming = urlBase + urlUpcoming + urlKey +urlLanguage;

```

接下来，让我们使用HTTP库来创建到我们已建立的URL的连接：

```dart
http.Response result = await http.get(upcoming);

```

http类的get方法返回一个包含Response的Future。 http.Response类包含从成功的HTTP调用接收到的数据。

await关键字仅在标记为异步的函数中起作用，它等待Future完成。 在完成这一行之前，它不会转到线程的下一行，其行为类似于同步代码，但是请记住，这发生在执行的第二行上，因此不会停止UI线程。

现在，我们如何阅读响应？ 让我们编写以下代码：

```dart
if (result.statusCode == HttpStatus.ok) {
 String responseBody = result.body;
 return responseBody;
}
else {
 return null;
}

```

HttpStatus类需要dart:io库。 在http\_helper.dart文件的顶部，添加所需的导入：

```dart
import 'dart:io';

```

响应具有statusCode和body属性。 状态代码可以表示成功的响应，它是HttpStatus.ok的代码200或错误。 您可能熟悉错误404； 在Dart中，您只需使用HttpStatus.notFound来表达它。

在前面的代码中，如果响应具有有效的状态码，我们将读取响应的主体，该主体是一个字符串，其中包含通过http.get方法检索的所有数据，并将其返回给调用方。

总结起来，我们现在有了一个异步函数，该函数发出一个HTTP请求并返回一个包含字符串的Future。 现在我们需要从main方法调用此函数，并将结果显示给用户。 接下来，让我们开始。

### 解析JSON数据并将其转换为模型对象

#### 添加模型类

### 添加列表数据显示

#### 显示尾随图标

### 显示详细信息屏幕并传递数据

### 添加搜索功能

</details>

<details>

<summary>Flutter使用SQLite储存数据应用实例</summary>

### 介绍

我们将在本文中构建的Shopping List应用程序由两个屏幕组成。 用户打开应用程序时将看到的第一个屏幕显示购物清单，如以下屏幕截图所示：

列表中的每个项目都有一个优先级，该优先级是您在前面的屏幕截图的左侧看到的数字，名称（面包店，水果等），以及在右侧的编辑按钮。 当您在列表中的任何项目上滑动时，该项目将被删除，而当您点击编辑按钮时，应用程序将显示一个编辑对话框屏幕，允许您编辑购物清单名称和优先级，如图所示。 以下屏幕截图：

当您点击其中一个购物清单时，您将进入应用程序的第二个屏幕，该屏幕显示另一个清单：所选购物清单中包含的商品。 例如，如果您点击“水果”购物清单，则会看到以下项目：桔子和苹果，如以下屏幕截图所示：

列表中的每个项目都会有一个名称，数量和注释。 该屏幕的功能将类似于第一个屏幕：您将能够通过点击浮动操作按钮（FAB）来添加新项目，通过点击编辑按钮来编辑项目，以及通过滑动来从列表中删除项目 列表中的任何元素。

### 使用SQLite数据库

### 创建模型类

#### 测试数据库

#### 展示数据库数据

#### 插入和编辑数据

#### 删除项

### 完成项目屏幕功能

### 源代码

</details>

{% content-ref url="../zhi-dian-mi-jin-brief" %}
[zhi-dian-mi-jin-brief](../zhi-dian-mi-jin-brief)
{% endcontent-ref %}
