如何让frameBuilder和loadBuilder在Image.network中工作?



如何让frameBuilder和loadBuilder工作?

"loadBuilder"一直工作(有或没有frameBuilder),但"frameBuilder"只有在单独工作时才有效(即注释loadBuilder)。

文档讨论了将框架生成器与 loadBuilder "链接",这并不明显是内部发生了什么,或者这是我们必须考虑的事情。

Image.network(_imageUrl,
height: 400,
width: MediaQuery.of(context).size.width,
fit: BoxFit.fitWidth,
frameBuilder: (BuildContext context, Widget child, int frame, bool wasSynchronouslyLoaded) {
if (wasSynchronouslyLoaded) return child;
return AnimatedOpacity(
opacity: frame == null ? 0 : 1,
duration: Duration(seconds: 5),
curve: Curves.easeOut,
child: child,
);
},
loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null ?
loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes :
null,
),
);
},
errorBuilder: (BuildContext context, Object exception, StackTrace stackTrace) {
return Text('Failed to load image');
},
),

编辑:

究竟发生了什么:

情况 1:当单独加载构建器时,循环进度指示器显示并且加载过程完成,然后指示器消失,加载的图像显示而不褪色。(完美)

情况2:当frameBuilder单独加载时,图像需要时间来加载(当然有指示器),然后加载的图像按预期逐渐显示。(完美)

情况 3:当 frameBuilder 和 loadBuilder 都启用时,所发生的事情是 CASE 1,图像没有任何褪色。(错)

预期行为:

该指示器在加载过程进行时显示,然后消失,图像显示淡入淡出效果。

我需要修复此代码并找出问题所在。

似乎"链接"是自动发生的,但您仍然需要考虑它们链接在一起的结果。

它们不能协同工作的问题是,当frame == null时,会显示循环进度指示器;仅当帧不为空时才显示AnimatedOpacity,因此它的不透明度始终为 1,这意味着我们失去了淡出效果。

解决方案是在加载时将乐观性设置为 0,并在加载时将其设置为 1。

以下代码应该有效:

/// Flutter code sample for Image.frameBuilder
// The following sample demonstrates how to use this builder to implement an
// image that fades in once it's been loaded.
//
// This sample contains a limited subset of the functionality that the
// [FadeInImage] widget provides out of the box.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
bool loading = false;
/// This is the stateless widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
@override
MyStatefulWigdetState createState() => MyStatefulWigdetState();
}
class MyStatefulWigdetState extends State<MyStatefulWidget> {
@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(),
borderRadius: BorderRadius.circular(20),
),
child: Image.network(
'https://flutter.github.io/assets-for-api-docs/assets/widgets/puffin.jpg',
frameBuilder: (BuildContext context, Widget child, int frame,
bool wasSynchronouslyLoaded) {
if (wasSynchronouslyLoaded) {
return child;
}
return AnimatedOpacity(
child: child,
opacity: loading ? 0 : 1,
duration: const Duration(seconds: 3),
curve: Curves.easeOut,
);
},
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent loadingProgress) {
if (loadingProgress == null) {
// The child (AnimatedOpacity) is build with loading == true, and then the setState will change loading to false, which trigger the animation
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() => loading = false);
});
return child;
}
loading = true;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes
: null,
),
);
},
),
);
}
}

关键是将动画不透明度小部件保留在小部件树中。使用if (loadingProgress == null) return child;会导致AnimatedOpacity子项仅在加载完成后放入小部件树中。此时,它作为不透明度为 1 的新小部件插入,这意味着它没有要淡入的先前0值。

这可以通过使用IndexedStack来解决,它会将AnimatedOpacityCircularProgress小部件放入小部件树中,并在加载完成后简单地在两者之间切换。像这样,您将首先获得加载指示器,然后是图像的淡入动画:

Image.network(_imageUrl,
height: 400,
width: MediaQuery.of(context).size.width,
fit: BoxFit.fitWidth,
frameBuilder: (BuildContext context, Widget child, int frame, bool wasSynchronouslyLoaded) {
if (wasSynchronouslyLoaded) return child;
return AnimatedOpacity(
opacity: frame == null ? 0 : 1,
duration: Duration(seconds: 5),
curve: Curves.easeOut,
child: child,
);
},
loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
return IndexedStack(
index: loadingProgress == null ? 0 : 1,
alignment: Alignment.center,
children: <Widget>[
child,
CircularProgressIndicator(
value: loadingProgress?.expectedTotalBytes != null
? loadingProgress!.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes!
: null,
),
],
);
},
errorBuilder: (BuildContext context, Object exception, StackTrace stackTrace) {
return Text('Failed to load image');
},
),

享受!

最新更新