5

我想改变一些小部件的孩子,然后看到它动画到新孩子的高度,也有一个淡入淡出过渡。

我可以用 来做到这一点AnimatedCrossFade,但是我必须同时保留firstChildsecondChild,这是我做不到的。

如果我使用AnimatedSwitcher,它可以让我简单地改变它的孩子,但它只会动画淡入淡出,而不是大小。

AnimatedContainer也不起作用,因为我事先不知道孩子的大小。

是否有一些我缺少的小部件可以满足我的需要,如果没有,我该如何在不诉诸AnimationControllers 的情况下做到这一点?

4

2 回答 2

7

这解决了这个问题:

https://pub.dev/packages/animated_size_and_fade

它同时淡化和动画大小,无需指定两个孩子。您还可以分别为淡入淡出和大小定义持续时间和曲线。

像这样使用它:

bool toggle=true; 
Widget widget1 = ...;
Widget widget2 = ...;

AnimatedSizeAndFade(
    vsync: this,
    child: toggle ? widget1 : widget2,
),

注意:如果你想使用上面的代码,请阅读文档:

  • “旧”和“新”子级必须具有相同的宽度,但可以具有不同的高度。

  • 如果“新”子项与“旧”子项的小部件类型相同,但参数不同,则AnimatedSizeAndFade不会在它们之间进行转换,因为就框架而言,它们是相同的小部件,并且现有的小部件可以使用新参数进行更新。要强制转换发生,请设置一个Key(通常是一个ValueKey获取任何小部件数据,这些数据会改变您希望被视为唯一的每个子小部件上小部件的视觉外观。


这是一个可运行的示例:

import 'package:flutter/material.dart';
import 'package:widgets/widgets.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
  bool toggle;

  @override
  void initState() {
    super.initState();
    toggle = false;
  }

  @override
  Widget build(BuildContext context) {
    var toggleButton = Padding(
      padding: const EdgeInsets.all(8.0),
      child: MaterialButton(
        child: const Text("Toggle"),
        color: Colors.grey,
        onPressed: () {
          setState(() {
            toggle = !toggle;
          });
        },
      ),
    );

    var widget1 = Container(
      key: ValueKey("first"),
      color: Colors.blue,
      width: 200.0,
      child: const Text(
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt "
            "ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation "
            "ullamco laboris nisi ut aliquip ex ea commodo consequat.",
      ),
    );

    var widget2 = Container(
      key: ValueKey("second"),
      color: Colors.red,
      width: 200.0,
      child: const Text(
        "I am ready for my closeup.",
      ),
    );

    return MaterialApp(
      home: Material(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Container(height: 100.0),
            toggleButton,
            Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text("Some text above."),
                AnimatedSizeAndFade(
                  vsync: this,
                  child: toggle ? widget1 : widget2,
                  fadeDuration: const Duration(milliseconds: 300),
                  sizeDuration: const Duration(milliseconds: 600),
                ),
                const Text("Some text below."),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
于 2018-08-08T02:15:35.383 回答
2

有很多方法可以实现这一目标。这只是一个例子:

  class LogoApp extends StatefulWidget {
    _LogoAppState createState() => _LogoAppState();
  }

  class _LogoAppState extends State<LogoApp> with TickerProviderStateMixin {
    Animation animation;
    Animation animationOpacity;
    AnimationController controller;

    initState() {
      super.initState();
      controller = AnimationController(
          duration: const Duration(milliseconds: 2000), vsync: this);
      final CurvedAnimation curve =
          CurvedAnimation(parent: controller, curve: Curves.easeIn);
      animation = Tween(begin: 0.0, end: 300.0).animate(curve);
      animationOpacity = Tween(begin: 0.0, end: 1.0).animate(curve);
      controller.forward();
    }

    Widget build(BuildContext context) {
      return AnimatedBuilder(
        animation: controller,
        builder: (context, widget) {
          return Opacity(
            opacity: animationOpacity.value,
                    child: Container(
              margin: EdgeInsets.symmetric(vertical: 10.0),
              height: animation.value,
              width: animation.value,
              child: FlutterLogo(),
            ),
          );
        },
      );
    }

    dispose() {
      controller.dispose();
      super.dispose();
    }
  }

用法:

   @override
    Widget build(BuildContext context) {
      return new MaterialApp(
          title: 'Flutter Demo',
          theme: new ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: Material(child: Center(child: LogoApp())));
    }

请参阅本文档 Flutter Animations

更新

  class LogoApp extends StatefulWidget {
    _LogoAppState createState() => _LogoAppState();
  }

  class _LogoAppState extends State<LogoApp> with TickerProviderStateMixin {
    Animation controllerAnimation;
    AnimationController controller;

    initState() {
      super.initState();
      controller = AnimationController(
          duration: const Duration(milliseconds: 1000), vsync: this);
      final CurvedAnimation curve =
          CurvedAnimation(parent: controller, curve: Curves.easeIn);
      controllerAnimation = Tween(begin: 0.0, end: 1.0).animate(curve);
      controller.forward();
    }

    bool isSelected = false;

    Widget build(BuildContext context) {
      return Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          AnimatedSwitcher(
            duration: Duration(seconds: 10),//it is ignored
            child: isSelected
                ? Container(
                    width: 200.0,
                    height: 200.0,
                    child: FlutterLogo(
                      colors: Colors.red,
                    ),
                  )
                : Container(
                    width: 200.0,
                    height: 200.0,
                    child: FlutterLogo(
                      colors: Colors.blue,
                    )),
            transitionBuilder: defaultTransitionBuilder,
          ),
          MaterialButton(
            child: Text("Texting"),
            onPressed: () {
              if (controller.isCompleted) {
                controller.reset();
              }

              controller.forward();

              setState(() {
                isSelected = !isSelected;
              });
            },
          )
        ],
      );
    }

    Widget defaultTransitionBuilder(Widget child, Animation<double> animation) {
      return AnimatedBuilder(
        animation: controller,
        builder: (context, widget) {
          return Opacity(
            opacity: controllerAnimation.value,
            child: ScaleTransition(
              scale: controllerAnimation,
              child: widget,
            ),
          );
        },
        child: child,
      );
    }

    dispose() {
      controller.dispose();
      super.dispose();
    }
  }
于 2018-08-07T23:37:50.297 回答