How do I force a ListView.builder to refresh?

0

In my Flutter app I have a ListView which contains several bespoke stateful Widgets (of type EditableAreaCard, based on data from a bespoke Area object). The EditableAreaCard can be editable, and if so there is an edit button on the card which takes it into edit mode - in which cancel and save buttons then appear.

The user can add a new item to the list - so an Area object is created, and a flag is set so that the ListView.builder adds this new Area object to the top of the list, in editable mode.

But when that happens, the new object is added to the top of the list, but in the wrong background colour and not in editing mode. Watching the behaviour as this card is then edited and closed, it's pretty clear that the ListView is not refreshing all its children fully, but reusing them - the new item at index 0 is in the same colour and (non-editing) state as the previous index 0, which has now been pushed down to index 1.

Here's the code - with the guts of the Card removed because I don't think it's relevant ... but will post it if needed.


class AreasScreen extends StatefulWidget {
  static const id = '/areas';

  static _AreasScreenState of(BuildContext context) =>
      context.ancestorStateOfType(const TypeMatcher<_AreasScreenState>());

  @override
  _AreasScreenState createState() => _AreasScreenState();
}

class _AreasScreenState extends State<AreasScreen> {
  Areas _alist = AreaRepo.get();
  Area _new_area = null;
  bool _is_adding_area = false;

  void cancel_add_area() {
    setState(() {
      _new_area = null;
      _is_adding_area = false;
    });
  }

  void finished_adding_area() {
    setState(() {
      _is_adding_area = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Areas of law'),
        leading: IconButton(
            icon: Icon(FontAwesomeIcons.arrowLeft),
            onPressed: () {
              Navigator.pop(context);
            }),
        actions: <Widget>[
          IconButton(
            icon: Icon(FontAwesomeIcons.plusCircle),
            onPressed: () {
              setState(() {
                _is_adding_area = true;
                _new_area = Area(
                  area_name: 'new area',
                  area_colour: '0xFFFFFFFF',
                );
              });
            },
          )
        ],
      ),
      body: ListView.builder(
        itemCount: _alist.areaCount + (_is_adding_area ? 1 : 0),
        itemBuilder: (context, index) {
          Area this_area_item = _is_adding_area
              ? (index == 0 ? _new_area : _alist.sortedList()[index - 1])
              : _alist.sortedList()[index];
          // TODO: somehow this isn't refreshing all items in the list when a card is added
          return EditableAreaCard(
            case_area: this_area_item,
            can_edit: true,
            start_editing: _is_adding_area && index == 0,
            index: index,
          );
        },
      ),
    );
  }
}


class EditableAreaCard extends StatefulWidget {
  Area case_area;
  bool can_edit;
  bool start_editing;

  EditableAreaCard({this.case_area, this.can_edit = false, this.start_editing = false});

  @override
  _EditableAreaCardState createState() => _EditableAreaCardState();
}

class _EditableAreaCardState extends State<EditableAreaCard> {

  TextEditingController _controller;
  Color _editingColor;
  bool _isCurrentlyEditing;

  @override
  void initState() {
    super.initState();
    setInitVars();
  }

  void setInitVars() {
    _controller = TextEditingController(text: widget.case_area.area_name);
    _editingColor = widget.case_area.color();
    _isCurrentlyEditing = widget.start_editing;
  }

  @override
  Widget build(BuildContext context) {
    return Card(…);
  }
}

Fundamentally I want to know how to force ListView.builder to rebuild all its children fully because they've shifted index. I've tried including a Key parameter in the EditableAreaCard but it makes no difference.

UPDATE - the final sentence above isn't quite correct... it seems I didn't try Key properly. See below - it solves the problem.

flutter
asked on Stack Overflow Jun 12, 2019 by Jonathan • edited Jun 13, 2019 by Jonathan

1 Answer

2

When creating each EditableAreaCard, are you passing a key to the constructor?

Something like this:

return EditableAreaCard(
            key: ValueKey(this_area_item),
            case_area: this_area_item,
            can_edit: true,
            start_editing: _is_adding_area && index == 0,
            index: index,
          );
answered on Stack Overflow Jun 12, 2019 by Sergio Bernal • edited Jun 12, 2019 by Sergio Bernal

User contributions licensed under CC BY-SA 3.0