Flutter no puede mantener el estado de las pestañas usando PageTransitionSwitcher
Estoy luchando con el paquete de animaciones y quiero usar la animación con BottomNavigationBar. Sin animación, puedo guardar mi estado usando IndexedStack. Si envuelvo IndexedStack dentro de PageTransitionSwitcher, no funciona. En particular:
- las animaciones no se muestran pero el estado se mantiene
- si utilizo la propiedad clave de mi IndexedStack, se muestran animaciones pero el estado no funciona.
¿Cómo puedo arreglarlo? No sé cómo configurar las claves. ¡¡Muchas gracias!!
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;
});
}
}
Cada pestaña es solo una columna con dos widgets de texto, un botón y un contador de texto (para probar el estado de cada pestaña):
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',
),
),
],
),
);
}
}
ACTUALIZACIÓN 1: Usando solo
pageList[_selectedPage],
en vez de
IndexedStack(
...
)
pero no funciona (las animaciones están bien pero el estado no se mantiene)
ACTUALIZACIÓN 2 CON SOLUCIÓN (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,
),
);
}
}
Respuestas
Hay algunas formas de solucionar su situación. Ninguno de ellos es muy simple y ya mucha discusión aquí y aquí , tratando de combinar IndexedStack
con PageTransitionSwitcher
. No vi ninguna solución hasta ahora.
Recopilo las siguientes formas posibles de lograr esto:
Almacene el estado en otro lugar y pase al niño. No he visto ningún método que pueda detener la
PageTransitionSwitcher
reconstrucción del widget secundario. Si no extrae la reconstrucción del widget secundario , puede ser el método más sencillo para hacerlo.Utilice Personalizado
IndexedStack
con animación. como este y este . Funciona bien con la característica de IndexStack que los niños no reconstruyen, pero la animación no es tan buenaPageTransitionSwitcher
y solo puede mostrar 1 widget a la vez.