【发布时间】:2018-12-22 22:24:16
【问题描述】:
是否可以在应用启动时以某种方式预加载图像?就像我的抽屉里有一张背景图片,但是当我第一次打开抽屉时,我可以看到图片闪烁,就像它是从资产中获取然后显示的一样,一旦我第一次看到它,它就会给我带来不好的体验抽屉的开口表现如预期,因为它已被缓存。我想在应用加载时预取它,所以没有这样的效果。
【问题讨论】:
是否可以在应用启动时以某种方式预加载图像?就像我的抽屉里有一张背景图片,但是当我第一次打开抽屉时,我可以看到图片闪烁,就像它是从资产中获取然后显示的一样,一旦我第一次看到它,它就会给我带来不好的体验抽屉的开口表现如预期,因为它已被缓存。我想在应用加载时预取它,所以没有这样的效果。
【问题讨论】:
使用precacheImage 函数在构建抽屉之前开始加载图像。例如,在包含您的抽屉的小部件中:
class MyWidgetState extends State<MyWidget> {
@override
void initState() {
// adjust the provider based on the image type
precacheImage(new AssetImage('...'));
super.initState();
}
}
【讨论】:
imageCache.maximumSize,但是要小心内存不足,尤其是在低 RAM 的设备上。
precacheImage 应该在didChangeDependencies() 中调用,因为它需要一个允许媒体查询的BuildContext。在initState() 中调用它可能会导致未处理的异常等。请参阅@Ethirallan 的回答
我遇到了在 initState 中使用 precacheImage() 的上层解决方案的问题。下面的代码解决了它们。另请注意,您可能在调试模式下看不到预期结果,而只能在发布模式下看到。
Image myImage;
@override
void initState() {
super.initState();
myImage= Image.asset(path);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
precacheImage(myImage.image, context);
}
【讨论】:
关于这个主题有一篇很好的文章:https://alex.domenici.net/archive/preload-images-in-a-stateful-widget-on-flutter
它应该看起来像这样
class _SampleWidgetState extends State<SampleWidget> {
Image image1;
Image image2;
Image image3;
Image image4;
@override
void initState() {
super.initState();
image1 = Image.asset("assets/image1.png");
image2 = Image.asset("assets/image2.png");
image3 = Image.asset("assets/image3.png");
image4 = Image.asset("assets/image4.png");
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
precacheImage(image1.image, context);
precacheImage(image2.image, context);
precacheImage(image3.image, context);
precacheImage(image4.image, context);
}
@override
Widget build(BuildContext context) {
return Container(
child: Stack(
children: <Widget>[
image1,
image2,
image3,
image4,
],
),
);
}
}
【讨论】:
要摆脱“闪烁”,您可以简单地将FadeInImage class 与transparent_image 结合使用,它会消失而不是立即出现。就您而言,用法如下所示:
// you need to add transparent_image to your pubspec and import it
// as it is required to have the actual image fade in from nothing
import 'package:transparent_image/transparent_image.dart';
import 'package:flutter/material.dart';
...
FadeInImage(
placeholder: MemoryImage(kTransparentImage),
image: AssetImage('image.png'),
)
【讨论】:
package:transparent_image/transparent_image.dart
precacheImage() 函数(在大多数答案中使用) 返回一个 Future,对于某些用例场景,它可能非常有用。例如,在我的应用程序中,我需要加载外部图像,并且像其他人一样不想体验闪烁。所以我最终得到了这样的结果:
// show some sort of loading indicator
...
precacheImage(
NetworkImage(),
context,
).then((_) {
// replace the loading indicator and show the image
// (may be with some soothing fade in effect etc.)
...
});
请注意,在上面的示例中,我想说明Future 的潜在用途。 cmets 只是为了帮助表达这个想法。实际实现必须以Fluttery 的方式完成。
【讨论】:
我遇到了以下问题:图像需要一些空间。所以加载后,它把 UI 往下推,这对 UX 不利。我决定创建一个构建器来显示一个空(或正在加载)的容器,直到图像被加载,然后显示我的 UI。
看起来precacheImage 返回已解决的未来。对我来说棘手的部分是FutureBuilder 和snapshot.hasData,它们总是false,因为未来是用null 解决的。所以我添加了未来的转换来修复snapshot.hasData:
precacheImage(this.widget.imageProvider, context).then((value) => true)
我不确定是否可以多次调用precacheImage,所以我决定将其封装到StatefulWidget。
您可以查看最终构建器here
使用builder的方式如下:
return PreloadingImageBuilder(
imageProvider: AssetImage("assets/images/logo-main.png"),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Scaffold(
...
);
} else {
return Container();
}
},
);
【讨论】:
build() 方法的一部分,因此不会发生实际的预加载。它只会延迟构建 Scaffold,直到加载图像并且 AssetImage 返回的 Future 完成。然而,这可能足以避免由于图像迟到而导致的闪烁