Calling Animation in 2 Different Widgets from a 3rd (Not the Parent Widget)

Viewed 32

I have 2 pages (Page1 and Page2) To navigate between them I have a custom PrimaryMenu widget that is the same for both pages. The PrimaryMenu is contained in BodyPage1 and BodyPage2 respectively. I also have a custom Header widget. Both pages have animation as does the header.

What I am look to do it press one of the InkWell widgets then the animation is reversed on the current page, then the new page is called. I know how to call the new page, I have a rough understanding on how to use GlobalKey but im starting to think this cant be done with GlobalKey. Below I will show the individual widgets i have: dartpad link just in case - https://dartpad.dev/88b8536ea7888b5621d7d80acdcd2887

class Header extends StatefulWidget {
  @override
  _HeaderState createState() => _HeaderState();
}

class _HeaderState extends State<Header> with SingleTickerProviderStateMixin {
  AnimationController transitionAnimation;

  @override
  void initState() {
    super.initState();
    transitionAnimation = AnimationController(
      duration: const Duration(seconds: 3),
      vsync: this,
    );
    transitionAnimation.forward();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        color: Color(0x88dddddd),
        height: MediaQuery.of(context).size.height,
        width: MediaQuery.of(context).size.width,
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            SizedBox(
              width: 8,
            ),
            AnimatedBuilder(
              animation: transitionAnimation,
              builder: (context, child) {
                return SlideTransition(
                  position: Tween<Offset>(
                          begin: const Offset(-2, 0), end: const Offset(0, 0))
                      .animate(CurvedAnimation(
                          curve: const Interval(0, 0.3,
                              curve: Curves.easeInOutBack),
                          parent: transitionAnimation)),
                  child: child,
                );
              },
              child: Container(
                height: 100,
                width: MediaQuery.of(context).size.width * 0.135,
                color: Colors.pink,
              ),
            ),
            Expanded(
              child: Container(),
            ),
            Container(
              width: 100,
              height: 50,
              color: Colors.purple,
            ),
            SizedBox(
              width: 8,
            ),
            Container(
              height: 50,
              width: 50,
              color: Colors.amber,
            ),
            SizedBox(
              width: 8,
            )
          ],
        ));
  }
}
class Page1 extends StatefulWidget {
  @override
  _Page1State createState() => _Page1State();
}

class _Page1State extends State<Page1> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      extendBodyBehindAppBar: true,
      appBar: PreferredSize(
        preferredSize: Size(MediaQuery.of(context).size.width, 120),
        child: Header(),
      ),
      body: BodyPage1(),
    );
  }
}
class PrimaryMenu extends StatefulWidget {
  @override
  _PrimaryMenuState createState() => _PrimaryMenuState();
}

class _PrimaryMenuState extends State<PrimaryMenu> {
  @override
  Widget build(BuildContext context) {
    return Container(
        height: MediaQuery.of(context).size.height,
        width: MediaQuery.of(context).size.width * 0.15,
        color: Colors.blue,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(
                height: 40,
                width: MediaQuery.of(context).size.width * 0.135,
                color: Colors.grey,
                child: InkWell(
                  onTap: () {
                    print('1');
                    Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (context) => Page1(),
                        ));
                  },
                )),
            SizedBox(height: 16),
            Container(
                height: 40,
                width: MediaQuery.of(context).size.width * 0.135,
                color: Colors.black,
                child: InkWell(
                  onTap: () {
                    print('2');
                    Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (context) => Page2(),
                        ));
                  },
                )),
          ],
        ));
  }
}
class BodyPage1 extends StatefulWidget {

  @override
  _BodyPage1State createState() => _BodyPage1State();
}

class _BodyPage1State extends State<BodyPage1>
    with SingleTickerProviderStateMixin {
  AnimationController transitionAnimation;

  @override
  void initState() {
    super.initState();
    transitionAnimation = AnimationController(
      duration: const Duration(seconds: 3),
      vsync: this,
    );
    transitionAnimation.forward();
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        AnimatedBuilder(
            animation: transitionAnimation,
            builder: (context, child) {
              return SlideTransition(
                position: Tween<Offset>(
                        begin: const Offset(-2, 0), end: const Offset(0, 0))
                    .animate(CurvedAnimation(
                        curve:
                            const Interval(0, 0.3, curve: Curves.easeInOutBack),
                        parent: transitionAnimation)),
                child: child,
              );
            },
            child: PrimaryMenu()),
        AnimatedBuilder(
          animation: transitionAnimation,
          builder: (context, child) {
            return SlideTransition(
                position: Tween<Offset>(
                        begin: const Offset(-2, 0), end: const Offset(0, 0))
                    .animate(CurvedAnimation(
                        curve: const Interval(0.3, 1, curve: Curves.easeIn),
                        parent: transitionAnimation)),
                child: child);
          },
          child: Padding(
            padding: EdgeInsets.only(top: 140, left: 20, right: 20, bottom: 20),
            child: Container(
              height: MediaQuery.of(context).size.height - 140,
              width: (MediaQuery.of(context).size.width * .85) - 40,
              color: Colors.grey,
            ),
          ),
        )
      ],
    );
  }
}
class Page2 extends StatefulWidget {
  @override
  _Page2State createState() => _Page2State();
}

