Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[完成翻译] src/content/cookbook/effects/download-button.md #1485

Merged
merged 4 commits into from
Aug 24, 2024
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 117 additions & 2 deletions src/content/cookbook/effects/download-button.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
---
# title: Create a download button
title: 创建一个带进度条的下载按钮
title: Create a download button
标题:创建一个下载按钮
description: How to implement a download button.
描述:如何实现下载按钮
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved
js:
- defer: true
url: /assets/js/inject_dartpad.js
Expand All @@ -19,18 +20,36 @@ to provide this feedback. In this recipe,
you'll build a download button that transitions through
multiple visual states, based on the status of an app download.

应用程序中充满了执行长时间操作的按钮。
例如,一个按钮可能会触发下载,
启动下载过程,并在一段时间内接收数据,
然后提供对下载资源的访问。
向用户显示长时间运行过程的进度是很有帮助的,
而按钮本身是提供此反馈的好地方。
在这个示例中,你将构建一个下载按钮,
根据应用程序下载的状态在多个视觉状态之间切换。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

The following animation shows the app's behavior:

下面的动画展示了应用程序的行为:

![The download button cycles through its stages](/assets/images/docs/cookbook/effects/DownloadButton.gif){:.site-mobile-screenshot}

## Define a new stateless widget

## 定义一个新的 stateless widget

Your button widget needs to change its appearance over time.
Therefore, you need to implement your button with a custom
stateless widget.

你的button widget需要随着时间的推移改变其外观。
因此,你需要使用 custom stateless widget来实现你的按钮。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

Define a new stateless widget called `DownloadButton`.

定义一个名为 `DownloadButton` 的新stateless widget。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

