xvrh / lottie-flutter

Render After Effects animations natively on Flutter. This package is a pure Dart implementation of a Lottie player.
https://pub.dev/packages/lottie
MIT License
1.13k stars 197 forks source link

Lottie not working in web since 3.1.2 #353

Open konsultaner opened 1 month ago

konsultaner commented 1 month ago

After updating from 3.1.1 to 3.1.2 I cannot use it in my web view anymore I get the following error:

The following UnsupportedError was thrown during paint():
Unsupported operation: toImageSync is not supported on the HTML backend. Use drawPicture instead, or toImage.

The relevant error-causing widget was: 
  RawLottie RawLottie:file://.pub-cache/hosted/pub.dev/lottie-3.1.2/lib/src/lottie.dart:442:16
When the exception was thrown, this was the stack: 
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 296:3       throw_
lib/_engine/engine/picture.dart 90:5                                              toImageSync
packages/lottie/src/render_cache/store_raster.dart 123:24                         imageForProgress
packages/lottie/src/render_cache/store_raster.dart 144:23                         draw
packages/lottie/src/render_cache/store_raster.dart 59:17                          draw
packages/lottie/src/lottie_drawable.dart 219:30                                   draw
packages/lottie/src/render_lottie.dart 268:5                                      paint
packages/flutter/src/rendering/object.dart 3239:7                                 [_paintWithContext]
packages/flutter/src/rendering/object.dart 250:12                                 paintChild
packages/flutter/src/rendering/proxy_box.dart 130:12                              paint
packages/flutter/src/rendering/object.dart 3239:7                                 [_paintWithContext]
packages/flutter/src/rendering/object.dart 166:10                                 _repaintCompositedChild
packages/flutter/src/rendering/object.dart 109:5                                  repaintCompositedChild
packages/flutter/src/rendering/object.dart 1182:31                                flushPaint
packages/flutter/src/rendering/object.dart 1192:14                                flushPaint
packages/flutter/src/rendering/binding.dart 579:5                                 drawFrame
packages/flutter/src/widgets/binding.dart 1138:13                                 drawFrame
packages/flutter/src/rendering/binding.dart 443:5                                 [_handlePersistentFrameCallback]
packages/flutter/src/scheduler/binding.dart 1392:7                                [_invokeFrameCallback]
packages/flutter/src/scheduler/binding.dart 1313:9                                handleDrawFrame
packages/flutter/src/scheduler/binding.dart 1171:5                                [_handleDrawFrame]
lib/_engine/engine/platform_dispatcher.dart 1404:5                                invoke
lib/_engine/engine/platform_dispatcher.dart 307:5                                 invokeOnDrawFrame
lib/_engine/engine/initialization.dart 187:36                                     <fn>
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 426:37  _checkAndCall
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 431:39  dcall
The following RenderObject was being processed when the exception was fired: RenderLottie#51e5b relayoutBoundary=up5
...  parentData: <none> (can use size)
...  constraints: BoxConstraints(0.0<=w<=399.0, 0.0<=h<=1142.0)
...  size: Size(100.0, 100.0)
...  composition: LottieComposition:
    Ebene 4/n loader Konturen
        Shapes:
            ShapeGroup{name: 'Gruppe 1' Shapes: [ShapePath{name=Pfad 1, index=0}, ShapeFill{color=, fillEnabled=false}, Instance of 'AnimatableTransform']}
            ShapeGroup{name: 'Gruppe 2' Shapes: [ShapePath{name=Pfad 1, index=0}, ShapeFill{color=, fillEnabled=false}, Instance of 'AnimatableTransform']}
            ShapeGroup{name: 'Gruppe 3' Shapes: [ShapePath{name=Pfad 1, index=0}, ShapeFill{color=, fillEnabled=false}, Instance of 'AnimatableTransform']}

...  width: 100.0
...  height: 100.0
...  fit: contain
...  alignment: Alignment.center
RenderObject: RenderLottie#51e5b relayoutBoundary=up5
  parentData: <none> (can use size)
  constraints: BoxConstraints(0.0<=w<=399.0, 0.0<=h<=1142.0)
  size: Size(100.0, 100.0)
  composition: LottieComposition:
    Ebene 4/n loader Konturen
        Shapes:
            ShapeGroup{name: 'Gruppe 1' Shapes: [ShapePath{name=Pfad 1, index=0}, ShapeFill{color=, fillEnabled=false}, Instance of 'AnimatableTransform']}
            ShapeGroup{name: 'Gruppe 2' Shapes: [ShapePath{name=Pfad 1, index=0}, ShapeFill{color=, fillEnabled=false}, Instance of 'AnimatableTransform']}
            ShapeGroup{name: 'Gruppe 3' Shapes: [ShapePath{name=Pfad 1, index=0}, ShapeFill{color=, fillEnabled=false}, Instance of 'AnimatableTransform']}

  width: 100.0
  height: 100.0
  fit: contain
  alignment: Alignment.center
xvrh commented 1 month ago

@konsultaner can you show how you configure the lottie widget? Do you use the renderCache property?

konsultaner commented 1 month ago

@xvrh sorry, totally rushed that issue. This is my file including the lottie asset.

import 'dart:math';

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

class QRCodeLoginNoScan extends StatelessWidget {
  const QRCodeLoginNoScan({super.key});

