【问题标题】:how to animate collapse elements in flutter如何在颤动中为折叠元素设置动画
【发布时间】:2018-08-08 08:23:17
【问题描述】:

当用户点击带有动画的不同小部件(兄弟或父)时,我如何展开和折叠小部件?

new Column(
    children: <Widget>[
        new header.IngridientHeader(
            new Icon(
                Icons.fiber_manual_record,
                color: AppColors.primaryColor
            ),
            'Voice Track 1'
        ),
        new Grid()
    ],
)

我希望用户能够点击 header.IngridientHeader 然后 Grid 小部件应该切换(如果可见则隐藏或其他方式)

编辑:

我正在尝试做一些在引导程序中称为 Collapse 的事情。 getbootstrap.com/docs/4.0/components/collapse

编辑 2: header.IngridientHeader 应始终保持原位 Grid() 是可滚动的(水平)小部件。

【问题讨论】:

标签: flutter dart flutter-layout


【解决方案1】:

如果您想将一个小部件折叠到零高度或零宽度,并且折叠时有一个子项溢出,我建议您使用SizeTransitionScaleTransition

这是一个 ScaleTransition 小部件的示例,该小部件用于折叠四个黑色按钮和状态文本的容器。我的 ExpandedSection 小部件与列一起使用以获得以下结构。

通过 SizeTransition 小部件使用动画的小部件示例:

class ExpandedSection extends StatefulWidget {

  final Widget child;
  final bool expand;
  ExpandedSection({this.expand = false, this.child});

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

class _ExpandedSectionState extends State<ExpandedSection> with SingleTickerProviderStateMixin {
  AnimationController expandController;
  Animation<double> animation; 

  @override
  void initState() {
    super.initState();
    prepareAnimations();
    _runExpandCheck();
  }

  ///Setting up the animation
  void prepareAnimations() {
    expandController = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 500)
    );
    animation = CurvedAnimation(
      parent: expandController,
      curve: Curves.fastOutSlowIn,
    );
  }

  void _runExpandCheck() {
    if(widget.expand) {
      expandController.forward();
    }
    else {
      expandController.reverse();
    }
  }

  @override
  void didUpdateWidget(ExpandedSection oldWidget) {
    super.didUpdateWidget(oldWidget);
    _runExpandCheck();
  }

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

  @override
  Widget build(BuildContext context) {
    return SizeTransition(
      axisAlignment: 1.0,
      sizeFactor: animation,
      child: widget.child
    );
  }
}

AnimatedContainer 也可以,但如果子级不能调整到零宽度或零高度,Flutter 会抱怨溢出。

【讨论】:

  • 您好,请您提供您用于创建动画的代码。在我的情况下,大小转换正在工作,但是当大小为 0 时它会占用空间。
  • 我使用上面创建的小部件制作了一个类似于 gif 动画的working app。很遗憾,我无法分享上述应用程序的具体代码,因为我不拥有该项目。
  • 非常感谢。对于要在开始时扩展的小部件,我必须在 initState() 中使用 forwardreverse 逻辑以及 didWidgetUpdate()
  • 您不需要addListener()setState,因为SizeTransition 会自动执行此操作。
  • 我的项目需要这个,所以修改了这段代码来制作一个 ExpandableSection 小部件。您可以自定义动画持续时间和曲线。警告:它需要使用 flutter_hooks。 gist.github.com/venkatd/b820b3b34b09239d85bf51ccb7f2278a
【解决方案2】:

或者,您也可以使用 AnimatedContainer 来模仿这种行为。

class AnimateContentExample extends StatefulWidget {
  @override
  _AnimateContentExampleState createState() => new _AnimateContentExampleState();
}

class _AnimateContentExampleState extends State<AnimateContentExample> {
  double _animatedHeight = 100.0;
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(title: new Text("Animate Content"),),
      body: new Column(
        children: <Widget>[
          new Card(
            child: new Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                new GestureDetector(
                  onTap: ()=>setState((){
                    _animatedHeight!=0.0?_animatedHeight=0.0:_animatedHeight=100.0;}),
                  child:  new Container(
                  child: new Text("CLICK ME"),
                  color: Colors.blueAccent,
                  height: 25.0,
                    width: 100.0,
                ),),
                new AnimatedContainer(duration: const Duration(milliseconds: 120),
                  child: new Text("Toggle Me"),
                  height: _animatedHeight,
                  color: Colors.tealAccent,
                  width: 100.0,
                )
              ],
            ) ,
          )
        ],
      ),
    );
  }
}

【讨论】:

  • 你在这里使用 StatefulWidget 有什么原因吗?
  • 是的,setState 不会工作,除非它是 StatefulWidget
  • AnimatedContainer 不适用于儿童身高不固定的情况。另一方面,SizeTransition 在这种情况下就像一个魅力。
  • 这种方法的问题是你必须为关闭(0)和打开提供一个高度。它不会像普通容器那样适应内容。
  • Flutter 2.* 版本:gist.github.com/AdamJonsson/…
