Karan
Karan

Reputation: 1187

ListView not updating correctly

I'm trying to replace the ListView with new data but this is not behaving as expected. The main focus is on the

ListView.builder(
          itemBuilder: (context, i) {
            return Order(_orders[i]);
          }

and

_getOrders() async {
    String authToken = await Auth.getAuthToken();

    http.Response response = await http.get(
        API_URL + '?date=' + _formatDate(_activePeriod),
        headers: {'x-auth': authToken});

    var parsedJson = json.decode(response.body);


    setState(() {
      if (parsedJson.runtimeType.toString() != 'List<dynamic>') {
        _orders = [];
      } else {
        _orders = parsedJson;
      }
    });
  }

the first render is correct when the app starts but when I call the _changeMonth method on Prev/Next button press which, in turn, calls the _getOrders, it doesn't reload the ListView after I override the _orders variable. If the array has more items than the previous then I see the new items added at the end but the previous (first time rendered) items stay on the screen. I can correctly see the new data but overriding the _orders variable after the API call does not render the new data in the ListView. Do I need to do something more than using the setState()?

Full code of main file. Order class is defined in other file but I believe it is not required here.

class Home extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return HomeState();
  }
}

 class HomeState extends State {
      final DateTime _thisMonth = DateTime.now();
      DateTime _activePeriod = DateTime.now();

  final String app = 'Orders';

  List<dynamic> _orders = [];

  bool isLoggedIn = false;

  @override
  initState() {
    super.initState();
    Auth.init().then((loggedIn) {
      if (loggedIn) {
        setState(() {
          isLoggedIn = true;
        });
        _getOrders();
      }
    });
  }

  HomeState() {}

  @override
  build(BuildContext context) {
    return MaterialApp(
      title: app,
      home: Scaffold(
        appBar: new AppBar(
          title: Text(app),
        ),
        body: isLoggedIn
            ? ListView.builder(
          itemBuilder: (context, i) {
            return Order(_orders[i]);
          },
          itemCount: _orders.length,
        )
            : Auth.showLoginForm(),
        persistentFooterButtons: <Widget>[
          Row(
            mainAxisAlignment: MainAxisAlignment.start,
            children: <Widget>[              
              FlatButton(
                child: Text('Prev'),
                onPressed: () {
                  _changeMonth(-1);
                },
              ),
              FlatButton(
                child: Text('Next'),
                onPressed: _activePeriod.month == _thisMonth.month
                    ? null
                    : () {
                  _changeMonth(1);
                },
              ),
            ],
          )
        ],
      ),
    );
  }

  _changeMonth(int increment) async {
    setState(() {
      _activePeriod =
          DateTime(_activePeriod.year, _activePeriod.month + increment, 1);
      _getOrders();
    });
  }

  _formatDate(DateTime date) {
    return '${date.year}/${date.month}/${date.day}';
  }

  _getOrders() async {
    String authToken = await Auth.getAuthToken();

    http.Response response = await http.get(
        API_URL + '?date=' + _formatDate(_activePeriod),
        headers: {'x-auth': authToken});

    var parsedJson = json.decode(response.body);
    //print(parsedJson);// new data appears fine
    setState(() {
      if (parsedJson.runtimeType.toString() != 'List<dynamic>') {
        _orders = [];
      } else {
        _orders = parsedJson;
      }
    });
  }
}

Upvotes: 0

Views: 80

Answers (1)

Kirill Shashov
Kirill Shashov

Reputation: 1019

Try to add a key for Order

// constructor    
Order(..., {Key key}): super(key: key)

// you should use unique key for stateful widgets, otherwise the list rebuild will be incorrect
itemBuilder: (context, i) {
   return Order(_orders[i], Key(_orders[i].id));
},

see article

You can use keys to control which widgets the framework matches up with other widgets when a widget rebuilds. By default, the framework matches widgets in the current and previous build according to their runtimeType and the order in which they appear. With keys, the framework requires that the two widgets have the same key as well as the same runtimeType.

Upvotes: 1

Related Questions