Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FlareActor flashing when used with Flutter Hero widget #111

Closed
DamienMrtl opened this issue Jun 23, 2019 · 5 comments
Closed

FlareActor flashing when used with Flutter Hero widget #111

DamienMrtl opened this issue Jun 23, 2019 · 5 comments

Comments

@DamienMrtl
Copy link

DamienMrtl commented Jun 23, 2019

When using a FlareActor inside a Flutter Hero (to transition the Actor to a new page for example), we see two "flashes". One when the Hero creates a new Actor to make the transition to destination page and once again when creating the Actor on the destination page at the end of the transition.

Video of the issue

It looks like this is due to the loading time when displaying an Actor dynamically.

Tested with Flutter v1.5.4-hotfix.2 & Flare-Flutter 1.5.2 in debug and release modes

Code to replicate (main.dart):

import 'package:flutter/material.dart';
import 'package:flare_flutter/flare_actor.dart';
import 'package:flutter/scheduler.dart' show timeDilation;

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    timeDilation = 5;
    return Container(
      color: Colors.white,
      child: MaterialApp(
        title: "Flare Bot",
        routes: {
          "/page1": (context) => Page1(),
          "/page2": (context) => Page2(),
        },
        home: Page1(),
      ),
    );
  }
}

class Page1 extends StatelessWidget {
  const Page1({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => Navigator.pushNamed(context, "/page2"),
      child: Container(
        child: Align(
            alignment: Alignment.topCenter,
            child: Hero(
              tag: "tag",
              child: Container(
                width: 300,
                height: 300,
                child: Stack(
                  children: <Widget>[
                    FlareActor(
                      'assets/Chatbot-back.flr',
                    ),
                    FlareActor(
                      'assets/Chatbot-front.flr',
                    )
                  ],
                ),
              ),
            )),
      ),
    );
  }
}

class Page2 extends StatelessWidget {
  const Page2({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => Navigator.pushNamed(context, "/page1"),
      child: Align(
          alignment: Alignment.bottomCenter,
          child: Container(
            width: 300,
            height: 300,
            child: Hero(
              tag: "tag",
              child: Stack(
                children: <Widget>[
                  FlareActor(
                    'assets/Chatbot-back.flr',
                    fit: BoxFit.fitHeight,
                    snapToEnd: false,
                    shouldClip: true,
                  ),
                  FlareActor(
                    'assets/Chatbot-front.flr',
                    animation: "face-to-broken",
                    fit: BoxFit.fitHeight,
                    snapToEnd: false,
                    shouldClip: true,
                  )
                ],
              ),
            ),
          )),
    );
  }
}

And using the 2 assets :
assets.zip

Do you know any workaround or tricks to display the Actor faster, maybe using some sort of caching ?

@luigi-rosso
Copy link
Contributor

I think the right way to do this is to load the Flare file in the context of the widget that will host the hero. Then you'd need to use a custom LeafRenderWidget (similar to the FlareActor) which displays the Flare file and shares it across the different LeafRenderWidgets.

Flare does use caching internally (and I actually tested your code with a pre-warmed cache with the code below) but the flicker is still there as retrieving the items from cache is also asynchronous:

Prewarmed (still flickers) cache:

import 'package:flare_flutter/flare_cache.dart';
import 'package:flare_flutter/flare_cache_asset.dart';
import 'package:flutter/services.dart';

// ... leaving out other imports

Future<void> main() async
{
	FlareCacheAsset asset1 = await cachedActor(rootBundle, 'assets/Chatbot-back.flr');
	asset1.ref();
	FlareCacheAsset asset2 = await cachedActor(rootBundle, 'assets/Chatbot-front.flr');
	asset2.ref();
	
	runApp(MyApp());
}

The right solution is a more involved example, I'll provide you with that as soon as I have a little time to spend on it.

@luigi-rosso
Copy link
Contributor

Alright give this example a try:
https://github.com/luigi-rosso/flare_flutter_shared_artboard

The two pages share the same Flare artboards and controller for the animation so that they can control the same Flare artboard without reloading and across different widgets. Result is no flickering and better animation control!

Notice how the animation gets triggered when the user taps:
https://github.com/luigi-rosso/flare_flutter_shared_artboard/blob/b08a67539728efbaf9814158315ad6d443c56c26/lib/main.dart#L70-L73

@DamienMrtl
Copy link
Author

Wow thanks for your reply @luigi-rosso . I wasn't asking for so much, now even the animation is working correctly while the Flare is moving 😃 . Thank you so much.

BTW: I first had a "Type" error when building your example.
To make it work I had to cast _indices to Int32List on line 1025 of "flare.dart" maybe because I use Flutter 1.5.4-hotfix.2

 _canvasVertices = ui.Vertices.raw(ui.VertexMode.triangles, _vertexBuffer,
        indices: _indices as Int32List, textureCoordinates: _uvBuffer);

@luigi-rosso
Copy link
Contributor

Oh yes, that's related to breaking changes between dev/master and stable (Flutter 1.5.4-hotfix.2). I'd strongly recommend you use the dev/master Flutter channel as there are other fixes in there that are worth having (like aot compiler bugs that will manifest as invisible shapes on certain device architectures with Flare) and also faster loading as we can perform the whole Flare file load in an isolate.

@JHBitencourt
Copy link

For those who are trying to implement the same thing and are wondering how to use the new cachedActor() method:

Old way:

final asset = await cachedActor(rootBundle, 'assets/file.flr');
asset.ref();

New way:

final provider = AssetFlare(bundle: rootBundle, name: 'assets/file.flr');
final asset = await cachedActor(provider);
asset.ref();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants