I am trying to retrieve data from API, that's works nice.
After that I want to show my data in a ExpansionPanelList, which is builded by a method:
class _CartaPageState extends State<CartaPage> {
@override
Widget build(BuildContext context) {
// Nos suscribimos al provider
final productoService = Provider.of<ProductoService>(context);
final List<Producto> productos = productoService.productos;
_productosItems = productosToItem(productos);
return Scaffold(
body: Container(
height: double.infinity,
width: double.infinity,
child: ListView(
children: [
ExpansionPanelList(
animationDuration: Duration(milliseconds: 300),
expansionCallback: (int index, bool isExpanded) {
setState(() {
_productosItems[index].isExpanded = !isExpanded;
//productosItems[index].isExpanded = !productosItems[index].isExpanded;
});
},
//children: productosToItem(productoService.entrantes).map<ExpansionPanel>((Item item) {
children: _productosItems.map<ExpansionPanel>((Item item) {
return ExpansionPanel(
headerBuilder: (context, isExpanded) {
return ListTile(
title: Text(item.headerValue),
);
},
................
The data is shown perfect, but the state is not refreshing on my ItemModel, I think the problem is because the widget is redrawing each time I touch the panel list, that retrieve (again) data from the API and never changes the state.
How can I resolve it?
Thank you in advance
EDIT: CartaPage is wraped by:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => ProductoService()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Material App',
home: CartaPage()
),
);
}
}
EDIT 2:
I agree I am losing state, this it the method to convert Product to Item:
List<Item> productosToItem(List<Producto> productos) {
return List.generate(productos.length, (index) {
return Item(
headerValue: productos[index].tipo,
expandedValue: productos[index].nombre,
);
});
}
Is ExpansionPanel
having its isExpanded
set to item.isExpanded
?
You get your isExpanded
state from whatever productosToItem()
generates.
When you call setState
you queue a new build, which will call productosToItem()
again. Without knowing what that method does, I cannot help much.
I would suggest you look into productosToItem
and why it isn't setting isExpanded
to the correct value.
If _productosItems[index].isExpanded
isn't a setter, I would imagine you are losing the state.
EDIT 1:
You can create an internal state list that can persist the expanded state:
class Item {
Item({
this.expandedValue,
this.headerValue,
this.producto,
this.isExpanded = false,
});
String expandedValue;
String headerValue;
Producto producto; // <------------- ADDED
bool isExpanded;
}
class _CartaPageState extends State<CartaPage> {
Map<Producto, bool> expanded = {}; // <------------- ADDED
@override
Widget build(BuildContext context) {
// Nos suscribimos al provider
final productoService = Provider.of<ProductoService>(context);
final List<Producto> productos = productoService.productos;
// NOTE: ----------- converted to a local variable
final _productosItems = productosToItem(productos);
return Scaffold(
body: Container(
height: double.infinity,
width: double.infinity,
child: ListView(
children: [
ExpansionPanelList(
key: ValueKey(productos.length), // <------------- ADDED
animationDuration: Duration(milliseconds: 300),
expansionCallback: (int index, bool isExpanded) {
// NOTE: ----------- updated
final producto = productos[index];
setState(() {
expanded[producto] = !isExpanded;
});
},
children: _productosItems.map<ExpansionPanel>((Item item) {
return ExpansionPanel(
isExpanded: expanded[item.producto], // <------------- ADDED
canTapOnHeader: true,
headerBuilder: (context, isExpanded) {
return ListTile(
title: Text(item.headerValue),
);
},
body: ListTile(
title: Text(item.expandedValue),
),
);
}).toList(),
),
],
),
),
);
}
List<Item> productosToItem(List<Producto> productos) {
// keep a list of previous map
final toRemove = Map<Producto, bool>.from(expanded);
final items = List.generate(productos.length, (index) {
final producto = productos[index];
// set initial expanded state
expanded.putIfAbsent(producto, () => false);
// the item will be retained
toRemove.remove(producto);
return Item(
headerValue: producto.tipo,
expandedValue: producto.nombre,
producto: producto,
isExpanded: expanded[producto],
);
});
if (toRemove.isNotEmpty) {
// cleanup unused items
expanded.removeWhere((key, _) => toRemove.containsKey(key));
}
return items;
}
}
The key: ValueKey(productos.length),
is needed, since ExpansionPanelList
acted weirdly with magically appearing or disappearing items.