Search code examples
performanceflutterlistviewlazy-evaluation

Flutter ListView in Container Lazy Rendering and performance


I have a long list of items that are being updated frequently. The list is rendered using ListView.builder wrapped in a container to apply borders etc.

My problems is, that when the ListView is located in a container, all items are rendered including elements outside the screen. As the list is redrawn very often, it affects performance severely.

I've created two examples to illustrate the problem. To keep it simple, the examples are stateless, in real life the list is stateful.

Example 1 - ListView is root widget. Notice in console that only visible cards are build. https://dartpad.dev/ac436b786ae48383c107a64bb3db8070

Example 2 - ListView is wrapped in container. Notice all cards are build. https://dartpad.dev/109f1f97085c1726c0826f6ea8b731ec

How do I make a scrollable container with a list of items, that only renders visible items?

The source of Example 2 here:

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

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

class MyApp extends StatelessWidget {
  MyApp({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: SingleChildScrollView(
            child: Container(
                color: Colors.green,
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Container(
                        width: 700,
                        decoration: BoxDecoration(
                            color: Colors.white,
                            borderRadius: BorderRadius.circular(10),
                            boxShadow: [
                              BoxShadow(
                                color: Colors.black.withOpacity(0.44),
                                spreadRadius: 0,
                                blurRadius: 15,
                                offset:
                                    Offset(0, 1), // changes position of shadow
                              ),
                            ]),
                        margin: EdgeInsets.all(24.0),
                        child: Container(
                            child: Column(
                                crossAxisAlignment: CrossAxisAlignment.stretch,
                                children: [
                              ListView.builder(
                                  scrollDirection: Axis.vertical,
                                  shrinkWrap: true,
                                  itemCount: 20,
                                  itemBuilder: (context, index) {
                                    var text = 'Card $index';
                                    print('Building card $text');
                                    return Card(
                                        child: Container(
                                            height: 130,
                                            child: Center(
                                              child: Text(text),
                                            )));
                                  })
                            ]))),
                  ],
                ))));
  }
}


Solution

  • You should give a height limit to the listView wrapped in container. Just like this:

    
    class MyApp extends StatelessWidget {
      MyApp({Key key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
            home: SingleChildScrollView(
                child: Container(
                    color: Colors.green,
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: <Widget>[
                        Container(
                            width: 700,
                            decoration: BoxDecoration(
                                color: Colors.white,
                                borderRadius: BorderRadius.circular(10),
                                boxShadow: [
                                  BoxShadow(
                                    color: Colors.black.withOpacity(0.44),
                                    spreadRadius: 0,
                                    blurRadius: 15,
                                    offset:
                                    Offset(0, 1), 
                                  ),
                                ]),
                            margin: EdgeInsets.all(24.0),
                            child: Container(
                                child: Column(
                                    crossAxisAlignment: CrossAxisAlignment.stretch,
                                    children: [
                                      SizedBox( // add a SizedBox and set a specific height
                                        height: 1000,
                                        child: ListView.builder(
                                            scrollDirection: Axis.vertical,
                                            shrinkWrap: true,
                                            itemCount: 20,
                                            itemBuilder: (context, index) {
                                              var text = 'Card $index';
                                              print('testtest Building card $text');
                                              return Card(
                                                  child: Container(
                                                      height: 130,
                                                      child: Center(
                                                        child: Text(text),
                                                      )));
                                            }),
                                      )
                                    ]))),
                      ],
                    ))));
      }
    }