rafaelcb21
rafaelcb21

Reputation: 13304

Flutter - Strange ListView behavior after refresh

Could they help me solve and understand why ListView's strange behavior after a refresh?

In the example below I created a list with 24 elements and I go to the last item in the list and tap the icone refresh that will update the list. Only after the update does the ListView no longer show all elements.

And to ensure each element always has a different Key through the randomString

enter image description here

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

void main() {
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new CategoryPage(),
    );
  }
}

class CategoryPage extends StatefulWidget {
  @override
  CategoryPageState createState() => new CategoryPageState();
}

class CategoryPageState extends State<CategoryPage> {
  Color blueAppBar = new Color(0xFF26C6DA);
  List<Widget> listCategories = [];
  List listaDB = [];
  var listaCategory;

  String randomString(int length) {
    var rand = new Random();
    var codeUnits = new List.generate(
        length, 
        (index){
          return rand.nextInt(33)+89;
        }
    );   
    return new String.fromCharCodes(codeUnits);
  }

  @override
  void initState() {
    this.listaDB = 
      [
        [{'category': 'foo01'}],
        [{'category': 'foo02'}],
        [{'category': 'foo03'}],
        [{'category': 'foo04'}],
        [{'category': 'foo05'}],
        [{'category': 'foo06'}],
        [{'category': 'foo07'}],
        [{'category': 'foo08'}],
        [{'category': 'foo09'}],
        [{'category': 'foo10'}],
        [{'category': 'foo11'}],
        [{'category': 'foo12'}],
        [{'category': 'foo13'}],
        [{'category': 'foo14'}],
        [{'category': 'foo15'}],
        [{'category': 'foo16'}],
        [{'category': 'foo17'}],
        [{'category': 'foo18'}],
        [{'category': 'foo19'}],
        [{'category': 'foo20'}],
        [{'category': 'foo21'}],
        [{'category': 'foo22'}],
        [{'category': 'foo23'}],
        [{'category': 'foo24'}]
      ];
  }

  @override
  Widget build(BuildContext context) {

    List<Widget> buildListCategories(List list) {
      this.listCategories = [];

      for(var i in list) {
        var category = i[0]['category'];

        this.listCategories.add(
          new ItemCategory(
            key: new Key(randomString(20)),
            category: category,
          )
        );  
      }
      return this.listCategories;
    }

    this.listaCategory = buildListCategories(this.listaDB);

    return new Scaffold( 
      appBar: new AppBar(
        title: new Text('Categories'),
        backgroundColor: blueAppBar,
        actions: <Widget>[
          new IconButton(
            icon: new Icon(Icons.refresh),
            onPressed: () {
              setState(() {
                this.listaCategory = buildListCategories(this.listaDB);
              });
            },
          )
        ],
      ),
      body: new ListView(
        padding: new EdgeInsets.only(top: 8.0, right: 0.0, left: 0.0),
        children: this.listaCategory
      ),
    );
  }
}

class ItemCategory extends StatelessWidget {
  final String category;

  ItemCategory({
    Key key,
    this.category}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return new Container(
      decoration: new BoxDecoration(
        border: new Border(
          top: new BorderSide(style: BorderStyle.solid, color: Colors.black26),
        ),
        color: new Color(0xFFFAFAFA),
      ),
      margin: new EdgeInsets.only(top: 0.0, bottom: 0.0),
      child: new Row(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          new Expanded(
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                new Container(
                  margin: new EdgeInsets.only(left: 16.0),
                  padding: new EdgeInsets.only(right: 40.0, top: 4.5, bottom: 4.5),
                  child: new Row(
                    children: <Widget>[
                      new Container(
                        margin: new EdgeInsets.only(right: 16.0),
                        child: new Icon(
                          Icons.brightness_1,
                          color: Colors.black,
                          size: 35.0,
                        ),
                      ),
                      new Text(this.category),
                    ],
                  )
                )
              ],
            ),
          )
        ],
      ),
    );
  }
}

Upvotes: 1

Views: 2334

Answers (2)

rafaelcb21
rafaelcb21

Reputation: 13304

Since the problem is in the key, the ListView also needs a key, because when updating to another list, if each list does not have its own key the list is mounted in the wrong way.

The ListView code should look like this:

body: new ListView(
  key: new Key(randomString(20)), //new
  padding: new EdgeInsets.only(top: 8.0, right: 0.0, left: 0.0),
  children: this.listaCategory
),

By making this change the list is mounted correctly.

Upvotes: 2

Rene
Rene

Reputation: 1047

You still call buildListCategories twice if you push the button (in the setState and in the build method), but that is not the problem. Also I would not nest the methods. Apparently something goes wrong with the key. If you don't set the key everything is fine. I am not sure why that breaks the listview, but I also don't think it adds anything.

I made a gist with the fixed code: https://gist.github.com/renefloor/0fed1bbd43567b45ed2605f57ecce29f

Upvotes: 1

Related Questions