diff --git a/src/content/cookbook/effects/gradient-bubbles.md b/src/content/cookbook/effects/gradient-bubbles.md index d55920a19d..bd4ea898b6 100644 --- a/src/content/cookbook/effects/gradient-bubbles.md +++ b/src/content/cookbook/effects/gradient-bubbles.md @@ -19,12 +19,20 @@ on the bubbles' position on the screen. In this recipe, you'll modernize the chat UI by implementing gradient backgrounds for the chat bubbles. +传统的对话应用程序会以实心颜色背景显示对话气泡, +而现代对话应用程序则根据气泡在屏幕上的位置显示带有渐变色的背景。 +在本教程中,您将通过为聊天气泡实现带有渐变色的背景来使对话界面更加现代化。 + The following animation shows the app's behavior: +下面的动画展示了应用程序的行为: + ![Scrolling the gradient chat bubbles](/assets/images/docs/cookbook/effects/GradientBubbles.gif){:.site-mobile-screenshot} ## Understand the challenge +## 理解挑战 + The traditional chat bubble solution probably uses a `DecoratedBox` or a similar widget to paint a rounded rectangle behind each chat message. That approach is @@ -36,6 +44,12 @@ combined with bubbles scrolling up and down the screen, requires an approach that allows you to make painting decisions based on layout information. +传统的对话气泡解决方案可能使用了 `DecoratedBox` +或类似的 widget 来在每条对话消息后面绘制一个圆角矩形。 +这种方法对于实心颜色或每个对话气泡都重复的带有渐变色的背景非常适用。 +然而,现代全屏的带有渐变色的气泡背景则需要一种不同的方法。 +全屏渐变结合气泡在屏幕上的上下滚动,要求在绘制时基于布局信息做出决策。 + Each bubble's gradient requires knowledge of the bubble's location on the screen. This means that the painting behavior requires access to layout information. @@ -47,24 +61,47 @@ behavior, but you don't require custom layout behavior or custom hit test behavior, a [`CustomPainter`][] is a great choice to get the job done. +每个气泡的渐变需要了解气泡在屏幕上的位置。 +这意味着绘制行为需要访问布局信息。 +典型的 widget 无法实现这样的绘制行为, +因为像 `Container` 和 `DecoratedBox` 这样的 widget +在布局发生之前就已经决定了背景颜色,而不是之后。 +在这种情况下,由于你只需要自定义绘制行为, +而不需要自定义布局或点击测试行为, +使用 [`CustomPainter`][] 是完成该任务的绝佳选择。 + :::note + In cases where you need control over the child layout, but you don't need control over the painting or hit testing, consider using a [`Flow`][] widget. +如果你需要控制子 widget 的布局,但不需要控制绘制或点击测试, +建议使用 [`Flow`][] widget。 + In cases where you need control over the layout, painting, _and_ hit testing, consider defining a custom [`RenderBox`][]. + +如果你需要控制布局、绘制 _以及_ 点击测试, +建议定义一个自定义的 [`RenderBox`][]。 + ::: ## Replace original background widget +## 替换原先的背景 widget + Replace the widget responsible for drawing the background with a new stateless widget called `BubbleBackground`. Include a `colors` property to represent the full-screen gradient that should be applied to the bubble. +将负责绘制背景的 widget 替换为 +一个名为 `BubbleBackground` 的新的 stateless widget。 +添加一个 `colors` 属性,用于表示应该应用于气泡的全屏渐变。 + ```dart BubbleBackground( @@ -89,12 +126,19 @@ BubbleBackground( ## Create a custom painter +## 创建一个自定义画板 + Next, introduce an implementation for `BubbleBackground` as a stateless widget. For now, define the `build()` method to return a `CustomPaint` with a `CustomPainter` called `BubblePainter`. `BubblePainter` is used to paint the bubble gradients. +接下来,为 `BubbleBackground` 引入一个 stateless widget 的实现。 +目前,定义 `build()` 方法以返回一个带有 `CustomPainter` 的 `CustomPaint`, +该 `CustomPainter` 被称为 `BubblePainter`。 +`BubblePainter` 用于绘制气泡的渐变效果。 + ```dart @immutable @@ -141,6 +185,8 @@ class BubblePainter extends CustomPainter { ## Provide access to scrolling information +## 提供对滚动信息的访问 + The `CustomPainter` requires the information necessary to determine where its bubble is within the `ListView`'s bounds, also known as the `Viewport`. Determining the location requires @@ -148,6 +194,12 @@ a reference to the ancestor `ScrollableState` and a reference to the `BubbleBackground`'s `BuildContext`. Provide each of those to the `CustomPainter`. +`CustomPainter` 需要获取信息来确定其气泡在 `ListView` 边界内的位置, +这也被称为 `Viewport`。 +确定位置需要一个对祖先 `ScrollableState` 的引用, +以及 `BubbleBackground` 的 `BuildContext`。 +将这些引用提供给 `CustomPainter`。 + ```dart BubblePainter( @@ -183,6 +235,8 @@ class BubblePainter extends CustomPainter { ## Paint a full-screen bubble gradient +## 绘制一个带有渐变色的全屏气泡 + The `CustomPainter` now has the desired gradient colors, a reference to the containing `ScrollableState`, and a reference to this bubble's `BuildContext`. @@ -193,6 +247,12 @@ of the bubble, configure a shader with the given colors, and then use a matrix translation to offset the shader based on the bubble's position within the `Scrollable`. +现在,`CustomPainter` 拥有所需的渐变颜色、包含它的 `ScrollableState` 的引用 +以及该气泡的 `BuildContext` 引用。 +这些信息足以让 `CustomPainter` 绘制带有渐变色的全屏气泡。 +实现 `paint()` 方法,计算气泡的位置,使用给定的颜色配置一个着色器(shader), +然后使用矩阵平移根据气泡在 `Scrollable` 中的位置来偏移着色器。 + ```dart class BubblePainter extends CustomPainter { @@ -239,7 +299,10 @@ class BubblePainter extends CustomPainter { Congratulations! You now have a modern, chat bubble UI. +恭喜!现在你已经完成了一个现代化的对话气泡 UI。 + :::note + Each bubble's gradient changes as the user scrolls because the `BubbleBackground` widget invokes `Scrollable.of(context)`. This method @@ -248,16 +311,32 @@ sets up an implicit dependency on the ancestor widget to rebuild every time the user scrolls up or down. See the [`InheritedWidget`][] documentation for more information about these types of dependencies. + +每个气泡的渐变色效果会随着用户滚动而变化, +因为 `BubbleBackground` widget 调用了 `Scrollable.of(context)`。 +此方法会在祖先 `ScrollableState` 上建立一个隐式依赖关系, +这会导致每次用户上下滚动时,`BubbleBackground` widget 都会重新构建。 +有关这些依赖关系的更多信息,请参阅 [`InheritedWidget`][] 文档。 + ::: ## Interactive example +## 交互示例 + Run the app: +运行应用程序: + * Scroll up and down to observe the gradient effect. + + 上下滑动来观察渐变色效果。 + * Chat bubbles located at the bottom of the screen have a darker gradient color than the ones at the top. + 位于屏幕底部的带有渐变色效果的对话气泡比顶部更深。 + ```dartpad title="Flutter graident bubbles hands-on example in DartPad" run="true" @@ -511,6 +590,8 @@ class MessageGenerator { ## Recap +## 回顾 + The fundamental challenge when painting based on the scroll position, or the screen position in general, is that the painting behavior must occur after the @@ -522,6 +603,13 @@ then you can base your painting decisions on the layout information, such as the position of the `CustomPaint` widget within a `Scrollable` or within the screen. +根据滚动位置或屏幕位置进行绘制的基本挑战在于, +绘制行为必须在布局阶段完成后进行。 +`CustomPaint` 是一个独特的 widget, +它允许在布局阶段完成后执行自定义绘制行为。 +如果在布局阶段之后执行绘制行为,则可以基于布局信息做出绘制决策, +例如 `CustomPaint` widget 在 `Scrollable` 或屏幕中的位置。 + [cloning the example code]: {{site.github}}/flutter/codelabs [`CustomPainter`]: {{site.api}}/flutter/rendering/CustomPainter-class.html [`Flow`]: {{site.api}}/flutter/widgets/Flow-class.html