The TextFields in ListView.builder are disappearing

1k views Asked by At

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"...

Textfields Disappearing in ListView

2

There are 2 answers

0
Iqbal Ar On

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.

0
Gareth Beall On

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.