flutterflutter-layoutflutter-animation

AnimatedSwitcher with IndexedStack


I have to use IndexedStack to maintain the state of my widgets for my BottomNavigationBar. Now i want to use AnimatedSwitcher (or an alternative) to create an animation when i switch tabs. I'm having issues getting AnimatedSwitcher to trigger on change of IndexedStack. I'm having IndexedStack as the child of AnimatedSwitcher, which obviously causes AnimatedSwitcher to not trigger because the IndexedStack widget doesn't change, only it's child.

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

Is there any way around this issue? By manually triggering the AnimatedSwitcher, or by using a different method to create an animation? I also tried changing the key, but that obviously resulted in it creating a new IndexedStack everytime the a new state was created, and therefor the states of the tabs was lost as well.


Solution

  • If you need to use IndexedStack.

    You can add custom animation and trigger it on changing tabs, like that: 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,
        );
      }
    }