flutter - 在 2 个不同的小部件中调用第 3 个小部件(不是父小部件)中的动画

标签 flutter dart flutter-web flutter-animation

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

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

此外,我还设置了一个 dartpad,显示我如何使用 GlobalKey 进行操作,只有在 Scaffold 上使用 FloatingActionButton 时,我才能使其正常工作 - https://dartpad.dev/5979f44ecaa9cf2e22b4ce0cc9c23aa8

抱歉,代码有点多,我已尽量精简

最佳答案

您需要做的就是将 AnimationController 从相应的 BodyPage 传递到 PrimaryMenu,它负责调用 Page1 和 Page2。一旦 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,
            ),
          ),
        )
      ],
    );
  }
}

关于flutter - 在 2 个不同的小部件中调用第 3 个小部件(不是父小部件)中的动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65237384/

相关文章:

flutter - VsCode Flutter Dart 语法颜色,无法更改

flutter - 在 Flutter 中,如何通过 checklisttile 使单个选项卡击键成为选项卡?

flutter 网络 : How to run without a device/browser?

flutter - Flutter App 添加 GetX 后上下文中未找到 GoRouter

flutter - 为什么 dart 有 get 和 set 的特殊关键字?

flutter - 导航不会使用自定义小部件更新 TabBarView 主体

dart - 如何强制 Flutter 重建/重绘所有小部件?

firebase-authentication - Flutter Web - 身份验证。热重载时用户 ID 丢失

Flutter:获取 print() 语句的行号,Android Studio

flutter - 在 Android Studio 的 flutter 中添加 auto-Const