如何为列表中非选定项目创建退出动画?

时间:2019-08-11 03:00:34

标签: flutter flutter-animation

我正在使用ListView.builder构建按钮的ListView。点击其中一个按钮时,我需要所有其他按钮淡出视图,然后重建ListView以仅包含被点击的按钮。

我已经进行了此设置来重建ListView,使其仅包含被点击的按钮,但是所有其他按钮都消失了。我该如何使它们全部消失?

如果我将每个包装在FadeTransition中,然后调用_animationController.forward(),它将淡出所有按钮,包括被点击的按钮。我还考虑过为每个按钮创建一个单独的控制器,并且只在未点击的按钮上调用.forward(),但这在简便性和性能方面是否是正确的方法?

到目前为止,这是我的代码:

import 'package:baseball_bluebook_mobile/search_page/nav_button_model.dart';
import 'package:baseball_bluebook_mobile/search_page/recent_button_model.dart';
import 'package:baseball_bluebook_mobile/search_page/search_page_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
import 'package:provider/provider.dart';

import '../colors.dart';
import 'index_picker_button_model.dart';
import 'link_button_model.dart';
import 'nav_button_type.dart';

class Navigation extends StatefulWidget {
  @override
  _NavigationState createState() => _NavigationState();
}

class _NavigationState extends State<Navigation>
    with SingleTickerProviderStateMixin {
  AnimationController _animationController;
  Animation _animation;
  List<NavButtonModel> previousNavButtons = [];

  @override
  void initState() {
    super.initState();

    _animationController = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 1000),
    );

    _animation = Tween(begin: 0.0, end: 1.0).animate(_animationController);
  }

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

  @override
  Widget build(BuildContext context) {
    return Consumer<SearchPageModel>(
      builder: (context, model, child) {
        return Card(
          margin: EdgeInsets.zero,
          shape: ContinuousRectangleBorder(),
          elevation: 4,
          // If we don't have the navButtons from the database yet, show a loading indicator
          child: model.navButtons.isEmpty
              ? _getNavigationLoadingIndicator(model)
              : _getNavigationContent(model),
        );
      },
    );
  }

  Widget _getNavigationLoadingIndicator(SearchPageModel model) {
    return Container(
      height: 48,
      padding: EdgeInsets.only(top: 46),
      child: LinearProgressIndicator(
        semanticsLabel: 'Search navigation loading',
        valueColor: AlwaysStoppedAnimation(CustomColors.blue),
      ),
    );
  }

  Widget _getNavigationContent(SearchPageModel model) {
    _animationController.forward();
    return Container(
      height: 48,
      padding: EdgeInsets.symmetric(vertical: 8, horizontal: 0),
      child: ListView.builder(
        scrollDirection: Axis.horizontal,
        itemCount: model.navButtons.length,
        itemBuilder: (context, index) {
          Widget navButtonWidget;
          // If this is the first button in the list, add left and right padding, otherwise, only add right padding
          if (index == 0) {
            navButtonWidget = Padding(
              padding: EdgeInsets.only(left: 8, right: 8),
              child: _getNavButton(index, model),
            );
          } else {
            navButtonWidget = Padding(
              padding: EdgeInsets.only(right: 8),
              child: _getNavButton(index, model),
            );
          }

          return navButtonWidget;
        },
      ),
    );
  }

  Widget _getNavButton(int index, SearchPageModel model) {
    Widget navButton;

    switch (model.navButtons[index].type) {
      case NavButtonType.recent:
        navButton = _getRecentButton(index, model);
        break;
      case NavButtonType.indexPicker:
        navButton = _getIndexPickerButton(index, model);
        break;
      case NavButtonType.link:
        navButton = _getLinkButton(index, model);
        break;
    }

    if (navButton != null) {
      return FadeTransition(
        opacity: _animationController,
        child: navButton,
      );
    } else {
      return Container();
    }
  }

  Widget _getRecentButton(int index, SearchPageModel model) {
    final recentButtonModel = model.navButtons[index];
    return FlatButton(
      padding: EdgeInsets.symmetric(horizontal: 8),
      color: recentButtonModel.isActive
          ? CustomColors.blue
          : CustomColors.lightGray,
      textColor: Colors.white,
      onPressed: () async {},
      child: Text(recentButtonModel.label),
    );
  }

  Widget _getIndexPickerButton(int index, SearchPageModel model) {
    IndexPickerButtonModel indexPickerButtonModel = model.navButtons[index];
    return FlatButton(
      padding: EdgeInsets.symmetric(horizontal: 8),
      color: indexPickerButtonModel.isActive
          ? CustomColors.blue
          : CustomColors.lightGray,
      textColor: Colors.white,
      onPressed: () async {
        model.setNavigationButtonActiveStatus(
          index,
          !indexPickerButtonModel.isActive,
        );
      },
      child: Text(indexPickerButtonModel.label),
    );
  }

  Widget _getLinkButton(int index, SearchPageModel model) {
    LinkButtonModel linkButtonModel = model.navButtons[index];
    return FlatButton(
      padding: EdgeInsets.symmetric(horizontal: 8),
      color:
          linkButtonModel.isActive ? CustomColors.blue : CustomColors.lightGray,
      textColor: Colors.white,
      onPressed: () async {
        await launch(
          linkButtonModel.url,
          option: new CustomTabsOption(
            toolbarColor: CustomColors.blue,
            enableUrlBarHiding: true,
            showPageTitle: true,
            animation: new CustomTabsAnimation.slideIn(),
            extraCustomTabs: <String>[
              'org.mozilla.firefox',
              'com.microsoft.emmx'
            ],
          ),
        );
      },
      child: Text(linkButtonModel.label),
    );
  }
}

1 个答案:

答案 0 :(得分:0)

我没有使用FadeTransition,而是使用纯Animation来实现它。

首先,每个按钮都需要知道它代表什么索引。

     onPressed: () async {
        _clickedBtn = index;
        await launch(
          linkButtonModel.url,
          option: new CustomTabsOption(
            toolbarColor: CustomColors.blue,
            enableUrlBarHiding: true,
            showPageTitle: true,
            animation: new CustomTabsAnimation.slideIn(),
            extraCustomTabs: <String>[
              'org.mozilla.firefox',
              'com.microsoft.emmx'
            ],
          ),
        );
      },

第二,使用Animation控制淡入淡出动画。

  Widget _getNavButton(int index, SearchPageModel model) {
    Widget navButton;

    switch (model.navButtons[index].type) {
      case NavButtonType.recent:
        navButton = _getRecentButton(index, model);
        break;
      case NavButtonType.indexPicker:
        navButton = _getIndexPickerButton(index, model);
        break;
      case NavButtonType.link:
        navButton = _getLinkButton(index, model);
        break;
    }

    if (navButton != null) {
      return AnimatedBuilder(
            animation: _animation,
            builder: (context, child) => Opacity(
                  opacity: _clickedBtn == index ? 1.0 : _animation.value, // 1 to 0
                  child: navButton ,
                ),
            child: navButton ),
    } else {
      return Container();
    }
  }

就是这样。我没有测试上面的代码,因为这些代码具有相当的依赖性和复杂性。希望对您有帮助。