Pete Alvin
Pete Alvin

Reputation: 4800

Flutter: Periodically calling list.add() inside initState() causes build() to run but the updated List isn't shown on screen

On the screen I expect to see an ever growing list:

but it's just black. If I hit CTRL+S in Android Studio all of a sudden the whole list is finally shown.

import 'dart:async';

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<Widget> list = [];

  @override
  Widget build(BuildContext context) {
    print('${list.length}');
    return MaterialApp(
      home: ListView(
        children: list,
      ),
    );
  }

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

    new Timer.periodic(const Duration(seconds: 1), (timer) {
      setState(() {
        list.add(Text('${list.length}', key: Key(list.length.toString())));
      });
    });
  }
}

At first I thought it was because I wasn't setting key property, but I added that and still nothing.

I know the build() property is being triggered every second because I see the print() statement showing the numbers in the debug Run tab output.

What am I missing here? It's got to be something simple!

Upvotes: 2

Views: 181

Answers (2)

KuKu
KuKu

Reputation: 7502

You need to 'key' parameter to 'ListView' widget.
With below code, I confirmed working what you want.
I think that because you add a 'key' to Text widget, 'ListView' widget didn't rebuild.

import 'dart:async';

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<Widget> list = [];

  @override
  Widget build(BuildContext context) {
    print('${list.length}');
    return MaterialApp(
      home: ListView(
        key: UniqueKey(),
        children: list,
      ),
    );
  }

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

    new Timer.periodic(const Duration(seconds: 1), (timer) {
      setState(() {
        list.add(Text('${list.length}', key: Key(list.length.toString())));
      });
    });
  }
}

Or you can do it also by changing 'ListView' to 'ListView.builder'.

ListView.builder(
        itemCount: list.length,
        itemBuilder: (context, index) {
          return list[index];
        },
      ),
    );

Upvotes: 1

Nisanth Reddy
Nisanth Reddy

Reputation: 6430

Interesting discovery.

What is actually causing your UI to not update is that the reference to the list variable is staying the same.

Adding an element, doesn't change the reference and ListView seems to work based on references.

Two ways you can confirm this are.

  1. Change your ListView to a much simpler Column widget and voila, it's updating correctly.

  2. Change your updation method. Destroy the reference and make a new List like this,

    list = [...list, Text('${list.length}')];
    

    and yet again, surprise.

Upvotes: 2

Related Questions