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