在Flutter中是否有一种方法可以预缓存Lottie动画?

6

我有一个Lottie动画,用作加载屏幕:

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:lottie/lottie.dart';

class Loading extends StatelessWidget {
  final String loadingText;
  Loading({this.loadingText});

  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          if (loadingText != null) _buildLoadingText(loadingText),
          _buildAnimation(),
        ],
      ),
    );
  }

  Widget _buildLoadingText(String text) {
    return Text(
      loadingText,
      style: GoogleFonts.poppins(
          textStyle:
              TextStyle(fontWeight: FontWeight.w500, color: Colors.black)),
    );
  }

  Widget _buildAnimation() {
    return Lottie.asset('assets/lottie/heartbeat_loading.json',
        width: 300, repeat: true, animate: true);
  }
}

当我的应用程序最初加载时,我会像这样使用它:

_determineHome() {
    return StreamBuilder(
      stream: AppBlocContainer().authenticationBloc().loggedIn,
      builder: (context, AsyncSnapshot<AuthenticationStatus> snapshot) {
        // return Loading();
        return AnimatedSwitcher(
            duration: Duration(seconds: 2),
            child: !snapshot.hasData
                ? Loading(
                    loadingText: 'Loading...',
                  )
                : _buildSecondChild(snapshot.data));
      },
    );

这个方案是有效的,唯一的问题是Lottie动画加载可能会晚一两秒,当Lottie资源加载完成时,页面已经完成了过渡。

我曾尝试通过在main()函数中预缓存SVG图像来解决这个问题:

    Future.wait([
      precachePicture(
        ExactAssetPicture(
            SvgPicture.svgStringDecoder, 'assets/svg/login.svg'),
        null,
      ),
      precachePicture(
        ExactAssetPicture(
            SvgPicture.svgStringDecoder, 'assets/svg/logo.svg'),
        null,
      ),
      precachePicture(
        ExactAssetPicture(
            SvgPicture.svgStringDecoder, 'assets/svg/signup_panel_1.svg'),
        null,
      )
    ]);

我能用Lottie做同样的事情吗?
1个回答

6
我发现在flutter中的lottie包自带了自动缓存功能。(无论是否需要都会将动画缓存在内存中)
方法A(自定义): 你只需要调用Lottie.asset('assets/Logo.json')一次,第二次调用Lottie.asset('assets/Logo.json')时,由于已经被缓存,不会有延迟。 这可以作为一个欺骗式缓存方法,例如可以像下面这样在启动屏幕中首先加载它: 通过用SizedBox包装并给予0的高度/宽度或使用visible属性为false但maintainState为true的Visibility小部件,这样它就不可见但仍然存在于小部件树中并加载到内存中。
        Visibility(
          visible: false,
          maintainSize: false,
          maintainState: true,
          maintainAnimation: false,
          maintainInteractivity: false,
          child: Row(
            children: [
               Lottie.asset('assets/Logo.json'),
            ],
          ),
        ),
            

下次需要时,只需调用Lottie.asset('assets/Logo.json'),它将无延迟加载。

方法B: 这是在Github上由一个叫Pante的人提到的。

class LottieCache {

  final Map<String, LottieComposition> _compositions = {};

  /// Caches the given [LottieAsset]s.

  Future<void> add(String assetName) async {
      _compositions[assetName] = await AssetLottie(assetName).load();
  }
  
  Widget load(String assetName, Widget fallback) {
    final composition = _compositions[assetName];
    return composition != null ? Lottie(composition: composition) : fallback;
  }

}

这里有两种方式: 1)使用AssetLottie(assetName).load()方法;提供LottieCache对象以便随时访问其加载方法。 2)完全忽略load方法和合成方式(您可以注释掉load方法);因为AssetLottie(assetName).load()会将动画加载到RAM中,就像Lottie.asset('assets/Logo.json')一样。 因此,只需调用其add方法即可将动画加载到RAM中,并且下次需要时可以立即使用。
C方法: 这是Flutter Lottie文档中提到的自定义加载方法。
class _MyWidgetState extends State<MyWidget> {
  late final Future<LottieComposition> _composition;

  @override
  void initState() {
    super.initState();

    _composition = _loadComposition();
  }

  Future<LottieComposition> _loadComposition() async {
    var assetData = await rootBundle.load('assets/LottieLogo1.json');
    return await LottieComposition.fromByteData(assetData);
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<LottieComposition>(
      future: _composition,
      builder: (context, snapshot) {
        var composition = snapshot.data;
        if (composition != null) {
          return Lottie(composition: composition);
        } else {
          return const Center(child: CircularProgressIndicator());
        }
      },
    );
  }
}

第三种方法是最好的!即使它被原始团队推荐。 - ᴅ ᴇ ʙ ᴊ ᴇᴇ ᴛ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接