  @override
  Widget build(BuildContext context) {
    var lottieDelegates = [
      if (Theme.of(context).brightness == Brightness.dark)
        ValueDelegate.colorFilter(
          const [ '**'],
          value: ColorFilter.mode(Theme.of(context).colorScheme.onSurface, BlendMode.src),
        ),
      if (Theme.of(context).brightness == Brightness.light)
        ValueDelegate.colorFilter(
          const [ '**', 'scan', '**'],
          value: ColorFilter.mode(Theme.of(context).primaryColor, BlendMode.src),
        ),
      if (Theme.of(context).brightness == Brightness.light)
        ValueDelegate.colorFilter(
          const [ '**', 'phone', 'Group 1', '**'],
          value: ColorFilter.mode(Theme.of(context).primaryColor, BlendMode.src),
        )
    ];
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 200, horizontal: 20),
      child: Center(
        child: FittedBox(
          fit: BoxFit.contain,
          child: Lottie.asset(
            width: max(MediaQuery.of(context).size.width,MediaQuery.of(context).size.height),
            height: max(MediaQuery.of(context).size.width,MediaQuery.of(context).size.height),
            fit: BoxFit.contain,
            'assets/lottie/qr_code_animation.json',
            renderCache: RenderCache.raster,
            delegates: LottieDelegates(values: lottieDelegates),
          ),
        ),
      ),
    );
  }
}
xvrh commented 1 month ago

I think you should drop the renderCache: RenderCache.raster. It's not supported in the web with the html-renderer.

Alternatively, you can also try the canvaskit renderer.

konsultaner commented 1 month ago

I think you should drop the renderCache: RenderCache.raster. It's not supported in the web with the html-renderer.

Maybe the package should add a debug warning and have a kIsWeb check on the renderCache so nobody else will have to face this issue?

xvrh commented 1 month ago

I think it works with the canvaskit renderer so kIsWeb is not enough. We need to detect the web-renderer used and I don't know if it's possible. And this library works poorly with the html-renderer anyway, they are a lot of limitations.

Furthermore it's likely that the html-renderer will go away: https://github.com/flutter/flutter/issues/145954

konsultaner commented 1 month ago

I changed my code to:

import 'dart:math';

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

class QRCodeLoginNoScan extends StatelessWidget {
  const QRCodeLoginNoScan({super.key});

  @override
  Widget build(BuildContext context) {
    var lottieDelegates = [
      if (Theme.of(context).brightness == Brightness.dark)
        ValueDelegate.colorFilter(
          const [ '**'],
          value: ColorFilter.mode(Theme.of(context).colorScheme.onSurface, BlendMode.src),
        ),
      if (Theme.of(context).brightness == Brightness.light)
        ValueDelegate.colorFilter(
          const [ '**', 'scan', '**'],
          value: ColorFilter.mode(Theme.of(context).primaryColor, BlendMode.src),
        ),
      if (Theme.of(context).brightness == Brightness.light)
        ValueDelegate.colorFilter(
          const [ '**', 'phone', 'Group 1', '**'],
          value: ColorFilter.mode(Theme.of(context).primaryColor, BlendMode.src),
        )
    ];
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 200, horizontal: 20),
      child: Center(
        child: Lottie.asset(
          width: max(MediaQuery.of(context).size.width,MediaQuery.of(context).size.height),
          height: max(MediaQuery.of(context).size.width,MediaQuery.of(context).size.height),
          //fit: BoxFit.contain,
          'assets/lottie/qr_code_animation.json',
          renderCache: kIsWeb ? null : RenderCache.raster,
          delegates: LottieDelegates(values: lottieDelegates),
        ),
      ),
    );
  }
}

on first load eveything works great but on hot-reload I get this error:

The following PersistedSurfaceException was thrown during a scheduler callback:
PersistedScene: is in an unexpected state.
Expected one of: PersistedSurfaceState.active, PersistedSurfaceState.pendingUpdate
But was: PersistedSurfaceState.released

When the exception was thrown, this was the stack: 
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 296:3       throw_
lib/_engine/engine/html/surface.dart 181:3                                        debugAssertSurfaceState
lib/_engine/engine/html/surface.dart 705:12                                       update
lib/_engine/engine/html/scene_builder.dart 506:9                                  <fn>
lib/_engine/engine/profiler.dart 41:18                                            timeAction
lib/_engine/engine/html/scene_builder.dart 502:12                                 build
packages/flutter/src/rendering/layer.dart 1138:35                                 buildScene
packages/flutter/src/rendering/view.dart 304:30                                   compositeFrame
packages/flutter/src/rendering/binding.dart 582:19                                drawFrame
packages/flutter/src/widgets/binding.dart 1138:13                                 drawFrame
packages/flutter/src/rendering/binding.dart 443:5                                 [_handlePersistentFrameCallback]
packages/flutter/src/scheduler/binding.dart 1392:7                                [_invokeFrameCallback]
packages/flutter/src/scheduler/binding.dart 1313:9                                handleDrawFrame
packages/flutter/src/scheduler/binding.dart 1171:5                                [_handleDrawFrame]
lib/_engine/engine/platform_dispatcher.dart 1404:5                                invoke
lib/_engine/engine/platform_dispatcher.dart 307:5                                 invokeOnDrawFrame
lib/_engine/engine/initialization.dart 187:36                                     <fn>
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 426:37  _checkAndCall
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 431:39  dcall

Is that lottie related? Never seen anything like this.