<?code-excerpt "lib/stateful_widget.dart (DownloadButton)"?>
```dart
@immutable
Expand All @@ -49,12 +68,19 @@ class DownloadButton extends StatelessWidget {

## Define the button's possible visual states

## 定义按钮的可能的视觉状态
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

The download button's visual presentation is based on a
given download status. Define the possible states of
the download, and then update `DownloadButton` to accept
a `DownloadStatus` and a `Duration` for how long the button
should take to animate from one status to another.

下载按钮的视觉呈现基于给定的下载状态。
首先定义下载可能的状态,然后更新 `DownloadButton`
以接受一个 `DownloadStatus` 和一个 `Duration`,
用于指定按钮从一个状态动画过渡到另一个状态所需的时间。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

<?code-excerpt "lib/visual_states.dart (VisualStates)"?>
```dart
enum DownloadStatus {
Expand Down Expand Up @@ -86,6 +112,7 @@ class DownloadButton extends StatelessWidget {
```

:::note

Each time you define a custom widget,
you must decide whether all relevant
information is provided to that widget
Expand All @@ -102,21 +129,43 @@ By passing in all the relevant information,
you ensure greater reusability for the widget,
easier testing, and easier changes to application
behavior in the future.

每次定义custom widget时,
你都必须决定是将所有相关信息从其parent widget传递给该组件,
还是让该组件在其内部协调应用程序行为。
例如,`DownloadButton` 可以从其parent widget接收当前的 `DownloadStatus`,
也可以在其 `State` 对象内部管理下载过程。
对于大多数组件来说,最佳做法是将相关信息从parent widget传递到组件内部,
而不是在组件内部管理行为。
通过传递所有相关信息,
你可以确保组件具有更高的可重用性、更容易进行测试,
以及在未来更容易更改应用程序行为。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

:::

## Display the button shape

## 显示按钮形状

The download button changes its shape based on the download
status. The button displays a grey, rounded rectangle during
the `notDownloaded` and `downloaded` states.
The button displays a transparent circle during the
`fetchingDownload` and `downloading` states.

下载按钮会根据下载状态改变其形状。
在 `notDownloaded` 和 `downloaded` 状态下,按钮显示为灰色的圆角矩形。
而在 `fetchingDownload` 和 `downloading` 状态下,按钮显示为透明的圆形。

Based on the current `DownloadStatus`,
build an `AnimatedContainer` with a
`ShapeDecoration` that displays a rounded
rectangle or a circle.

根据当前的 `DownloadStatus`,
构建一个带有 `ShapeDecoration` 的 `AnimatedContainer`,
该装饰可以显示圆角矩形或圆形。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

Consider defining the shape's widget tree in a separated
`Stateless` widget so that the main `build()`
method remains simple, allowing for the additions
Expand All @@ -126,8 +175,18 @@ like `Widget _buildSomething() {}`, always prefer creating a
considerations on this can be found in the [documentation]({{site.api}}/flutter/widgets/StatelessWidget-class.html)
or in a dedicated video in the Flutter [YouTube channel]({{site.yt.watch}}?v=IOyq-eTRhvo).

建议将形状的widget tree定义在一个单独的 `Stateless` 组件中,
这样主 `build()` 方法可以保持简洁,便于后续添加功能。
与其创建一个返回组件的函数(例如 `Widget _buildSomething() {}`),
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved
更好的做法是创建一个 `StatelessWidget` 或 `StatefulWidget`,这在性能上更优。
有关更多的考虑因素,你可以在 [文档]({{site.api}}/flutter/widgets/StatelessWidget-class.html) 中找到,
或者在 Flutter 的 [YouTube 频道]({{site.yt.watch}}?v=IOyq-eTRhvo) 上观看相关视频。

For now, the `AnimatedContainer` child is just a `SizedBox` because we will come back at it in another step.

目前,`AnimatedContainer` 的child widget只是一个 `SizedBox`,
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved
因为我们将在后续步骤中再来处理它。

<?code-excerpt "lib/display.dart (Display)"?>
```dart
@immutable
Expand Down Expand Up @@ -213,17 +272,32 @@ ending shape of a circle. But, you don't want the final
circle to be visible, so you make it transparent,
which causes an animated fade-out.

你可能会疑惑,为什么需要为一个透明的圆形使用 `ShapeDecoration` 组件,毕竟它是不可见的。
这个透明圆形的目的是为了协调动画效果。`AnimatedContainer` 一开始显示为圆角矩形。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved
当 `DownloadStatus` 变为 `fetchingDownload` 时,
`AnimatedContainer` 需要从圆角矩形动画过渡到圆形,并在动画进行的过程中逐渐淡出。
实现这种动画的唯一方法是定义圆角矩形的起始形状和圆形的结束形状。
但是,你不希望最终的圆形可见,所以将其设置为透明,这样就能实现动画淡出的效果。

## Display the button text

## 显示按钮文本

The `DownloadButton` displays `GET` during the
`notDownloaded` phase, `OPEN` during the `downloaded`
phase, and no text in between.

`DownloadButton` 在 `notDownloaded` 阶段显示 `GET`,
在 `downloaded` 阶段显示 `OPEN`,在中间阶段没有文本。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

Add widgets to display text during each download phase,
and animate the text's opacity in between. Add the text
widget tree as a child of the `AnimatedContainer` in the
button wrapper widget.

添加用于显示每个下载阶段文本的组件,并在阶段之间动画化文本的透明度。
将text widget tree作为 `AnimatedContainer` 的child widget添加到按钮包装组件中。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

<?code-excerpt "lib/display_text.dart (DisplayText)"?>
```dart
@immutable
Expand Down Expand Up @@ -283,17 +357,27 @@ class ButtonShapeWidget extends StatelessWidget {

## Display a spinner while fetching download

## 下载时显示一个进度条
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

During the `fetchingDownload` phase, the `DownloadButton`
displays a radial spinner. This spinner fades in from
the `notDownloaded` phase and fades out to
the `fetchingDownload` phase.

在 `fetchingDownload` 阶段,`DownloadButton` 会显示一个径向进度条。
这个进度条从 `notDownloaded` 阶段淡入,到 `fetchingDownload` 阶段淡出。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

Implement a radial spinner that sits on top of the button
shape and fades in and out at the appropriate times.

实现一个位于按钮形状之上的径向进度条,并在适当的时间淡入和淡出。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

We have removed the `ButtonShapeWidget`'s constructor to keep the
focus on its build method and the `Stack` widget we've added.

我们删除了 `ButtonShapeWidget` 的构造函数,
以便集中精力关注其 build 方法和添加的 `Stack` 组件。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

<?code-excerpt "lib/spinner.dart (Spinner)"?>
```dart
@override
Expand Down Expand Up @@ -328,19 +412,30 @@ Widget build(BuildContext context) {

## Display the progress and a stop button while downloading

## 下载时显示进度和停止按钮

After the `fetchingDownload` phase is the `downloading` phase.
During the `downloading` phase, the `DownloadButton`
replaces the radial progress spinner with a growing
radial progress bar. The `DownloadButton` also displays a stop
button icon so that the user can cancel an in-progress download.

在 `fetchingDownload` 阶段之后,是 `downloading` 阶段。
在 `downloading` 阶段,`DownloadButton` 会用增长型的径向进度条替换径向进度条。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved
`DownloadButton` 还会显示一个停止按钮图标,以便用户可以取消正在进行的下载。

Add a progress property to the `DownloadButton` widget,
and then update the progress display to switch to a radial
progress bar during the `downloading` phase.

将一个进度属性添加到 `DownloadButton` 组件中,
然后在 `downloading` 阶段切换到径向进度条。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

Next, add a stop button icon at the center of the
radial progress bar.

接下来,在径向进度条的中心添加一个停止按钮图标。

<?code-excerpt "lib/stop.dart (StopIcon)"?>
```dart
@override
Expand Down Expand Up @@ -386,16 +481,24 @@ Widget build(BuildContext context) {

## Add button tap callbacks

## 添加按钮点击回调

The last detail that your `DownloadButton` needs is the
button behavior. The button must do things when the user taps it.

`DownloadButton` 的最后一个细节是按钮行为。当用户点击按钮时,该按钮必须做某些事情。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

Add widget properties for callbacks to start a download,
cancel a download, and open a download.

添加回调属性,以开始下载、取消下载和打开下载。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

Finally, wrap `DownloadButton`'s existing widget tree
with a `GestureDetector` widget, and forward the
tap event to the corresponding callback property.

最后,用 `GestureDetector` 包装 `DownloadButton` 的现有widget tree,并将点击事件转发到相应的回调属性。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

<?code-excerpt "lib/button_taps.dart (TapCallbacks)"?>
```dart
@immutable
Expand Down Expand Up @@ -457,10 +560,18 @@ fetching download, downloading, and downloaded.
Now, the user can tap to start a download, tap to cancel an
in-progress download, and tap to open a completed download.

恭喜!您已经有一个按钮,
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved
该按钮根据按钮所处的阶段而改变其显示:未下载、获取下载、下载中和已下载。
现在,用户可以点击以启动下载、点击以取消正在进行的下载,并点击以打开已完成的下载。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

## Interactive example

## 互动示例
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

Run the app:

运行应用程序:

* Click the **GET** button to kick off a
simulated download.
* The button changes to a progress indicator
Expand All @@ -470,6 +581,10 @@ Run the app:
that the app is ready for the user
to open the downloaded asset.

* 点击 **GET** 按钮以启动模拟下载。
* 该按钮更改为进度指示器以模拟正在进行的下载。
* 当模拟下载完成时,按钮将过渡到 **OPEN** ,表示应用已准备好让用户打开下载的资源。
AmosHuKe marked this conversation as resolved.
Show resolved Hide resolved

<!-- start dartpad -->

<?code-excerpt "lib/main.dart"?>
Expand Down