问题
I am struggling with animations package and I want to use animation with BottomNavigationBar. Without animation, I can save my state using IndexedStack. If I wrap IndexedStack inside PageTransitionSwitcher it doesn't work. In particular:
- animations are not showing but state is kept
- if I use key property of my IndexedStack, animations are showing but state is not working.
How can i fix it? I don't know how to set up keys. Thank you very much!!
class MainScreen extends StatefulWidget {
static String id = 'loading_screen';
@override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int _selectedPage = 0;
List<Widget> pageList = List<Widget>();
@override
void initState() {
pageList.add(PrimoTab());
pageList.add(SecondoTab());
pageList.add(TerzoTab());
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Bottom tab'),
),
body: PageTransitionSwitcher(
transitionBuilder: (child, primaryAnimation, secondaryAnimation) {
return SharedAxisTransition(
animation: primaryAnimation,
secondaryAnimation: secondaryAnimation,
child: child,
transitionType: SharedAxisTransitionType.horizontal,
);
},
child: IndexedStack(
index: _selectedPage,
children: pageList,
//key: ValueKey<int>(_selectedPage), NOT WORKING
),
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.directions_car),
label: 'First Page',
),
BottomNavigationBarItem(
icon: Icon(Icons.airplanemode_active),
label: 'Second Page',
),
BottomNavigationBarItem(
icon: Icon(Icons.directions_bike),
label: 'Third Page',
),
],
currentIndex: _selectedPage,
selectedItemColor: Colors.lightGreen,
unselectedItemColor: Colors.lightBlueAccent,
onTap: _onItemTapped,
),
);
}
void _onItemTapped(int index) {
setState(() {
_selectedPage = index;
});
}
}
Each tab is just a column with two text widgets, a button, and a text counter (for testing the state of each tab):
class PrimoTab extends StatefulWidget {
@override
_PrimoTabState createState() => _PrimoTabState();
}
class _PrimoTabState extends State<PrimoTab> {
int cont = -1;
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'TAB 1 - TEXT 1',
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'TAB 1 - TEXT 2',
),
),
FlatButton(
onPressed: () {
setState(() {
cont++;
});
},
child: Text("CLICK"),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Valore contatore $cont',
),
),
],
),
);
}
}
UPDATE 1: Using just
pageList[_selectedPage],
instead of
IndexedStack(
...
)
but not working (animations ok but state is not kept)
UPDATE 2 WITH SOLUTION (main.dart):
void main() {
runApp(
MaterialApp(
home: MainScreen(),
),
);
}
class MainScreen extends StatefulWidget {
static String id = 'loading_screen';
@override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int _selectedPage = 0;
List<Widget> pageList = List<Widget>();
@override
void initState() {
pageList.add(PrimoTab());
pageList.add(SecondoTab());
pageList.add(TerzoTab());
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Bottom tab'),
),
body: AnimatedIndexedStack(
index: _selectedPage,
children: pageList,
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.directions_car),
label: 'First Page',
),
BottomNavigationBarItem(
icon: Icon(Icons.airplanemode_active),
label: 'Second Page',
),
BottomNavigationBarItem(
icon: Icon(Icons.directions_bike),
label: 'Third Page',
),
],
currentIndex: _selectedPage,
selectedItemColor: Colors.lightGreen,
unselectedItemColor: Colors.lightBlueAccent,
onTap: _onItemTapped,
),
);
}
void _onItemTapped(int index) {
setState(() {
_selectedPage = index;
});
}
}
class AnimatedIndexedStack extends StatefulWidget {
final int index;
final List<Widget> children;
const AnimatedIndexedStack({
Key key,
this.index,
this.children,
}) : super(key: key);
@override
_AnimatedIndexedStackState createState() => _AnimatedIndexedStackState();
}
class _AnimatedIndexedStackState extends State<AnimatedIndexedStack>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
int _index;
@override
void initState() {
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 150),
);
_animation = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.ease,
),
);
_index = widget.index;
_controller.forward();
super.initState();
}
@override
void didUpdateWidget(AnimatedIndexedStack oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.index != _index) {
_controller.reverse().then((_) {
setState(() => _index = widget.index);
_controller.forward();
});
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Opacity(
opacity: _controller.value,
child: Transform.scale(
scale: 1.015 - (_controller.value * 0.015),
child: child,
),
);
},
child: IndexedStack(
index: _index,
children: widget.children,
),
);
}
}
回答1:
There are few ways to fix your situation. None of them is perfectly simple and there already lots of discussion here and here, trying to merge IndexedStack
with PageTransitionSwitcher
. No solution so far I saw.
I collect following possible ways to achieve this:
Store state somewhere else and pass into child. I haven't seen any method can stop
PageTransitionSwitcher
from rebuilding child widget. If you don't mine the child widget rebuild, it may be the most straight forward method to do with this.Use Custom
IndexedStack
with animation. like this and this. It works well with the feature in IndexStack that children won't rebuild, but the animation is not as good asPageTransitionSwitcher
and it can only show 1 widget in one time.
来源:https://stackoverflow.com/questions/65050756/flutter-cant-keep-the-state-of-tabs-using-pagetransitionswitcher