在显示和隐藏窗口小部件中的动画时,我在哪里出错?

时间:2020-08-29 21:56:40

标签: flutter dart

我创建了一个分为两个gridviews的列表,因为我只希望显示前3个项目,并带有一个按钮以便以后显示更多项目。我不知道这样做是否正确,但对我有用。

但是显示和隐藏项目的方法非常激进,因此我设法使动画出现,但我无法使其消失。然后q第一次出现,即使我隐藏并使动画再次出现,它也不再起作用。

我想知道如何使该动画正常工作,以及我是否以这种方式走上正轨,或者是否应该与众不同?

此gif显示了当前的动画效果。

gif de test

我将代码放在dartpad中以便于查看:

https://dartpad.dev/526a5719cae7d8be0772f1e87ef02ced

以及下面的完整代码:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark(),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Test'),
        ),
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

class MyWidget extends StatefulWidget {
  @override
  _MyWidget createState() => _MyWidget();
}

class _MyWidget extends State<MyWidget> with SingleTickerProviderStateMixin {
  AnimationController _animationController;
  Animation _animation;

  @override
  void initState() {
    _animationController = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 800),
    );
    _animation = Tween(
      begin: 0.0,
      end: 1.0,
    ).animate(_animationController);
    super.initState();
  }

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

  var _isExpanded = false;

  _toggleExpanded() {
    _animationController.forward();
    setState(() {
      _isExpanded = !_isExpanded;
    });
  }

  @override
  Widget build(BuildContext context) {

    return SingleChildScrollView(
      child: Column(children: <Widget>[
        GridView.builder(
          physics: NeverScrollableScrollPhysics(),
          shrinkWrap: true,
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 3,
            childAspectRatio: 1.0,
            mainAxisSpacing: 0.0,
            crossAxisSpacing: 0.0,
          ),
          itemCount: 3,
          itemBuilder: (context, index) {
            return ItemCategory(
              title: "Teste $index",
              icon: Icons.apps,
              coloritem: Colors.deepPurple,
              onpressbtn: "teste $index",
            );
          },
        ),
        Visibility(
          visible: _isExpanded,
          child: FadeTransition(
            opacity: _animation,
            child: GridView.builder(
              physics: NeverScrollableScrollPhysics(),
              shrinkWrap: true,
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 3,
                childAspectRatio: 1.0,
                mainAxisSpacing: 0.0,
                crossAxisSpacing: 0.0,
              ),
              itemCount: 5,
              itemBuilder: (context, index) {
                var indx = index+3;
                return ItemCategory(
                  title: "Teste $indx",
                  icon: Icons.apps,
                  coloritem: Colors.deepPurple,
                  onpressbtn: "teste $index",
                );
              },
            ),
          ),
        ),
        IconButton(
          icon: _isExpanded
              ? Icon(Icons.keyboard_arrow_up)
              : Icon(Icons.keyboard_arrow_down),
          tooltip: 'Open More options',
          onPressed: _toggleExpanded,
        ),
      ]),
    );
  }
}

// These are sample items to be displayed in the gridview.
class ItemCategory extends StatelessWidget {
  ItemCategory({this.title, this.icon, this.coloritem, this.onpressbtn});

  final String title;
  final IconData icon;
  final MaterialColor coloritem;
  final String onpressbtn;

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: EdgeInsets.all(8.5),
      child: InkWell(
        onTap: () {},
        splashColor: Colors.amber,
        child: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              Icon(
                icon,
                size: 70.0,
                color: coloritem,
              ),
              Text(title, style: new TextStyle(fontSize: 17.0))
            ],
          ),
        ),
      ),
    );
  }
}

2 个答案:

答案 0 :(得分:2)

_animationController.reset()添加到_toggleExpanded()并将Future.delayed设置为_isExpanded时使用false

_toggleExpanded() {
    if (_isExpanded) {
      _animationController.reverse();
       Future.delayed(Duration(milliseconds: 800), () => setState(() {
        _isExpanded = false;
       }));
    }
    else {
      _animationController.reset();
      _animationController.forward();
      setState(() {
        _isExpanded = true;
       });
    }
  }

答案 1 :(得分:1)

正向动画第一次工作是因为它从0开始并变为1。在随后的时间,它仍为1,因此启动控制器将使其立即停止,因为它从结尾开始值。您可以通过调用controller.reset()或将起始值传递到forward(即controller.forward(from: 0))来解决此问题。

要使反向动画正常工作,还需要有一个调用controller.reverse()的方法。这将导致动画反向播放,因此您需要将动画设置为0。像forward一样,您还需要告诉控制器从哪里开始使用controller.reverse(from: 1)进行动画制作。

但是,您将遇到另一个问题。如果仅在调用_isExpanded的同时将reverse设置为false,则将播放淡入淡出的动画,但可折叠面板将关闭,从而无法看到它。而不是根据按钮的按下来设置面板的可见性,而应基于动画的当前状态。通过听控制器的动画状态更改,如果动画值不是0,则可以使面板可见。

以下是建议采取的措施:

class _MyWidget extends State<MyWidget> with SingleTickerProviderStateMixin {
  AnimationStatus _currentStatus = AnimationStatus.dismissed;
  ...

  @override
  void initState() {
    _animationController = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 800),
    )..addStatusListener((status) {
      setState(() => _currentStatus = status);
    });
    ...
  }

  ...

  _toggleExpand() {
    _animationController.forward(from: 0);
    setState(() {
      _isExpanded = true;
    });
  }
  
  _toggleCollapse() {
    _animationController.reverse(from: 1);
    _isExpanded = false;
  }

  ...

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Column(children: <Widget>[
        ...
        Visibility(
          visible: _currentStatus != AnimationStatus.dismissed,
          ...
        ),
        IconButton(
          ...
          onPressed: _isExpanded ? _toggleCollapse : _toggleExpand,
        ),
      ]),
    );
  }
}