【问题标题】:Flutter image preloadFlutter 图像预加载
【发布时间】:2018-12-22 22:24:16
【问题描述】:

是否可以在应用启动时以某种方式预加载图像?就像我的抽屉里有一张背景图片,但是当我第一次打开抽屉时,我可以看到图片闪烁,就像它是从资产中获取然后显示的一样,一旦我第一次看到它,它就会给我带来不好的体验抽屉的开口表现如预期,因为它已被缓存。我想在应用加载时预取它,所以没有这样的效果。

【问题讨论】:

标签: dart flutter


【解决方案1】:

使用precacheImage 函数在构建抽屉之前开始加载图像。例如,在包含您的抽屉的小部件中:

class MyWidgetState extends State<MyWidget> {

  @override
  void initState() {
    // adjust the provider based on the image type
    precacheImage(new AssetImage('...'));
    super.initState();
  }

}

【讨论】:

  • 在我的例子中 precacheImage 方法需要上下文:precacheImage(new AssetImage('...'), context);
  • 这很有帮助,但似乎在加载了许多其他图像后,图像会被踢出缓存。有没有办法永久保存在缓存中?
  • @Aurast 你可以设置imageCache.maximumSize,但是要小心内存不足,尤其是在低 RAM 的设备上。
  • 感谢@mertcanb 的帮助,希望有一种方法可以缓存特定的图像,因为我真的只有一个我想保留在内存中(抽屉菜单横幅)。但我想这可能是不可能的。
  • precacheImage 应该在didChangeDependencies() 中调用,因为它需要一个允许媒体查询的BuildContext。在initState() 中调用它可能会导致未处理的异常等。请参阅@Ethirallan 的回答
【解决方案2】:

我遇到了在 initState 中使用 precacheImage() 的上层解决方案的问题。下面的代码解决了它们。另请注意,您可能在调试模式下看不到预期结果,而只能在发布模式下看到。

Image myImage;

  @override
  void initState() {
    super.initState();
    myImage= Image.asset(path);
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    precacheImage(myImage.image, context);
  }

【讨论】:

  • 如果小部件是有状态的,这真的很有帮助。如果是无状态的,则可以在创建小部件之前进行预缓存
【解决方案3】:

关于这个主题有一篇很好的文章: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,
            ],
          ),
        );
  }
}

【讨论】:

  • 这个答案已经提供here。让我知道您添加了哪些新的相关信息。
  • @iDecode 我认为我的答案更具可读性
  • 我还可以修改您的答案以使其更具可读性,但这实际上是 Stack Overflow 上的好习惯,当然不是。
【解决方案4】:

要摆脱“闪烁”,您可以简单地将FadeInImage classtransparent_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'),
  )

【讨论】:

  • 这实际上并没有回答所提出的问题
  • @JonahWilliams 我认为是这样,因为大多数问题都包含整个“眨眼”效果,即使标题并未暗示:“所以没有这样的效果”是我试过的回答。
  • 是的,这不能回答问题。我需要立即加载图像,不褪色不闪烁。只需打开已缓存图像的抽屉,就像这样。
  • 正是我需要的。
  • @Bawantha 它带有package:transparent_image/transparent_image.dart
【解决方案5】:

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 的方式完成。

【讨论】:

    【解决方案6】:

    我遇到了以下问题:图像需要一些空间。所以加载后,它把 UI 往下推,这对 UX 不利。我决定创建一个构建器来显示一个空(或正在加载)的容器,直到图像被加载,然后显示我的 UI。

    看起来precacheImage 返回已解决的未来。对我来说棘手的部分是FutureBuildersnapshot.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();
            }
          },
        );
    

    【讨论】:

    • 嗨@fedor.belov,这真的会在调用build() 方法之前预加载图像吗?通常,为了获得 precacheImage 的优势(在渲染之前加载),例如在 didChangeDependencies 中声明了 precacheImage。但是,在这里您已经在构建中调用它...
    • @Jorge 我认为你是对的。由于这将是build() 方法的一部分,因此不会发生实际的预加载。它只会延迟构建 Scaffold,直到加载图像并且 AssetImage 返回的 Future 完成。然而,这可能足以避免由于图像迟到而导致的闪烁
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-04
    • 2010-11-25
    • 2015-08-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多