@@ -18,29 +18,56 @@ to communicate to users that data is loading is to
18
18
display a chrome color with a shimmer animation over
19
19
the shapes that approximate the type of content that is loading.
20
20
21
+ 在应用开发中,加载时间是不可避免的。
22
+ 从用户体验 (UX) 的角度来看,
23
+ 最重要的是向用户展示正在进行加载操作。
24
+ 一种常见的方式是,
25
+ 在近似加载内容形状的区域上,
26
+ 显示带有微光动画的镀铬颜色覆盖层,
27
+ 以此来告知用户数据正在加载。
28
+
21
29
The following animation shows the app's behavior:
22
30
31
+ 下面的动画展示了应用程序的行为:
32
+
23
33
![ Gif showing the UI loading] ( /assets/images/docs/cookbook/effects/UILoadingAnimation.gif ) {:.site-mobile-screenshot}
24
34
25
35
This recipe begins with the content widgets defined and positioned.
26
36
There is also a Floating Action Button (FAB) in the bottom-right
27
37
corner that toggles between a loading mode and a loaded mode
28
38
so that you can easily validate your implementation.
29
39
40
+ 该示例从准备好的内容 widget 开始。
41
+ 界面右下角还放置了一个浮动操作按钮 (FAB),
42
+ 用于切换正在加载和已加载的模式,
43
+ 以便你可以轻松验证实现的效果。
44
+
30
45
## Draw the shimmer shapes
31
46
47
+ ## 绘制微光效果的形状
48
+
32
49
The shapes that shimmer in this effect are independent
33
50
from the actual content that eventually loads.
34
51
52
+ 在这个效果中,微光的形状是独立于最终加载的实际内容的。
53
+
35
54
Therefore, the goal is to display shapes that represent
36
55
the eventual content as accurately as possible.
37
56
57
+ 因此,目标是尽可能准确地显示代表最终内容的形状。
58
+
38
59
Displaying accurate shapes is easy in situations where the
39
60
content has a clear boundary. For example, in this recipe,
40
61
there are some circular images and some rounded rectangle images.
41
62
You can draw shapes that precisely match the outlines
42
63
of those images.
43
64
65
+ 在内容有明确边界的情况下,
66
+ 显示准确的形状很容易。
67
+ 例如,在这个示例中,
68
+ 有一些圆形图片和一些圆角矩形图片。
69
+ 你可以绘制与这些图片轮廓完全匹配的形状。
70
+
44
71
On the other hand, consider the text that appears beneath the
45
72
rounded rectangle images. You won't know how many lines of
46
73
text exist until the text loads.
@@ -50,10 +77,21 @@ you draw a couple of very thin rounded rectangles that
50
77
represent the text that will appear. The shape and size
51
78
doesn't quite match, but that is OK.
52
79
80
+ 另一方面,考虑显示在圆角矩形图片下方的文本。
81
+ 在文本加载之前,你不会知道有多少行文本。
82
+ 因此,尝试为每行文本绘制一个矩形是没有意义的。
83
+ 相反,当数据加载时,
84
+ 你可以绘制几个非常细的圆角矩形来代表即将出现的文本。
85
+ 形状和大小可能不完全匹配,
86
+ 但这样做是可以的。
87
+
53
88
Start with the circular list items at the top of the screen.
54
89
Ensure that each ` CircleListItem ` widget displays a circle
55
90
with a color while the image is loading.
56
91
92
+ 从屏幕顶部的圆形列表项开始。
93
+ 确保每个 ` CircleListItem ` widget 在图片加载时显示一个有颜色的圆形。
94
+
57
95
<? code-excerpt "lib/main.dart (CircleListItem)"?>
58
96
``` dart
59
97
class CircleListItem extends StatelessWidget {
@@ -86,13 +124,21 @@ class CircleListItem extends StatelessWidget {
86
124
As long as your widgets display some kind of shape,
87
125
you can apply the shimmer effect in this recipe.
88
126
127
+ 只要你的 widget 显示某种形状,
128
+ 你就可以在这个示例中应用微光效果。
129
+
89
130
Similar to the ` CircleListItem ` widgets,
90
131
ensure that the ` CardListItem ` widgets
91
132
display a color where the image will appear.
92
133
Also, in the ` CardListItem ` widget,
93
134
switch between the display of the text and
94
135
the rectangles based on the current loading status.
95
136
137
+ 类似于 ` CircleListItem ` widget,
138
+ 确保 ` CardListItem ` widget 在图片将出现的地方显示颜色。
139
+ 同时,在 ` CardListItem ` widget 中,
140
+ 根据当前的加载状态在文本和矩形的显示之间切换。
141
+
96
142
<? code-excerpt "lib/main.dart (CardListItem)"?>
97
143
``` dart
98
144
class CardListItem extends StatelessWidget {
@@ -181,24 +227,40 @@ whether it's loading or loaded.
181
227
By temporarily commenting out the image URLs,
182
228
you can see the two ways your UI renders.
183
229
230
+ 现在,你的 UI 会根据是否正在加载或已加载来呈现不同的效果。
231
+ 通过暂时注释掉图像 URL,
232
+ 你可以看到 UI 呈现的两种方式。
233
+
184
234
185
235
![ Gif showing the shimmer animation] ( /assets/images/docs/cookbook/effects/LoadingShimmer.gif ) {:.site-mobile-screenshot}
186
236
187
237
The next goal is to paint all of the colored areas
188
238
with a single gradient that looks like a shimmer.
189
239
240
+ 接下来的目标是将所有着色区域涂上一层看起来像微光效果的渐变。
241
+
190
242
## Paint the shimmer gradient
191
243
244
+ ## 绘制微光渐变效果
245
+
192
246
The key to the effect achieved in this recipe is to use a widget
193
247
called [ ` ShaderMask ` ] [ ] . The ` ShaderMask ` widget, as the name suggests,
194
248
applies a shader to its child, but only in the areas where
195
249
the child already painted something. For example,
196
250
you'll apply a shader to only the black shapes that you
197
251
configured earlier.
198
252
253
+ 实现这一效果的关键是使用一个名为 [ ` ShaderMask ` ] [ ] 的 widget。
254
+ 顾名思义, ` ShaderMask ` widget 将着色器应用于其子 widget,
255
+ 但仅限于子 widget 已经绘制了内容的区域。
256
+ 例如,你将只对之前配置的黑色形状应用着色器。
257
+
199
258
Define a chrome-colored, linear gradient that gets applied to the
200
259
shimmer shapes.
201
260
261
+ 定义一个铬色的线性渐变,
262
+ 将其应用于微光效果的形状。
263
+
202
264
<? code-excerpt "lib/main.dart (shimmerGradient)"?>
203
265
``` dart
204
266
const _shimmerGradient = LinearGradient(
@@ -225,6 +287,13 @@ gradient as a shader with a `blendMode` of `srcATop`.
225
287
The ` srcATop ` blend mode replaces any color that your
226
288
` child ` widget painted with the shader color.
227
289
290
+ 定义一个新的 stateful widget,名为 ` ShimmerLoading ` ,
291
+ 它用 ` ShaderMask ` 包裹一个传入的 ` child ` widget。
292
+ 配置 ` ShaderMask ` widget,
293
+ 使微光渐变效果应用为着色器,
294
+ 并将 ` blendMode ` 设置为 ` srcATop ` 。
295
+ ` srcATop ` 混合模式会将 ` child ` widget 绘制的任何颜色替换为着色器颜色。
296
+
228
297
<? code-excerpt "lib/main.dart (ShimmerLoading)"?>
229
298
``` dart
230
299
class ShimmerLoading extends StatefulWidget {
@@ -261,6 +330,8 @@ class _ShimmerLoadingState extends State<ShimmerLoading> {
261
330
262
331
Wrap your ` CircleListItem ` widgets with a ` ShimmerLoading ` widget.
263
332
333
+ 用 ` ShimmerLoading ` widget 包裹你的 ` CircleListItem ` widget。
334
+
264
335
<? code-excerpt "lib/shimmer_loading_items.dart (buildTopRowItem)"?>
265
336
``` dart
266
337
Widget _buildTopRowItem() {
@@ -273,6 +344,8 @@ Widget _buildTopRowItem() {
273
344
274
345
Wrap your ` CardListItem ` widgets with a ` ShimmerLoading ` widget.
275
346
347
+ 用 ` ShimmerLoading ` widget 包裹你的 ` CardListItem ` widget。
348
+
276
349
<? code-excerpt "lib/shimmer_loading_items.dart (buildListItem)"?>
277
350
``` dart
278
351
Widget _buildListItem() {
@@ -289,6 +362,9 @@ When your shapes are loading, they now display
289
362
the shimmer gradient that is
290
363
returned from the ` shaderCallback ` .
291
364
365
+ 当你的形状正在加载时,
366
+ 它们会显示从 ` shaderCallback ` 返回的微光渐变效果。
367
+
292
368
This is a big step in the right direction,
293
369
but there's a problem with this gradient display.
294
370
Each ` CircleListItem ` widget and each ` CardListItem ` widget
@@ -297,14 +373,27 @@ For this recipe, the entire screen should
297
373
look like one, big shimmering surface.
298
374
You solve this problem in the next step.
299
375
376
+ 这是朝着正确方向迈出的重要一步,
377
+ 但这个渐变显示存在一个问题。
378
+ 每个 ` CircleListItem ` widget 和每个 ` CardListItem ` widget
379
+ 都会显示各自独立的渐变效果。
380
+ 对于这个示例,整个屏幕应该呈现为一个大而统一的微光表面。
381
+ 你将在下一步解决这个问题。
382
+
300
383
## Paint one big shimmer
301
384
385
+ ## 绘制大而统一的微光效果
386
+
302
387
To paint one big shimmer across the screen,
303
388
each ` ShimmerLoading ` widget needs
304
389
to paint the same full-screen gradient based
305
390
on the position of that ` ShimmerLoading `
306
391
widget on the screen.
307
392
393
+ 为了在整个屏幕上绘制大而统一的微光效果,
394
+ 每个 ` ShimmerLoading ` widget 需要根据
395
+ 该 ` ShimmerLoading ` widget 在屏幕上的位置绘制相同的全屏渐变。
396
+
308
397
To be more precise, rather than assume that the shimmer
309
398
should take up the entire screen,
310
399
there should be some area that shares the shimmer.
@@ -317,10 +406,22 @@ Then, each `ShimmerLoading` widget gets a reference
317
406
to the ` Shimmer ` ancestor
318
407
and requests the desired size and gradient to display.
319
408
409
+ 更准确地说,不应该假设微光效果会占据整个屏幕,
410
+ 而是应该有一个区域共享微光效果。
411
+ 这个区域可能会占据整个屏幕,也可能不会。
412
+ 解决这个问题的方式是在所有的 ` ShimmerLoading ` widget 上方
413
+ 定义另一个 widget,称为 ` Shimmer ` 。
414
+ 然后,每个 ` ShimmerLoading ` widget 可以引用 ` Shimmer ` 祖先,
415
+ 并请求显示所需的大小和渐变。
416
+
320
417
Define a new stateful widget called ` Shimmer ` that
321
418
takes in a [ ` LinearGradient ` ] [ ] and provides descendants
322
419
with access to its ` State ` object.
323
420
421
+ 定义一个新的 stateful widget,命名为 ` Shimmer ` ,
422
+ 它接收一个 [ ` LinearGradient ` ] [ ]
423
+ 并允许后代 widget 访问其 ` State ` 对象。
424
+
324
425
<? code-excerpt "lib/main.dart (Shimmer)"?>
325
426
``` dart
326
427
class Shimmer extends StatefulWidget {
@@ -355,6 +456,10 @@ the size of the `ShimmerState`'s `RenderBox`,
355
456
and look up the position of a descendant within the
356
457
` ShimmerState ` 's ` RenderBox ` .
357
458
459
+ 在 ` ShimmerState ` 类中添加方法,
460
+ 以提供对 ` linearGradient ` 、` ShimmerState ` 的 ` RenderBox ` 的大小
461
+ 以及查找 ` ShimmerState ` 的 ` RenderBox ` 中某个后代位置的访问权限。
462
+
358
463
<? code-excerpt "lib/shimmer_state.dart (ShimmerState)"?>
359
464
``` dart
360
465
class ShimmerState extends State<Shimmer> {
@@ -387,6 +492,8 @@ class ShimmerState extends State<Shimmer> {
387
492
388
493
Wrap all of your screen's content with the ` Shimmer ` widget.
389
494
495
+ 将屏幕上的所有内容都用 ` Shimmer ` widget 包裹起来。
496
+
390
497
<? code-excerpt "lib/main.dart (ExampleUiAnimationState)"?>
391
498
``` dart
392
499
class _ExampleUiLoadingAnimationState extends State<ExampleUiLoadingAnimation> {
@@ -407,6 +514,8 @@ class _ExampleUiLoadingAnimationState extends State<ExampleUiLoadingAnimation> {
407
514
Use the ` Shimmer ` widget within your
408
515
` ShimmerLoading ` widget to paint the shared gradient.
409
516
517
+ 在你的 ` ShimmerLoading ` widget 中使用 ` Shimmer ` widget 来绘制共享的渐变效果。
518
+
410
519
<? code-excerpt "lib/shimmer_loading_state_pt2.dart (ShimmerLoadingStatePt2)"?>
411
520
``` dart
412
521
class _ShimmerLoadingState extends State<ShimmerLoading> {
@@ -451,19 +560,33 @@ Your `ShimmerLoading` widgets now display a shared
451
560
gradient that takes up all of the space within the
452
561
` Shimmer ` widget.
453
562
563
+ 你的 ` ShimmerLoading ` widget 现在显示了一个共享的渐变效果,
564
+ 这个效果覆盖了 ` Shimmer ` widget 内部的所有空间。
565
+
454
566
## Animate the shimmer
455
567
568
+ ## 给微光效果添加动画
569
+
456
570
The shimmer gradient needs to move in order to
457
571
give the appearance of a shimmering shine.
458
572
573
+ 微光渐变效果需要通过移动来产生光泽感。
574
+
459
575
The ` LinearGradient ` has a property called ` transform `
460
576
that can be used to transform the appearance of the gradient,
461
577
for example, to move it horizontally.
462
578
The ` transform ` property accepts a ` GradientTransform ` instance.
463
579
580
+ ` LinearGradient ` 有一个名为 ` transform ` 的属性,
581
+ 可以用来改变渐变的外观,例如,使其水平移动。
582
+ ` transform ` 属性接受一个 ` GradientTransform ` 实例。
583
+
464
584
Define a class called ` _SlidingGradientTransform ` that implements
465
585
` GradientTransform ` to achieve the appearance of horizontal sliding.
466
586
587
+ 定义一个名为 ` _SlidingGradientTransform ` 的类,
588
+ 来实现 ` GradientTransform ` 接口,以实现水平滑动的效果。
589
+
467
590
<? code-excerpt "lib/original_example.dart (sliding-gradient-transform)"?>
468
591
``` dart
469
592
class _SlidingGradientTransform extends GradientTransform {
@@ -485,6 +608,9 @@ in order to create the appearance of motion.
485
608
To change the percentage, configure an
486
609
[ ` AnimationController ` ] [ ] in the ` ShimmerState ` class.
487
610
611
+ 渐变滑动百分比随时间变化,以创造运动的效果。
612
+ 要改变百分比,请在 ` ShimmerState ` 类中配置一个 [ ` AnimationController ` ] [ ] 。
613
+
488
614
<? code-excerpt "lib/original_example.dart (shimmer-state-animation)" replace="/\/\/ code-excerpt-closing-bracket/}/g"?>
489
615
``` dart
490
616
class ShimmerState extends State<Shimmer> with SingleTickerProviderStateMixin {
@@ -509,6 +635,10 @@ class ShimmerState extends State<Shimmer> with SingleTickerProviderStateMixin {
509
635
Apply the ` _SlidingGradientTransform ` to the ` gradient `
510
636
by using the ` _shimmerController ` 's ` value ` as the ` slidePercent ` .
511
637
638
+ 将 ` _shimmerController ` 的 ` value `
639
+ 赋值给 ` _SlidingGradientTransform ` 的 ` slidePercent ` ,
640
+ 然后将 ` _SlidingGradientTransform ` 应用于 ` gradient ` 。
641
+
512
642
<? code-excerpt "lib/original_example.dart (linear-gradient)"?>
513
643
``` dart
514
644
LinearGradient get gradient => LinearGradient(
@@ -526,9 +656,15 @@ The gradient now animates, but your individual
526
656
as the gradient changes. Therefore, it looks like nothing
527
657
is happening.
528
658
659
+ 渐变现在已经动画化了,
660
+ 但你的单个 ` ShimmerLoading ` widget 在渐变变化时没有重新绘制自己。
661
+ 因此,看起来什么也没有发生。
662
+
529
663
Expose the ` _shimmerController ` from ` ShimmerState `
530
664
as a [ ` Listenable ` ] [ ] .
531
665
666
+ 在 ` ShimmerState ` 中将 ` _shimmerController ` 暴露为一个 [ ` Listenable ` ] [ ] 。
667
+
532
668
<? code-excerpt "lib/original_example.dart (shimmer-changes)"?>
533
669
``` dart
534
670
Listenable get shimmerChanges => _shimmerController;
@@ -538,6 +674,10 @@ In `ShimmerLoading`, listen for changes to the ancestor
538
674
` ShimmerState ` 's ` shimmerChanges ` property,
539
675
and repaint the shimmer gradient.
540
676
677
+ 在 ` ShimmerLoading ` 中,
678
+ 监听祖先 ` ShimmerState ` 的 ` shimmerChanges ` 属性的变化,
679
+ 并重新绘制微光渐变效果。
680
+
541
681
<? code-excerpt "lib/original_example.dart (shimmer-loading-state)" replace="/\/\/ code-excerpt-closing-bracket/}/g"?>
542
682
``` dart
543
683
class _ShimmerLoadingState extends State<ShimmerLoading> {
@@ -576,8 +716,14 @@ You now have a full-screen,
576
716
animated shimmer effect that turns
577
717
on and off as the content loads.
578
718
719
+ 恭喜!
720
+ 你现在拥有了一个全屏的动画微光效果,
721
+ 随着内容加载,它会打开或关闭。
722
+
579
723
## Interactive example
580
724
725
+ ## 交互示例
726
+
581
727
<? code-excerpt "lib/original_example.dart" remove="code-excerpt-closing-bracket"?>
582
728
``` dartpad title="Flutter shimmer loading hands-on example in DartPad" run="true"
583
729
import 'package:flutter/material.dart';
0 commit comments