Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Start Widget Animation after Hero Animation has finished

Let's say I have two pages, Page1 and Page2 containing a Hero. In Page2, I want to start a Widget animation after the Hero animation has finished.

Is it possible to get notified in Page2 via a Callback about the state of the Hero animation?

The only workaround I found so far is to add a delay to the Widget animation to avoid that it starts before the Hero animation has finished:

class Page1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) => Container(
        child: Hero(
          tag: "hero-tag",
          child: IconButton(
              icon: Icon(Icons.person),
              onPressed: () {
                Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (BuildContext context) => Page2(),
                    ));
              }),
        ),
      );
}

class Page2 extends StatefulWidget {
  @override
  _Page2State createState() => _Page2State();
}

class _Page2State extends State<Page2> with TickerProviderStateMixin {
  AnimationController _controller;
  Animation _fabAnimation;

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

    _controller = AnimationController(
        duration: const Duration(milliseconds: 400), vsync: this);

    _fabAnimation = Tween<double>(
      begin: 0.0,
      end: 1.0,
    ).animate(
      CurvedAnimation(
        parent: _controller,

        // delay to wait for hero animation to end
        curve: Interval(
          0.300,
          1.000,
          curve: Curves.ease,
        ),
      ),
    );

    _controller.forward();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Row(
        children: <Widget>[
          Hero(
            tag: "hero-tag",
            child: Icon(Icons.person),
          ),
          ScaleTransition(
            scale: _fabAnimation,
            child: FloatingActionButton(
              child: Icon(
                Icons.camera_alt,
              ),
              onPressed: () {},
            ),
          ),
        ],
      ),
    );
  }
}
like image 315
huonderv Avatar asked Dec 28 '18 15:12

huonderv


1 Answers

Ok here is the better answer. Based on a previous answer here. Flutter: Run method on Widget build complete

Short Answer I tested your scenario with the WidgetsBinding's postFrameCallback() as shown below and I thing now the tween animation on the camera icon works after the hero animation has finished. You can try by wrapping your _contoller.forward inside this callback as shown below.

//this ensures the animation is forwarded only after the widget is rendered 
//atleast one frame.

// based on this answer: 
//https://stackoverflow.com/questions/49466556/flutter-run-method-on-widget-build-complete

WidgetsBinding.instance.addPostFrameCallback((_) {
//Following future can be uncommented to check 
//if the call back works after 5 seconds.
//Future.delayed(const Duration(seconds: 5), () => {
  _controller.forward();
 //});
});

I also tried RouteAwareWidget option given below but it didn't make much difference. https://api.flutter.dev/flutter/widgets/RouteObserver-class.html

Full Working Code

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      //navigatorObservers: [routeObserver],
      initialRoute: '/',
      routes: {
        '/': (context) => Page1(),
        '/page2': (context) => Page2(),
      },
    );
  }
}

class Page1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Page 1'),
      ),
      body: Center(
        child: Hero(
          tag: "hero-tag",
          child: IconButton(
              icon: Icon(Icons.person),
              onPressed: () {
                Navigator.pushNamed(context, '/page2');
              }),
        ),
      ),
    );
  }
}

class Page2 extends StatefulWidget {
  @override
  _Page2State createState() => _Page2State();
}

class _Page2State extends State<Page2> with TickerProviderStateMixin {
  AnimationController _controller;
  Animation _fabAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
        duration: const Duration(milliseconds: 400), vsync: this);

    _fabAnimation = Tween<double>(
      begin: 0.0,
      end: 1.0,
    ).animate(
      CurvedAnimation(
        parent: _controller,

        // delay to wait for hero animation to end
//        curve: Interval(
//          0.900,
//          1.000,
        curve: Curves.ease,
        //),
      ),
    );

// this ensures the animation is forwarded only after the widget is rendered 
//atleast one frame.
// based on: 
//https://stackoverflow.com/questions/49466556/flutter-run-method-on-widget-build-complete
    WidgetsBinding.instance.addPostFrameCallback((_) {
      //Future.delayed(const Duration(seconds: 5), () =>
      _controller.forward();
      //);
    });
    //end
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Screen 2')),
      body: Center(
        child: Row(
          children: <Widget>[
            Hero(
              tag: "hero-tag",
              child: Icon(Icons.person),
            ),
            ScaleTransition(
              scale: _fabAnimation,
              child: FloatingActionButton(
                child: Icon(
                  Icons.camera_alt,
                ),
                onPressed: () {
                  Navigator.pop(
                    context,
                  ); // Added to avoid exceptions of no material widget.
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

like image 149
Abhilash Chandran Avatar answered Sep 22 '22 17:09

Abhilash Chandran