Flutter does not display stateful children Widget in ListView
Your CRD widget is a StatefulWidget and the state will be reused when rebuilding since the type of the widget is the same an you did not give it a key.
To solve your issue there are a few possibilities:
- Add a key to all the items in the list
- Implement the
didUpdateWidget
method in the state of your widget - Use a statelesswidget and do the string concatination in the build method
Knoll Alexander
Updated on December 25, 2022Comments
-
Knoll Alexander over 1 year
As in the titel, I have a problem with ListView and I hope you can help me out.
I am using a basic ListView to build "Card Widgets" (with their own state). The ListView uses a List of Ids, which are used to build those "Card Widgets"
The problem: Any time I remove a card from the list by deleting an Id the ListView always removes the top most Child Widget. My backend deletes the right things, becouse after I restart the app so that the page gets populated anew, the deleted card is actually deleted and the one the removed by the ListView is visible again. It seems like ListView does not redraw it's children. Any Idea what is going on?
I created basic DartPad code to illustrate the problem
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; List<String> dd = new List<String>(); @override void initState() { super.initState(); dd.add('A'); dd.add('B'); dd.add('C'); dd.add('D'); } void _incrementCounter() { setState(() { _counter++; dd.insert(1, 'Q'); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title + _counter.toString()), ), body: ListView.builder( addAutomaticKeepAlives: false, itemBuilder: (context, index) { print('calling: $index :' + _counter.toString() + ' -> ' + dd[index] ); return new CRD(title: dd[index]); }, itemCount: dd.length ), /* ListView( children: dd.map((str) { return CRD(title: str); }).toList() ), */ floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), ); } } class CRD extends StatefulWidget { CRD({Key key, this.title}) : super(key: key); final String title; @override _CRD createState() => _CRD(); } class _CRD extends State<CRD> { String _val; @override void initState() { super.initState(); _val = widget.title + ' ' + widget.title; } @override Widget build(BuildContext context) { return Text(_val); } }
So after clicking once on the Add button the list content is [A,Q,B,C,D] but the app displays [A,B,C,D,D]. Whats going on here? Am i missing something?
-
Knoll Alexander over 3 yearsOk, so basically I populate my widget state within the 'didUpdateWidget' based upon my widget instance? I need to do that because the underlying state is reused and not created again. Is that right? Implementing 'didUpdateWidget' gave the results I expected! Thanks!
-
Pieter van Loon over 3 yearsWell partially yes, didUpdateWidgetbis only called if it is rebuilt so you also need to do it in initState still
-
Knoll Alexander over 3 yearsOkay thanks alot! Adding a key seems the easiest for what I want to do. Have a nice day :)