You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

更新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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 06:37:04