class _Page2State extends State<Page2> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      extendBodyBehindAppBar: true,
      appBar: PreferredSize(
        preferredSize: Size(MediaQuery.of(context).size.width, 120),
        child: Header(),
      ),
      body: BodyPage2(),
    );
  }
}
class BodyPage2 extends StatefulWidget {
  @override
  _BodyPage2State createState() => _BodyPage2State();
}

class _BodyPage2State extends State<BodyPage2>
    with SingleTickerProviderStateMixin {
  AnimationController transitionAnimation;

  @override
  void initState() {
    super.initState();
    transitionAnimation = AnimationController(
      duration: const Duration(seconds: 3),
      vsync: this,
    );
    transitionAnimation.forward();
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        AnimatedBuilder(
            animation: transitionAnimation,
            builder: (context, child) {
              return SlideTransition(
                position: Tween<Offset>(
                        begin: const Offset(-2, 0), end: const Offset(0, 0))
                    .animate(CurvedAnimation(
                        curve:
                            const Interval(0, 0.3, curve: Curves.easeInOutBack),
                        parent: transitionAnimation)),
                child: child,
              );
            },
            child: PrimaryMenu()),
        AnimatedBuilder(
          animation: transitionAnimation,
          builder: (context, child) {
            return SlideTransition(
                position: Tween<Offset>(
                        begin: const Offset(-2, 0), end: const Offset(0, 0))
                    .animate(CurvedAnimation(
                        curve: const Interval(0.3, 1, curve: Curves.easeIn),
                        parent: transitionAnimation)),
                child: child);
          },
          child: Padding(
            padding: EdgeInsets.only(top: 140, left: 20, right: 20, bottom: 20),
            child: Container(
              height: MediaQuery.of(context).size.height - 140,
              width: (MediaQuery.of(context).size.width * .85) - 40,
              color: Colors.black,
            ),
          ),
        )
      ],
    );
  }
}

Also I have set up a dartpad showing what i have managed to do with GlobalKey, I can only get it to work if i use a FloatingActionButton on the Scaffold - https://dartpad.dev/5979f44ecaa9cf2e22b4ce0cc9c23aa8

Sorry that there is a lot of code, I have tried to condense it as much as possible

1 Answers

All you need to do is to pass the AnimationController from respective BodyPage to your PrimaryMenu which is responsible to call Page1 and Page2. Once the AnimationController is available to PrimaryMenu it will reverse the animation first and .then call the respective page. Please see the working code below :

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(visualDensity: VisualDensity.adaptivePlatformDensity),
      debugShowCheckedModeBanner: false,
      home: Page1(),
    );
  }
}

class Header extends StatefulWidget {
  @override
  _HeaderState createState() => _HeaderState();
}

class _HeaderState extends State<Header> with SingleTickerProviderStateMixin {
  AnimationController transitionAnimation;

  @override
  void initState() {
    super.initState();
    transitionAnimation = AnimationController(
      duration: const Duration(seconds: 3),
      vsync: this,
    );
    transitionAnimation.forward();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
        color: const Color(0x88dddddd),
        height: MediaQuery.of(context).size.height,
        width: MediaQuery.of(context).size.width,
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            const SizedBox(
              width: 8,
            ),
            AnimatedBuilder(
              animation: transitionAnimation,
              builder: (context, child) {
                return SlideTransition(
                  position: Tween<Offset>(
                          begin: const Offset(-2, 0), end: const Offset(0, 0))
                      .animate(CurvedAnimation(
                          curve: const Interval(0, 0.3,
                              curve: Curves.easeInOutBack),
                          parent: transitionAnimation)),
                  child: child,
                );
              },
              child: Container(
                height: 100,
                width: MediaQuery.of(context).size.width * 0.135,
                color: Colors.pink,
              ),
            ),
            Expanded(
              child: Container(),
            ),
            Container(
              width: 100,
              height: 50,
              color: Colors.purple,
            ),
            const SizedBox(
              width: 8,
            ),
            Container(
              height: 50,
              width: 50,
              color: Colors.amber,
            ),
            const SizedBox(
              width: 8,
            )
          ],
        ));
  }
}

class Page1 extends StatefulWidget {
  @override
  _Page1State createState() => _Page1State();
}

class _Page1State extends State<Page1> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      extendBodyBehindAppBar: true,
      appBar: PreferredSize(
        preferredSize: Size(MediaQuery.of(context).size.width, 120),
        child: Header(),
      ),
      body: BodyPage1(),
    );
  }
}

class PrimaryMenu extends StatefulWidget {
  final AnimationController controller;

