如何无感显示首页的网络图片

需求:当App的第一个页面是首页时,每次当App启动时会立马显示网络图片,而不是需要等待Api请求完成、等待图片下载后才显示图片,效果就相当于显示本地图片。

一、本地缓存Api结果

去掉Api请求的等待,是将Api的请求结果缓存到本地,每次App开启时优先从本地获取图片数据显示。

二、预加载图片到内存

去掉图片下载加载的等待,是在图片显示之前预加载图片到内存中,这样当图片显示的时候就不会感觉到有Loading

预加载需要结合cached_network_imageprecacheImage(ImageProvider provider, BuildContext context)来完成,cached_network_image负责将图片缓存到本地,precacheImage负责将缓存图片预加载到内存。

三、预加载图片完成之后才显示首页

App的第一帧就是首页,我们需要保证预加载图片之后才显示首页,不然预加载图片到内存也是需要一定时间的,会导致图片有一段时间Loading

由于首页是第一帧,我们只能将预加载方法放在main()方法里面,但预加载方法precacheImage需要上下文context,此时我们就需要用到deferFirstFrame()allowFirstFrame()

  • deferFirstFrame(): 用于告诉Flutter引擎暂时不要渲染首帧。在App启动时,有一些初始化操作需要完成,例如数据预加载、用户身份验证等,防止UI在未准备好时显示给用户,影响用户体验。

  • allowFirstFrame():用于告诉Flutter引擎可以渲染首帧了。在完成初始化任务后调用,需要与deferFirstFrame()配套使用。

void main() {

  // 延迟首帧
  final binding = WidgetsFlutterBinding.ensureInitialized();
  binding.deferFirstFrame();

  runApp(MyApp());

  // 模拟初始化操作
  Future.delayed(Duration(seconds: 3), () {
    binding.allowFirstFrame(); // 初始化完成后允许首帧渲染
  });
}

四、显示图片

在显示图片的时候不要使用CachedNetworkImage组件,因为CachedNetworkImage提供了很多高级功能,例如占位图、加载指示器、错误显示等。其占位图是从加载图像时显示的,即时图片已经缓存到内存中了,占位图也会显示一小会儿。

使用CachedNetworkImageProvider,主要用于更底层的图片加载逻辑,没有额外的UI功能(如占位图、错误处理)。

五、参考代码

void main() {
  final binding = WidgetsFlutterBinding.ensureInitialized();
  binding
    ..deferFirstFrame()
    ..addPostFrameCallback((_) async {
      final context = binding.rootElement;
      if (context != null) {
        await Init.instance.initialize(context);
      }
      binding.allowFirstFrame();
    });
  runApp(MyApp());
}

/// 初始化
class Init {
  Init._();
  static final instance = Init._();

  final homeRepo = HomeHttpRepository();

  Future<void> initialize(BuildContext context) async {
    /// 預加載首頁輪播圖片
    final homeBanners = await SpRepository.instance.getHomeBanners();
    if (homeBanners != null) {
      /// 本地有Api缓存数据,直接预加载
      await precacheBannerImage(context, banners: homeBanners);
    } else {
      /// 本地没有Api缓存数据,先获取Api数据,再缓存
      ///
      /// 注意:App一般会有欢迎页,所以若没有缓存数据,其实相当于第一次下载App,会先进入欢迎页,以下逻辑是在欢迎页完成,所以不需要等待
      unawaited(
        homeRepo.banners().then((value) {
          precacheBannerImage(context, banners: value);
        }),
      );
    }
  }

  /// 預加載輪播圖
  Future<void> precacheBannerImage(
    BuildContext context, {
    required List<HomeBannerModel> banners,
  }) async {
    for (final banner in banners) {
      if (banner.imageUrl.isURL) {
        await precacheImage(
          CachedNetworkImageProvider(banner.imageUrl!),
          context,
        );
      }
    }
  }
}

/// 显示图片
Image(
  fit: BoxFit.cover,
  image: CachedNetworkImageProvider(imageUrl),
)
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容