Flutter中返回上一页时FadeInImage组件重复执行淡入动画的问题排查与解决
嘿,我来帮你搞定这个FadeInImage返回页面重复触发动画的问题!
问题出在哪?
你现在的ScreenOne是个StatelessWidget,每次从ScreenTwo返回时,Flutter都会重新构建整个ScreenOne组件——这就意味着里面的FadeInImage会被重新创建一遍,淡入动画自然就会重复执行。另外你尝试用AutomaticKeepAliveClientMixin没生效,是因为这个混入只能用在StatefulWidget的State类里,你的原组件是无状态的,所以完全起不了作用。
怎么解决?
我们把ScreenOne改成StatefulWidget,然后正确配置AutomaticKeepAliveClientMixin来维持组件状态,这样返回页面时就不会重新构建组件,动画也就不会重复触发了。
修改后的完整代码
class ScreenOne extends StatefulWidget { const ScreenOne({super.key}); @override State<ScreenOne> createState() => _ScreenOneState(); } class _ScreenOneState extends State<ScreenOne> with AutomaticKeepAliveClientMixin { // 告诉Flutter要保留这个组件的状态 @override bool get wantKeepAlive => true; @override Widget build(BuildContext context) { // 这里必须调用super.build,不然AutomaticKeepAliveClientMixin不会生效,很多人容易漏这个! super.build(context); return GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), primary: false, padding: const EdgeInsets.only(left: 6, right: 6), itemCount: 4, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, childAspectRatio: 180 / 304, crossAxisSpacing: 0, mainAxisSpacing: 0, ), itemBuilder: (BuildContext context, int index) { return InkWell( onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => const ScreenTwo()), ); }, child: Container( height: 191, width: 180, decoration: BoxDecoration( borderRadius: BorderRadius.circular(9), ), child: Center( child: ClipRRect( borderRadius: BorderRadius.circular(9), child: FadeInImage( placeholder: const MemoryImage(kTransparentImage), image: const NetworkImage('https://i.pravatar.cc/300'), imageErrorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) { return const Icon(Icons.error); }, fit: BoxFit.contain, height: 300, width: 270, // 可选优化:如果想让动画只在首次加载时触发,后续缓存加载直接显示,就加这行 // fadeInDuration: Duration.zero, ), ), ), ), ); }); } } class ScreenTwo extends StatelessWidget { const ScreenTwo({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Second Route"), ), body: Center( child: ElevatedButton( onPressed: () { Navigator.pop(context); }, child: const Text('Go back!'), ), ), ); } }
额外小技巧
如果你希望图片被缓存后,下次加载完全没有动画,可以给FadeInImage加上fadeInDuration: Duration.zero,这样只有第一次从网络加载时会有淡入效果,后续从缓存取图时直接显示。
再啰嗦两句为啥原来的方法没用
AutomaticKeepAliveClientMixin的作用是告诉Flutter“这个组件的状态要保留,别随便销毁”,但它只能和StatefulWidget搭配使用——你原来的ScreenOne是无状态组件,Flutter根本不会保存它的状态,所以不管怎么加mixin都白搭。改成有状态组件并正确实现mixin的两个要求(返回wantKeepAlive为true,调用super.build),问题就解决啦!
内容的提问来源于stack exchange,提问作者mtkguy
相关产品推荐
相关产品推荐

