图像重新绘制时出现白色闪屏 [Flutter]

7

我正在尝试构建一个图片网格,它具有给定的宽度和高度,将它们包装在Containers中,并使用fit: BoxFit.fill来设置边框(如果选择了图像,则不保持图像纵横比,我希望每个容器都具有相同的总宽度和高度)。

问题是,当图像在被点击后重新绘制时,我注意到一个白色闪光。当只有少量图像时似乎不会出现这种情况,但是对于15+张图片,会很嘈杂。

我尝试在图像小部件上添加gaplessPlayback: true,因为我在这里找到了该解决方法,但这并没有解决我的问题。

这是一个显示我的问题的gif(我使用了16张图片,大小为1920x1080):

编辑:

我忘了指出这只是一个示例,我在代码中使用了边框,但在我的情况下,我还想向容器添加填充以使图像变小(就像在Android照片库中一样),这意味着每次点击图像都应该重新绘制。

enter image description here

这是我的代码:

import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  // See https://github.com/flutter/flutter/wiki/Desktop-shells#target-platform-override
  debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(body: ImageGridView()),
    );
  }
}

class ImageGridView extends StatelessWidget {
  List<File> _fileList = [];
  ImageGridView(){
    _fileList = [
        File('C:/flutter/img/1.jpg'),
        File('C:/flutter/img/3.jpg'),
        File('C:/flutter/img/4.jpg'),
        File('C:/flutter/img/5.jpg'),
        File('C:/flutter/img/6.jpg'),
        File('C:/flutter/img/7.jpg'),
        File('C:/flutter/img/8.jpg'),
        File('C:/flutter/img/9.jpg'),
        File('C:/flutter/img/10.jpg'),
        File('C:/flutter/img/11.jpg'),
        File('C:/flutter/img/12.jpg'),
        File('C:/flutter/img/13.jpg'),
        File('C:/flutter/img/14.jpg'),
        File('C:/flutter/img/15.jpg'),
        File('C:/flutter/img/16.jpg'),
      ];
    }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Wrap(
        children: _fileList.map((file) {
          return WindowsAsset(file);
        }).toList(),
      ),
    );
  }
}

class WindowsAsset extends StatefulWidget {
  final File _file;

  WindowsAsset(this._file);

  @override
  State<StatefulWidget> createState() => WindowsAssetState();
}

class WindowsAssetState extends State<WindowsAsset> {
  bool selected = false;

  @override
  Widget build(BuildContext context) {
    final width = MediaQuery.of(context).size.width / 2;
    final height = width * 1080 / 1920;
    return Container(
      width: width,
      height: height,
      child: Container(
        child: Container(
          constraints: BoxConstraints.expand(),
          decoration: !selected
              ? null
              : BoxDecoration(
                  border: Border.all(
                    color: Color.fromRGBO(153, 209, 255, 1),
                    width: 4
                  ),
                ),
          child: Container(
            child: GestureDetector(
              child: Image.file(
                widget._file,
                filterQuality: FilterQuality.medium,
                fit: BoxFit.fill,
                gaplessPlayback: true,
              ),
              onTap: () => setState(() {
                selected = !selected;
              }),
            ),
          ),
        ),
      ),
    );
  }
}

我该如何解决这个问题?谢谢!

我认为你应该使用堆栈并在图像上绘制一个容器,而不是更改图像父容器的边框。 - Amir
@veneno 感谢您的评论,我忘了指出我的例子只是一个示例,我在代码中只使用了边框,但在我的情况下,我还想向容器添加填充,这意味着每次点击图像时都应该重新绘制。 - Ansharja
2个回答

30

将您的图像的gaplessPlayback设置为true

Image.memory(
  thumbData,
  gaplessPlayback: true,
)

gaplessPlayback - 当图像提供者更改时,是继续显示旧图像(true),还是短暂地不显示任何内容(false)。默认值为false。 - a.tarasevich
1
感谢您的回复,但它并未解决我的问题。 - martinseal1987

4
这可能是由于两个原因混合在一起导致的:1. 图像缓存的限制条件,以及2. 将所有图像嵌套在 Wrap 中。
ImageCache有一个最大的图片缓存数量和总字节数的限制条件。当达到这个限制时,旧图片将被清除以腾出空间供新图片使用。当试图显示之前已从缓存中删除的图片时,需要时间将其重新加载到内存中,因此会出现白色闪烁。您可以根据 如何在Flutter中更改或替换ImageCache? 更改缓存的限制条件。
默认的图片缓存限制是1000张不同的图片和总共100MiB,通常足够在优化布局的情况下使用,其中离屏窗口部件不会被构建。 Wrap可能是您问题的主要原因-我认为它会在内部构建出所有子窗口小部件以便找出如何包裹它们。在您的情况下,那将是所有图片。可能是因为Wrap,您所有的图片都被认为是在屏幕上,因此不能从缓存中删除。这也解释了为什么GIF中的一些图片会闪烁,即使您没有重建整个窗口小部件树-因为那些图片没有缓存,因为缓存已满,无法删除任何图片。
您可能需要使用GridView.builder替换Wrap,它将仅构建在屏幕上的窗口小部件。
如果显示的图片太大而无法在缓存中容纳,则可能会出现同样的问题。如果在10x10的容器中显示50MB的图像,则仍需要50MB的内存缓存空间。

谢谢您的答案和解释,我会尝试您的解决方案并告诉您! - Ansharja
我尝试使用GridView.builder方法,但结果是一样的。不过,我现在正在尝试使用ImageCache,你认为Image.file默认缓存图像吗?如果我使用你发布的链接中的代码并打印imageCache.maximumSizeBytes,它告诉我它已经是100MB(1000张图片),所以我不应该有任何问题,因为我只使用了16张图片,总大小约为10MB。 - Ansharja
1
是的,所有的 Image 构造函数都会导致图像被缓存,包括 Image.file。尝试在点击图片时检查 ImageCachecurrentSizecurrentSizeBytes 值 - 这将澄清问题是否是 ImageCache 或其他原因引起的。 - Ovidiu
好的,我不知道为什么,但是即使图像的总大小约为10 MB,currentSizeBytes也比100 MB大。如果我设置一个巨大的最大大小,白色闪光就会消失。我将在未来考虑这个问题,谢谢! - Ansharja
将 imageCache.maximumSizeBytes = 9999999999; 这一设置应用于我的代码后,问题得到了解决! - Syed Saad
3
我使用Wrap()来包装我的图片,而在Image()小部件中添加gaplessPlayback: true解决了我的闪烁问题。 - Matthew Rideout

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接