Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I insert widgets at the top of a ListView?

Tags:

flutter

dart

Brief Note:

In all of my code examples you will see things like material.Widget instead of just Widget. This is because I like to name my imports like this for example:

import 'package:flutter/material.dart' as material;

My Question:

I am trying to make a ListView show newly added Widgets at the top of the list. I have a simple stateful widget that only contains a list of models (classes that contain necessary information to construct the widgets that are going into the ListView), and should construct ListView's children based off of that list.

class Page extends material.StatefulWidget {
  final List<CardModel> cardModels;
  Page(this.cardModels);

  @override
  _PageState createState() => new _PageState(cardModels);
}

class _PageState extends material.State<Page> {
  List<CardModel> cardModels;

  _PageState(this.cardModels);

  @override
  material.Widget build(material.BuildContext context) {
    return new material.ListView(
      children: cardModels.map((cardModel) => new Card(cardModel.cardID)).toList(),
    );
  }
}

The behavior that I expect from this is that whenever the build method is called (and setState is properly used), that the ListView should be reconstructed properly and contain child widgets in the order of the list. It successfully does this if I simply add new models to cardModels sequentially:

cardModels.add(new CardModel(nextID++));

You can see widgets get added sequentially with their id's incrementing properly:

enter image description here

However, I want newer widgets to be inserted at the top (which would be shown with higher ids at the top). In order to accomplish this, I try inserting new models at the beginning of the list:

cardModels.insert(0, new CardModel(nextID++));

Unfortunately, instead of seeing the correct widgets, I just get widget with id 0 over and over again:

enter image description here

I know that the list of models is being updated correctly because I can print it out and see the ids in descending order. I am assuming that there is something about how flutter detects changes to widgets that is causing this behavior, but after a lot of reading, I still have not been able to figure it out. Any help would be much appreciated. Also, I call set state in the widget that ends up building the page (the widget containing the ListView) as one of its children:

btn = new FloatingActionButton(
          onPressed: () => setState(_pageController.addCard),
          tooltip: 'Upload File',
          child: new Icon(Icons.file_upload),
        );

_pageController is the object that modifies the list of models. If this is not enough information, let me know and I am happy to provide more code or answer any questions.

like image 428
Nick Avatar asked Mar 01 '18 23:03

Nick


1 Answers

There is a simple quick fix, using the didWidgetUpdate override, by checking if the cardModels object in the oldWidget is the same in the cardModels being passed as a parameter. like so

 @override
  void didUpdateWidget(covariant Page oldWidget) {
    if (widget.cardModels != oldWidget.cardModels) {
      setState((){
        cardModels = widget.cardModels;
      });
    }
    super.didUpdateWidget(oldWidget);
  }

Full example below

class Page extends material.StatefulWidget {
  final List<CardModel> cardModels;
  Page(this.cardModels);

  @override
  _PageState createState() => new _PageState(cardModels);
}

class _PageState extends material.State<Page> {
  List<CardModel> cardModels;

  @override
  void initState(){
    cardModels = widget.cardModels;
    super.initState();
  }
   
  @override
  void didUpdateWidget(covariant Page oldWidget) {
    if (widget.cardModels != oldWidget.cardModels) {
      setState((){
        cardModels = widget.cardModels;
      });
    }
    super.didUpdateWidget(oldWidget);
  }

  _PageState(this.cardModels);

  @override
  material.Widget build(material.BuildContext context) {
    return new material.ListView(
      children: cardModels.map((cardModel) => new Card(cardModel.cardID)).toList(),
    );
  }
}
like image 83
Patrick Waweru Avatar answered Oct 14 '22 15:10

Patrick Waweru