AnimatedSwitcher with IndexedStack

人盡茶涼 提交于 2021-01-18 05:39:05

问题


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.


回答1:


If you need to use IndexedStack.

You can add custom animation and trigger it on changing tabs, like that:

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



回答2:


This is a cleaner way to use IndexedStack with animations , I created a FadeIndexedStack widget.

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

Usage

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




回答3:


Try to add key to your IndexedStack so your code will look like:

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

The key is changed so AnimatedSwitcher will know that it's child is need to rebuild.



来源:https://stackoverflow.com/questions/57694249/animatedswitcher-with-indexedstack

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!