带有IndexedStack的AnimatedSwitcher

时间:2019-08-28 14:01:36

标签: flutter flutter-layout flutter-animation

我必须使用IndexedStack来维护BottomNavigationBar的小部件状态。 现在,我想在切换选项卡时使用AnimatedSwitcher(或替代方法)创建动画。 我遇到了使AnimatedSwitcher在IndexedStack更改时触发的问题。我将IndexedStack作为AnimatedSwitcher的子级,这显然会导致AnimatedSwitcher不会触发,因为IndexedStack小部件没有变化,只是它的子级。

body: AnimatedSwitcher(  
  duration: Duration(milliseconds: 200),  
  child: IndexedStack(  
    children: _tabs.map((t) => t.widget).toList(),  
    index: _currentIndex,  
  ),  
)

有什么办法解决这个问题?通过手动触发AnimatedSwitcher还是通过使用其他方法来创建动画? 我还尝试过更改密钥,但这显然导致每次创建新状态时都会创建一个新的IndexedStack,因此选项卡的状态也会丢失。

4 个答案:

答案 0 :(得分:1)

如果您需要使用IndexedStack。

您可以添加自定义动画并在更改选项卡上触发它,如下所示: enter image description here

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(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  final List<Widget> myTabs = [
    Tab(text: 'one'),
    Tab(text: 'two'),
    Tab(text: 'three'),
  ];

  AnimationController _animationController;
  TabController _tabController;
  int _tabIndex = 0;
  Animation animation;

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

  @override
  void initState() {
    _tabController = TabController(length: 3, vsync: this);
    _animationController = AnimationController(
      vsync: this,
      value: 1.0,
      duration: Duration(milliseconds: 500),
    );
    _tabController.addListener(_handleTabSelection);
    animation = Tween(begin: 0.0, end: 1.0).animate(_animationController);
    super.initState();
  }

  _handleTabSelection() {
    if (_tabController.indexIsChanging) {
      setState(() {
        _tabIndex = _tabController.index;
      });
      _animationController.reset();
      _animationController.forward();
    }
  }

  @override
  Widget build(BuildContext context) {
    List<Widget> _tabs = [
      MyAnimation(
        animation: animation,
        child: Text('first tab'),
      ),
      MyAnimation(
        animation: animation,
        child: Column(
          children: List.generate(20, (index) => Text('line: $index')).toList(),
        ),
      ),
      MyAnimation(
        animation: animation,
        child: Text('third tab'),
      ),
    ];

    return Scaffold(
      appBar: AppBar(),
      bottomNavigationBar: TabBar(
        controller: _tabController,
        labelColor: Colors.redAccent,
        isScrollable: true,
        tabs: myTabs,
      ),
      body: IndexedStack(
        children: _tabs,
        index: _tabIndex,
      ),
    );
  }
}

class MyAnimation extends AnimatedWidget {
  MyAnimation({key, animation, this.child})
      : super(
          key: key,
          listenable: animation,
        );

  final Widget child;

  @override
  Widget build(BuildContext context) {
    Animation<double> animation = listenable;
    return Opacity(
      opacity: animation.value,
      child: child,
    );
  }
}

答案 1 :(得分:0)

这是将IndexedStack与动画结合使用的更简洁的方法,我创建了一个FadeIndexedStack小部件。

https://gist.github.com/diegoveloper/1cd23e79a31d0c18a67424f0cbdfd7ad

用法

body: FadeIndexedStack(  
    //this is optional
    //duration: Duration(seconds: 1),
    children: _tabs.map((t) => t.widget).toList(),  
    index: _currentIndex,  
  ),  

答案 2 :(得分:0)

尝试将键添加到您的 IndexedStack 并将 transitionBuilder 方法添加到您的 AnimatedSwitcher 小部件,就像这样...

AnimatedSwitcher(
            duration: Duration(milliseconds: 1200),
            transitionBuilder: (child, animation) => SizeTransition(
              sizeFactor: animation,
              child: IndexedStack(
                key: ValueKey<int>(navigationIndex.state),
                index: navigationIndex.state,
                children: _tabs.map((t) => t.widget).toList(),  
              ),
            ),
            child: IndexedStack(
              key: ValueKey<int>(navigationIndex.state), //using StateProvider<int>
              index: navigationIndex.state,
              children: _tabs.map((t) => t.widget).toList(),  
            ),
          ),

还有其他很酷的过渡,如 ScaleTransition、SizeTransition、FadeTransition

答案 3 :(得分:-2)

尝试向您的IndexedStack添加密钥,这样您的代码将如下所示:

body: AnimatedSwitcher(  
  duration: Duration(milliseconds: 200),  
  child: IndexedStack(
    key: ValueKey<int>(_currentIndex),
    children: _tabs.map((t) => t.widget).toList(),  
    index: _currentIndex,  
  ),  
)

键已更改,因此AnimatedSwitcher会知道需要重建它的子级。