重新绘制图像时出现白色闪光 [flutter]
White flash when image is repainted [flutter]
我正在尝试构建具有给定宽度和高度的图像网格,将它们包裹在 Containers
内并使用 fit: BoxFit.fill
以便能够在选择图像时设置边框(我不关心保持图像纵横比,我希望每个容器的总宽度和高度相同。
问题是当图像被点击后重新绘制时,我注意到白色闪光。
当图像很少时,这似乎不会发生,但是当图像超过 15 个时,它就会很嘈杂。
我尝试在图像小部件上添加 gaplessPlayback: true
,正如我发现的那样 here,但这并没有解决我的问题。
这是显示我的问题的 gif(我使用了 16 张图片,尺寸为 1920x1080):
编辑:
我忘了指出这只是一个示例,我在代码中使用了边框,但在我的例子中,我还想向容器添加填充以使图像变小(如 android 照片库),这意味着每次点击的图像都应该重新绘制。
这是我的代码:
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;
}),
),
),
),
),
);
}
}
我该如何解决?谢谢!
这可能是因为 1. ImageCache 限制和 2. 将所有图像嵌套在 Wrap
中
ImageCache 具有可在内存中缓存的最大图像数,以及最大总字节数。当达到该限制时,旧图像将被驱逐,为新图像制作 space。当尝试显示先前从缓存中逐出的图像时,需要一些时间将其再次加载到内存中,因此会出现白色闪烁。您可以根据
更改缓存限制
默认的图像缓存限制是 1000 张不同的图像和总计 100MiB,如果没有构建离屏小部件的最佳布局,这通常就足够了。
Wrap
可能是您遇到问题的主要原因 - 我相信它会在内部构建其所有子项以便弄清楚如何包装它们。在您的情况下,这将是所有图像。可能是因为 Wrap
,您的所有图像都被认为在屏幕上,因此不会从缓存中逐出 - 这可以解释为什么即使您没有重建整个小部件,GIF 中的某些图像也会闪烁树 - 因为这些图像未被缓存,因为缓存已满并且 none 的图像可以被逐出。
您可能希望将 Wrap
替换为 GridView.builder
,这只会构建屏幕上的小部件。
如果图像太大而无法在缓存中同时显示尽可能多的图像,您可能会遇到同样的问题。如果您在 10x10 的容器中显示 50MB 的图像,仍然需要 50MB 的内存缓存。
将图片的 gaplessPlayback
设置为 true
。
Image.memory(
thumbData,
gaplessPlayback: true,
)
我正在尝试构建具有给定宽度和高度的图像网格,将它们包裹在 Containers
内并使用 fit: BoxFit.fill
以便能够在选择图像时设置边框(我不关心保持图像纵横比,我希望每个容器的总宽度和高度相同。
问题是当图像被点击后重新绘制时,我注意到白色闪光。 当图像很少时,这似乎不会发生,但是当图像超过 15 个时,它就会很嘈杂。
我尝试在图像小部件上添加 gaplessPlayback: true
,正如我发现的那样 here,但这并没有解决我的问题。
这是显示我的问题的 gif(我使用了 16 张图片,尺寸为 1920x1080):
编辑:
我忘了指出这只是一个示例,我在代码中使用了边框,但在我的例子中,我还想向容器添加填充以使图像变小(如 android 照片库),这意味着每次点击的图像都应该重新绘制。
这是我的代码:
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;
}),
),
),
),
),
);
}
}
我该如何解决?谢谢!
这可能是因为 1. ImageCache 限制和 2. 将所有图像嵌套在 Wrap
ImageCache 具有可在内存中缓存的最大图像数,以及最大总字节数。当达到该限制时,旧图像将被驱逐,为新图像制作 space。当尝试显示先前从缓存中逐出的图像时,需要一些时间将其再次加载到内存中,因此会出现白色闪烁。您可以根据
默认的图像缓存限制是 1000 张不同的图像和总计 100MiB,如果没有构建离屏小部件的最佳布局,这通常就足够了。
Wrap
可能是您遇到问题的主要原因 - 我相信它会在内部构建其所有子项以便弄清楚如何包装它们。在您的情况下,这将是所有图像。可能是因为 Wrap
,您的所有图像都被认为在屏幕上,因此不会从缓存中逐出 - 这可以解释为什么即使您没有重建整个小部件,GIF 中的某些图像也会闪烁树 - 因为这些图像未被缓存,因为缓存已满并且 none 的图像可以被逐出。
您可能希望将 Wrap
替换为 GridView.builder
,这只会构建屏幕上的小部件。
如果图像太大而无法在缓存中同时显示尽可能多的图像,您可能会遇到同样的问题。如果您在 10x10 的容器中显示 50MB 的图像,仍然需要 50MB 的内存缓存。
将图片的 gaplessPlayback
设置为 true
。
Image.memory(
thumbData,
gaplessPlayback: true,
)