【问题标题】:Flutter AnimatedSize works in one direction onlyFlutter AnimatedSize 仅在一个方向上起作用
【发布时间】:2020-03-26 17:05:51
【问题描述】:

我从https://medium.com/flutter-community/flutter-working-with-animatedsize-35253ff8f16a得到以下代码

它使用了 AnimatedSize,但动画只在容器扩展时起作用,而不是在它推卸时起作用。这是默认行为吗?我想在扩展和收缩的同时制作动画。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  double _height = 80.0;
  double _width = 80.0;
  var _color = Colors.blue;
  bool _resized = false;
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            AnimatedSize(
              curve: Curves.easeIn,
              vsync: this,
              duration: new Duration(seconds: 1),
              child: new GestureDetector(
                onTap: () {
                  setState(() {
                    if (_resized) {
                      _resized = false;
                      _color = Colors.blue;
                      _height = 80.0;
                      _width = 80.0;
                    } else {
                      _resized = true;
                      _color = Colors.blue;
                      _height = 320.0;
                      _width = 320.0;
                    }
                  });
                },
                child: new Container(
                  width: _width,
                  height: _height,
                  color: _color,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

【问题讨论】:

  • 我认为这是一个错误

标签: flutter flutter-animation


【解决方案1】:

AnimatedContainer 在使用文本、列表或其他可变大小的小部件时并不是一个真正的选择,因为它不允许宽度或高度为空。它也不会让你指定对齐方式。

相反,我使用带有动画控制器的 ClipRect 解决了这个问题。在下面的动画中,您可以在收盘反弹中看到中心线左侧的单词“laboris”。这表明文本在容器关闭期间居中,请注意“laboris”。

我创建了一个 AnimatedClipRect 小部件类,您可以轻松地自己实现它。您可以指定对齐方式、曲线、持续时间以及是否需要水平和/或垂直动画。现在它假设您只想完全关闭或打开它:

class AnimatedClipRect extends StatefulWidget {
  @override
  _AnimatedClipRectState createState() => _AnimatedClipRectState();

  final Widget child;
  final bool open;
  final bool horizontalAnimation;
  final bool verticalAnimation;
  final Alignment alignment;
  final Duration duration;
  final Duration? reverseDuration;
  final Curve curve;
  final Curve? reverseCurve;

  ///The behavior of the controller when [AccessibilityFeatures.disableAnimations] is true.
  final AnimationBehavior animationBehavior;

  const AnimatedClipRect({
    Key? key,
    required this.child,
    required this.open,
    this.horizontalAnimation = true,
    this.verticalAnimation = true,
    this.alignment = Alignment.center,
    this.duration = const Duration(milliseconds: 500),
    this.reverseDuration,
    this.curve = Curves.linear,
    this.reverseCurve,
    this.animationBehavior = AnimationBehavior.normal,
  }) : super(key: key);
}

class _AnimatedClipRectState extends State<AnimatedClipRect> with TickerProviderStateMixin {
  late AnimationController _animationController;
  late Animation _animation;

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

  @override
  void initState() {
    _animationController = AnimationController(
        duration: widget.duration,
        reverseDuration: widget.reverseDuration ?? widget.duration,
        vsync: this,
        value: widget.open ? 1.0 : 0.0,
        animationBehavior: widget.animationBehavior);
    _animation = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
      parent: _animationController,
      curve: widget.curve,
      reverseCurve: widget.reverseCurve ?? widget.curve,
    ));
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    widget.open ? _animationController.forward() : _animationController.reverse();

    return ClipRect(
      child: AnimatedBuilder(
        animation: _animationController,
        builder: (_, child) {
          return Align(
            alignment: widget.alignment,
            heightFactor: widget.verticalAnimation ? _animation.value : 1.0,
            widthFactor: widget.horizontalAnimation ? _animation.value : 1.0,
            child: child,
          );
        },
        child: widget.child,
      ),
    );
  }
}

基本用法示例,将您想要动画的任何内容放在其子项中:

// declare bool _open somewhere

Column(
  children: <Widget>[
    AnimatedClipRect(
      open: _open,
      horizontalAnimation: false,
      verticalAnimation: true,
      alignment: Alignment.center,
      duration: const Duration(milliseconds: 1000),
      curve: Curves.bounceOut,
      reverseCurve: Curves.bounceIn,
      child: Container(
        color: Colors.lightGreenAccent,
        padding: const EdgeInsets.all(8),
        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. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'),
      ),
    ),
    ElevatedButton(
        child: const Text("open/close"),
        onPressed: () {
          setState(() => _open ^= true);
        }),
  ],
)

如您所见,您只需更改 _open 并执行 setState((){}) 即可触发动画。

【讨论】:

  • 谢谢。工作完美。我真的很喜欢动画控制器隐藏在小部件中 - 因此只需更改 open:boolean 值即可控制滑动动画。赞一个!
  • 别忘了处理你的控制器
  • 谢谢,这些配置太容易忘记了。我还更新了代码以使用 null 安全性,并在这里和那里添加了更多的 const。
【解决方案2】:

AnimatedContainer 是你要找的,不需要AnimatedSize

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

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

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  double _height = 80.0;
  double _width = 80.0;
  var _color = Colors.blue;
  bool _resized = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            GestureDetector(
              onTap: () {
                setState(() {
                  if (_resized) {
                    _resized = false;
                    _color = Colors.blue;
                    _height = 80.0;
                    _width = 80.0;
                  } else {
                    _resized = true;
                    _color = Colors.blue;
                    _height = 320.0;
                    _width = 320.0;
                  }
                });
              },
              child: AnimatedContainer(
                duration: Duration(seconds: 1),
                width: _width,
                height: _height,
                color: _color,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

或者如果您需要AnimatedSize,您可以添加一个新容器,其颜色为AnimatedSize 父级。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

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

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  double _height = 320.0;
  double _width = 320.0;
  var _color = Colors.blue;
  bool _resized = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: GestureDetector(
          onTap: () {
            setState(() {
              if (_resized) {
                _resized = false;
                _color = Colors.blue;
                _height = 80.0;
                _width = 80.0;
              } else {
                _resized = true;
                _color = Colors.blue;
                _height = 320.0;
                _width = 320.0;
              }
            });
          },
          child: Container(
            color: _color,
            child: AnimatedSize(
              curve: Curves.easeIn,
              vsync: this,
              duration: Duration(seconds: 1),
              child: Container(
                width: _width,
                height: _height,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

【讨论】:

    【解决方案3】:

    Animated Size 给了我和你一样的问题,但后来我查看了 AnimatedCrossFade 的源代码,它使用了 Animated Size 并且它在收缩和扩展时都设置了动画大小。使用 LayoutBuilder 作为 AnimatedSize 的子级,并将实际的子级放在 LayoutBuilder 中。我不确定它为什么会起作用,但 AnimatedCrossFade 就是这样。

    【讨论】:

    • 对我不起作用!!!!!!
    【解决方案4】:

    小部件SizeTransition 为我工作。当孩子的高度或宽度也为null 时,它也有效。

    这是我的示例代码:

    import 'package:flutter/material.dart';
    
    void main() => runApp(const MyApp());
    
    class MyApp extends StatelessWidget {
      const MyApp({Key? key}) : super(key: key);
    
      static const String _title = 'Flutter Code Sample';
    
      @override
      Widget build(BuildContext context) {
        return const MaterialApp(
          title: _title,
          home: MyStatefulWidget(),
        );
      }
    }
    
    class MyStatefulWidget extends StatefulWidget {
      const MyStatefulWidget({Key? key}) : super(key: key);
    
      @override
      State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
    }
    
    class _MyStatefulWidgetState extends State<MyStatefulWidget>
        with TickerProviderStateMixin {
      late final AnimationController _controller = AnimationController(
        duration: const Duration(seconds: 1),
        vsync: this,
      );
      late final Animation<double> _animation = CurvedAnimation(
        parent: _controller,
        curve: Curves.fastOutSlowIn,
      );
    
      @override
      void dispose() {
        _controller.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              SizeTransition(
                sizeFactor: _animation,
                axis: Axis.vertical,
                axisAlignment: 1,
                child: Container(
                  color: Colors.amberAccent,
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.stretch,
                    children: const [Text("Hello"), Text("World")],
                  ),
                ),
              ),
              TextButton(
                child: const Text('toggle'),
                onPressed: () => _controller.isDismissed
                    ? _controller.forward()
                    : _controller.reverse(),
              ),
            ],
          ),
        );
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-30
      • 2020-03-21
      • 2022-01-14
      相关资源
      最近更新 更多