【解决方案3】:

我认为您正在寻找ExpansionTile 小部件。这需要一个title 属性,它相当于标题和children 属性,您可以将小部件传递给它以在切换时显示或隐藏。 你可以找到一个如何使用它的例子here

简单示例用法:

new ExpansionTile(title: new Text("Numbers"),
      children: <Widget>[
        new Text("Number: 1"),
        new Text("Number: 2"),
        new Text("Number: 3"),
        new Text("Number: 4"),
        new Text("Number: 5")
      ],
),

希望有帮助!

【讨论】:

  • 如果不是因为我的 header.IngridientHeader (代码中的 new Text("Numbers") )必须保持原位并且 Grid (代码中的子项)是可滚动的小部件,那么您的答案将是完美的.很抱歉没有早点指出这一点
  • 我认为这也是如此。只需为孩子添加一个可滚动的孩子。那是你想要的吗。或者你能否指出一些关于你想要达到的目标的视觉参考。
【解决方案4】:

输出:


代码:

class FooPageState extends State<SoPage> {
  static const _duration = Duration(seconds: 1);
  int _flex1 = 1, _flex2 = 2, _flex3 = 1;

  @override
  Widget build(BuildContext context) {
    final total = _flex1 + _flex2 + _flex3;
    final height = MediaQuery.of(context).size.height;
    final height1 = (height * _flex1) / total;
    final height2 = (height * _flex2) / total;
    final height3 = (height * _flex3) / total;

    return Scaffold(
      body: Column(
        children: [
          AnimatedContainer(
            height: height1,
            duration: _duration,
            color: Colors.red,
          ),
          AnimatedContainer(
            height: height2,
            duration: _duration,
            color: Colors.green,
          ),
          AnimatedContainer(
            height: height3,
            duration: _duration,
            color: Colors.blue,
          ),
        ],
      ),
    );
  }
}

【讨论】:

    【解决方案5】:

    感谢@Adam Jonsson,他的回答解决了我的问题。这是关于如何使用ExpandedSection的演示,希望对您有所帮助。

    class ExpandedSection extends StatefulWidget {
      final Widget child;
      final bool expand;
    
      ExpandedSection({this.expand = false, this.child});
    
      @override
      _ExpandedSectionState createState() => _ExpandedSectionState();
    }
    
    class _ExpandedSectionState extends State<ExpandedSection>
        with SingleTickerProviderStateMixin {
      AnimationController expandController;
      Animation<double> animation;
    
      @override
      void initState() {
        super.initState();
        prepareAnimations();
        _runExpandCheck();
      }
    
      ///Setting up the animation
      void prepareAnimations() {
        expandController =
            AnimationController(vsync: this, duration: Duration(milliseconds: 500));
        animation = CurvedAnimation(
          parent: expandController,
          curve: Curves.fastOutSlowIn,
        );
      }
    
      void _runExpandCheck() {
        if (widget.expand) {
          expandController.forward();
        } else {
          expandController.reverse();
        }
      }
    
      @override
      void didUpdateWidget(ExpandedSection oldWidget) {
        super.didUpdateWidget(oldWidget);
        _runExpandCheck();
      }
    
      @override
      void dispose() {
        expandController.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return SizeTransition(
            axisAlignment: 1.0, sizeFactor: animation, child: widget.child);
      }
    }
      
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          home: Scaffold(
            appBar: AppBar(
              title: Text('Demo'),
            ),
            body: Home(),
          ),
        );
      }
    }
    
    class Home extends StatefulWidget {
      @override
      _HomeState createState() => _HomeState();
    }
    
    class _HomeState extends State<Home> {
      bool _expand = false;
    
      @override
      Widget build(BuildContext context) {
        return Column(
          children: [
            Header(
              onTap: () {
                setState(() {
                  _expand = !_expand;
                });
              },
            ),
            ExpandedSection(child: Content(), expand: _expand,)
          ],
        );
      }
    }
    
    class Header extends StatelessWidget {
      final VoidCallback onTap;
    
      Header({@required this.onTap});
    
      @override
      Widget build(BuildContext context) {
        return GestureDetector(
          onTap: onTap,
          child: Container(
            color: Colors.cyan,
            height: 100,
            width: double.infinity,
            child: Center(
              child: Text(
                'Header -- Tap me to expand!',
                style: TextStyle(color: Colors.white, fontSize: 20),
              ),
            ),
          ),
        );
      }
    }
    
    class Content extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Container(
          color: Colors.lightGreen,
          height: 400,
        );
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2021-03-04
      • 2022-08-18
      • 1970-01-01
      • 1970-01-01
      • 2022-01-13
      • 1970-01-01
      • 2020-03-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多