How to reposition widget without rebuilding its children in Flutter?

Issue

In my Flutter project, I want to reposition a list on a screen, using the Positioned widget, so the user can move the list manually or by using an animation.

What I did works pretty well, but I found out that, every time the list is moved, each item of the list is rebuild, which leads to some performance issues.

Here is a basic example:

class TestPositionedPage extends StatefulWidget {
  @override
  _TestPositionedPageState createState() => _TestPositionedPageState();
}

class _TestPositionedPageState extends State<TestPositionedPage> {

  double _yPage = 0;

  Widget _getItem(int position) {
    print("get item at $position");
    return ListTile(title: Text("Item at position $position"));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Stack(
          children: [
            Align(
              alignment: Alignment.topRight,
              child: FlatButton(
                onPressed: () {
                  setState(() => _yPage = Random().nextDouble() * 500);
                },
                child: Text("MOVE"),
                color: Colors.redAccent,
              ),
            ),
            Positioned(
              left: 0,
              top: _yPage,
              child: Container(
                width: 700,
                height: 700,
                child: ListView.builder(
                  itemCount: 100,
                  itemBuilder: (BuildContext context, int position) => _getItem(position),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

In that example, there is a button and every time I click on the button, it randomly repositions the list. But every time the list is repositioned, here is what I see in the console:

I/flutter (17851): get item at 0
I/flutter (17851): get item at 1
I/flutter (17851): get item at 2
I/flutter (17851): get item at 3
I/flutter (17851): get item at 4
I/flutter (17851): get item at 5
I/flutter (17851): get item at 6
I/flutter (17851): get item at 7
I/flutter (17851): get item at 8
I/flutter (17851): get item at 9
I/flutter (17851): get item at 10
I/flutter (17851): get item at 11
I/flutter (17851): get item at 12
I/flutter (17851): get item at 13
I/flutter (17851): get item at 14
I/flutter (17851): get item at 15
I/flutter (17851): get item at 16

Which means that every time the list is repositioned, it’s also rebuilt, even though nothing changed in the list.

So my question is: is there a way to prevent each item from being rebuilt every time the list is repositioned, or is there a way to cache the list rendering, in order to improve performances while repositioning?

Thanks.

Solution

When you call setState(() => _yPage = Random().nextDouble() * 500); the whole tree is rebuilt again, which causing the list rebuilt too. Instead build the list one time and reuse required.

Widget _myList;
  
  Widget initList(){
   return ListView.builder(
      itemCount: 100,
      itemBuilder: (BuildContext context, int position) => _getItem(position),
    );
  }

  @override
  void initState() {
    _myList = initList();
    super.initState();
  }

Then use the list in this way

Positioned(
    left: 0,
    top: _yPage,
    child: Container(
        width: 700,
        height: 700,
        child: _myList
        ...
            

Answered By – blackkara

Answer Checked By – Terry (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published.