从第三个小部件(不是父小部件)调用两个不同小部件中的动画

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

我有 2 个页面(Page1Page2)为了在它们之间导航,我有一个自定义 PrimaryMenu 小部件,两个页面都相同。 PrimaryMenu 分别包含在 BodyPage1BodyPage2 中。我还有一个自定义 Header 小部件。两个页面和页眉都有动画。

我想做的是按 InkWell 小部件之一,然后动画在当前页面上反转,然后调用新页面。我知道如何调用新页面,我对如何使用 GlobalKey 有一个粗略的了解,但我开始认为这不能用 GlobalKey 来完成。下面我将展示我拥有的各个小部件:dartpad link 以防万一 - 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,
            ),
          ),
        )
      ],
    );
  }
}

我还设置了一个飞镖板,显示我用 GlobalKey 做了什么,如果我在 Scaffold - [=29= 上使用 FloatingActionButton,我只能让它工作]

对不起,代码有点多,我尽量压缩了

您需要做的就是将 AnimationController 从相应的 BodyPage 传递到负责调用 Page1 和 Page2 的 PrimaryMenu。一旦 AnimationController 可用于 PrimaryMenu,它将首先反转动画,然后调用相应的页面。请参阅下面的工作代码:

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,
            ),
          ),
        )
      ],
    );
  }
}