  const PrimaryMenu({Key key, this.controller}) : super(key: key);
  @override
  _PrimaryMenuState createState() => _PrimaryMenuState();
}

class _PrimaryMenuState extends State<PrimaryMenu> {
  @override
  Widget build(BuildContext context) {
    return Container(
        height: MediaQuery.of(context).size.height,
        width: MediaQuery.of(context).size.width * 0.15,
        color: Colors.blue,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(
                height: 40,
                width: MediaQuery.of(context).size.width * 0.135,
                color: Colors.grey,
                child: InkWell(
                  onTap: () async {
                    print('1');
                    await widget.controller
                        .reverse()
                        .then((value) => Navigator.push(
                            context,
                            MaterialPageRoute(
                              builder: (context) => Page1(),
                            )));
                  },
                )),
            const SizedBox(height: 16),
            Container(
                height: 40,
                width: MediaQuery.of(context).size.width * 0.135,
                color: Colors.black,
                child: InkWell(
                  onTap: () async {
                    print('2');
                    await widget.controller
                        .reverse()
                        .then((value) => Navigator.push(
                            context,
                            MaterialPageRoute(
                              builder: (context) => Page2(),
                            )));
                  },
                )),
          ],
        ));
  }
}

class BodyPage1 extends StatefulWidget {
  @override
  _BodyPage1State createState() => _BodyPage1State();
}

class _BodyPage1State extends State<BodyPage1>
    with SingleTickerProviderStateMixin {
  AnimationController transitionAnimation;

  @override
  void initState() {
    super.initState();
    transitionAnimation = AnimationController(
      duration: const Duration(seconds: 3),
      vsync: this,
    );
    transitionAnimation.forward();
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        AnimatedBuilder(
            animation: transitionAnimation,
            builder: (context, child) {
              return SlideTransition(
                position: Tween<Offset>(
                        begin: const Offset(-2, 0), end: const Offset(0, 0))
                    .animate(CurvedAnimation(
                        curve:
                            const Interval(0, 0.3, curve: Curves.easeInOutBack),
                        parent: transitionAnimation)),
                child: child,
              );
            },
            child: PrimaryMenu(
              controller: transitionAnimation,
            )),
        AnimatedBuilder(
          animation: transitionAnimation,
          builder: (context, child) {
            return SlideTransition(
                position: Tween<Offset>(
                        begin: const Offset(-2, 0), end: const Offset(0, 0))
                    .animate(CurvedAnimation(
                        curve: const Interval(0.3, 1, curve: Curves.easeIn),
                        parent: transitionAnimation)),
                child: child);
          },
          child: Padding(
            padding: const EdgeInsets.only(
                top: 140, left: 20, right: 20, bottom: 20),
            child: Container(
              height: MediaQuery.of(context).size.height - 140,
              width: (MediaQuery.of(context).size.width * .85) - 40,
              color: Colors.grey,
            ),
          ),
        )
      ],
    );
  }
}

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

class _Page2State extends State<Page2> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      extendBodyBehindAppBar: true,
      appBar: PreferredSize(
        preferredSize: Size(MediaQuery.of(context).size.width, 120),
        child: Header(),
      ),
      body: BodyPage2(),
    );
  }
}

class BodyPage2 extends StatefulWidget {
  @override
  _BodyPage2State createState() => _BodyPage2State();
}

class _BodyPage2State extends State<BodyPage2>
    with SingleTickerProviderStateMixin {
  AnimationController transitionAnimation;

  @override
  void initState() {
    super.initState();
    transitionAnimation = AnimationController(
      duration: const Duration(seconds: 3),
      vsync: this,
    );
    transitionAnimation.forward();
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        AnimatedBuilder(
            animation: transitionAnimation,
            builder: (context, child) {
              return SlideTransition(
                position: Tween<Offset>(
                        begin: const Offset(-2, 0), end: const Offset(0, 0))
                    .animate(CurvedAnimation(
                        curve:
                            const Interval(0, 0.3, curve: Curves.easeInOutBack),
                        parent: transitionAnimation)),
                child: child,
              );
            },
            child: PrimaryMenu(
              controller: transitionAnimation,
            )),
        AnimatedBuilder(
          animation: transitionAnimation,
          builder: (context, child) {
            return SlideTransition(
                position: Tween<Offset>(
                        begin: const Offset(-2, 0), end: const Offset(0, 0))
                    .animate(CurvedAnimation(
                        curve: const Interval(0.3, 1, curve: Curves.easeIn),
                        parent: transitionAnimation)),
                child: child);
          },
          child: Padding(
            padding: const EdgeInsets.only(
                top: 140, left: 20, right: 20, bottom: 20),
            child: Container(
              height: MediaQuery.of(context).size.height - 140,
              width: (MediaQuery.of(context).size.width * .85) - 40,
              color: Colors.black,
            ),
          ),
        )
      ],
    );
  }
}
Related