Reputation: 1344
To do an Inventory Control, I'm building a ListView from Firebase Data (a list of "Alimentos") and a TextField to edit the quantity for each "Alimento".
It's working fine when I have less than a screen of "Alimentos", but when I have some pages of "Alimentos", my textfields are disappearing. I understand it happens because ListView.builder is only going to build the items that are in or near the viewport (as you scroll), but I don't know how to solve it.
I read Textfield in ListView.builder Post, where @Ashton Thomas suggests to implemment a Custom Widget per Row, but I don't understand how to get that.
This is my ListView.Builder...
Widget _crearListado(BuildContext context, AlimentoBloc alimentoBloc) {
final _size = MediaQuery.of(context).size;
return Container(
height: _size.height * 0.5,
child: StreamBuilder(
stream: alimentoBloc.alimentoStream ,
builder: (BuildContext context, AsyncSnapshot<List<AlimentoModel>> snapshot){
if (snapshot.hasData) {
List<AlimentoModel> alimentos = snapshot.data;
alimentos.sort((a, b) => a.proteina.compareTo(b.proteina));
return Stack(
children: <Widget>[
ListView.builder(
itemCount: alimentos.length,
itemBuilder: (context, i) { //Esta variable es gráfica, sólo se crea para lo que se está viendo
_controlList.add(new ControlInventarioModel());
return _crearItem(context, alimentoBloc, alimentos[i], i);
},
padding: EdgeInsets.only(left: 10.0, right: 10.0, top: 0.0, bottom: 20.0),
),
],
);
} else {
return Center (child: Image(image: AssetImage('assets/Aplians-fish-Preloader.gif'), height: 200.0,));
}
},
),
);
}
Each row has an Item (_CrearItem) with an a TextField on its trailing (_cajaNum) as following:
Widget _crearItem(BuildContext context, AlimentoBloc alimentoBloc, AlimentoModel alimento, int i) {
return Card(
color: Colors.grey[200], //color de la tarjeta
elevation: 0.0, //sin sombra
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
child: ListTile(
title: Text('${alimento.nombre}', style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 18.0)),
subtitle: Text ('${alimento.fabricante}'), //refLoteActual
onTap: () {},
leading: Icon(FontAwesomeIcons.shoppingBag, color: Theme.of(context).accentColor),
trailing: _cajaNum(context, i, alimento.idAlimento),// nuevoControlador),
),
);
}
Widget _cajaNum(BuildContext context, int i, String idAlimento){
return Container(
width: 100.0,
height: 40.0,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.0),
),
child: TextField(
style: TextStyle(fontSize: 20.0),
textAlign: TextAlign.center,
keyboardType: TextInputType.numberWithOptions(),//decimal:false, signed: false),
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(RegExp(r'^(\d+)?\.?\d{0,2}')),
FilteringTextInputFormatter.singleLineFormatter,
],
maxLength: 5,
onChanged: (value) {
if(utils.isNumeric(value) && double.parse(value)>=0) {
_controlList[i].cantControl = double.parse(value);
}
else {
_controlList[i].cantControl = null;
}
},
onEditingComplete: () {
FocusScope.of(context).unfocus();
},
onSubmitted: (value) {
},
),
);
}
If my ListView continuously rebuilds on scroll, how can I keep the values of TextFields?
UPDATE
I include screenshots that show textfields "disappearing"...
Upvotes: 3
Views: 1201
Reputation: 333
For this problem you need to understand the widget life cycle of ListView.builder. See: https://api.flutter.dev/flutter/widgets/ListView-class.html#:~:text=Child%20elements%27%20lifecycle
Destruction
When a child is scrolled out of view, the associated element subtree, states and render objects are destroyed. A new child at the same position in the list will be lazily recreated along with new elements, states and render objects when it is scrolled back.
So, how do we stop Flutter from destroying our precious subtree and state?
With the AutoMaticKeepAliveClientMixin
:)
Extend your state class with this mixin as follows:
class MyHomePageState extends State<MyHomepage> with AutomaticKeepAliveClientMixin
Then you must do two things:
Add the wantKeepAlive
override:
@override
bool get wantKeepAlive => true;
Then add:
super.build(context);
Just under the build method that you want to preserve.
If there is a scenario where you know longer require the state to be preserved you can set:
bool get wantKeepAlive => false;
Hope this helps.
Upvotes: 1
Reputation: 61
add property TextFormField : initialValue
.
its worked for me, I also experienced the same thing.
like this
TextField(
style: TextStyle(fontSize: 20.0),
initialValue: _controlList[i].cantControll ?? '0',
onChanged: (value) {
if(utils.isNumeric(value) && double.parse(value)>=0) {
_controlList[i].cantControl = double.parse(value);
}
else {
_controlList[i].cantControl = null;
}
},
onEditingComplete: () {
FocusScope.of(context).unfocus();
},
onSubmitted: (value) {
},
),
cause, your textfield rebuild when you scroll and input next texfield.
but your data is already stored in _controlList[i].cantControl
.
Upvotes: 0