更新Image.memory()时屏幕闪烁问题求助
解决二维码更新时的屏幕闪烁问题
问题核心是更新二维码时,旧内容被提前移除,新内容加载过程中出现空白。可以通过保留旧内容直到新内容完全准备好、优化状态更新逻辑来解决,具体方案如下:
1. 优化状态管理,缓存当前有效二维码
在页面State中保存当前显示的二维码数据,只有当新二维码成功获取并加载完成后,才替换旧数据,避免中间空白状态。
2. 用Timer定时更新,替代StreamBuilder
StreamBuilder的周期触发会导致快照状态频繁切换,容易出现中间状态。改用Timer.periodic可以更精准控制更新时机,减少不必要的重绘。
3. 预加载新二维码图片
提前解码并预加载新图片,确保替换时图片已经加载完成,消除加载延迟导致的空白。
修改后的代码示例
第一步:修复AppManager的通知逻辑
确保数据更新时通知UI(之前未调用notifyListeners,导致Consumer无法感知变化):
Future<ApiResponse<SigninModel>> getQrData() async { signinData = await signinRepo.fetchSigninData(); if (signinData.status == Status.COMPLETED) { appCache.setSigninQR(signinData.data!.qrcode); appCache.setLnurl(signinData.data!.lnurl); appCache.setSigninK1(signinData.data!.k1); checkAuthorization(signinData.data!.k1); notifyListeners(); // 新增:通知UI数据更新 } return signinData; }
第二步:重写QR页面的State逻辑
class QR extends StatefulWidget { const QR({Key? key}) : super(key: key); @override State<QR> createState() => _QRState(); } class _QRState extends State<QR> { SigninModel? _currentQrData; Timer? _qrUpdateTimer; @override void initState() { super.initState(); // 从缓存初始化当前二维码(避免首次加载空白) final qrCode = appCache.getSigninQR(); final lnurl = appCache.getLnurl(); final k1 = appCache.getSigninK1(); if (qrCode != null && lnurl != null && k1 != null) { _currentQrData = SigninModel(qrcode: qrCode, lnurl: lnurl, k1: k1); } // 立即请求最新二维码 _fetchQrData(); // 设置10分钟更新定时器(替换原代码的10秒,符合需求描述) _qrUpdateTimer = Timer.periodic(const Duration(minutes: 10), (_) => _fetchQrData()); } Future<void> _fetchQrData() async { final manager = Provider.of<AppStateManager>(context, listen: false); final response = await manager.getQrData(); if (response.status == Status.COMPLETED && response.data != null) { // 预加载新二维码图片,确保加载完成再更新UI final imageBytes = Base64Decoder().convert(response.data!.qrcode); final imageProvider = MemoryImage(imageBytes); await precacheImage(imageProvider, context); setState(() => _currentQrData = response.data); } // 请求失败时保留旧二维码,避免空白 } @override void dispose() { _qrUpdateTimer?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { // 优先显示当前缓存的二维码,无数据时显示加载 if (_currentQrData != null) { final imageProvider = MemoryImage( Base64Decoder().convert(_currentQrData!.qrcode) ); return InkWell( onTap: () => launchInNonBrowser( Uri.parse("lightning:${_currentQrData!.lnurl}") ), child: Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20.0) ), elevation: 10, child: Padding( padding: const EdgeInsets.all(8.0), child: Image(image: imageProvider), ), ), ); } else { return Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20.0) ), elevation: 10, child: const Padding( padding: EdgeInsets.all(120.0), child: Center( child: SizedBox( height: 50, width: 50, child: CircularProgressIndicator(), ), ), ), ); } } }
关键优化点说明
- 状态缓存:始终保留当前有效的二维码数据,更新时只在新数据准备完成后替换,避免中间空白。
- 预加载图片:通过
precacheImage提前加载新图片,确保替换时图片已就绪,消除加载延迟。 - Timer替代StreamBuilder:避免Stream周期触发导致的快照状态波动,减少不必要的UI重绘。
- 缓存初始化:从本地缓存读取历史二维码,首次打开页面时直接显示,提升体验。
内容的提问来源于stack exchange,提问作者Vladimir
相关产品推荐
相关产品推荐

