Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent rebuild StatelessWidget children of PageView

Tags:

flutter

I've create simple PageView app to test multiple pages.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    final firstPage = FirstPage(key: Key("FirstPage"));
    final secondPage = SecondPage(key: Key("SecondPage"));

    debugPrint("_MyHomePageState.build");
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: PageView(
        children: <Widget>[
          firstPage,
          secondPage,
        ],
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    debugPrint("FirstPage.build");
    return Container(
      child: Center(
        child: Text("First Page"),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    debugPrint("SecondPage.build");
    return Container(
      child: Center(
        child: Text("Second Page"),
      ),
    );
  }
}

Even thought _MyHomePageState.build has been shown only once, FirstPage.build and SecondPage.build were printed on every page changes.

What I'd like to prevent unnecessary page draw, how can I accomplish this?

like image 980
Hongseok Yoon Avatar asked Aug 29 '19 06:08

Hongseok Yoon


People also ask

How do I stop rebuilding the Flutter?

Try to make widgets constant. Provider has different types, such as Consumer, or Selector. As a whole, state management is an intermediate topic in Flutter that requires a lot of attention. Because changing state often requires widget rebuilding.

What is PageView Flutter?

A PageView is a widget that generates scrollable pages on the screen. This can either be a fixed list of pages or a builder function that builds repeating pages. PageView acts similarly to a Listview in the sense of constructing elements.


2 Answers

You can achieve so by using

1. const keyword

  • Make your widgets accept to be const:

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

    @override
    Widget build(BuildContext context) {
      debugPrint("FirstPage.build");
      return Container(
        child: Center(
          child: Text("First Page"),
        ),
      );
    }
    

    }

  • and call it with const keyword:

      return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: PageView(
          children: <Widget>[
            const firstPage(),
            const secondPage(),
          ],
        ),
      );
    

2. AutomaticKeepAliveClientMixin

  • Convert your StatelessWidget to StatefullWidget.
class FirstPage extends StatefulWidget {
  FirstPage({Key key}) : super(key: key);

  @override
  _FirstPageState createState() => _FirstPageState();
}

class _FirstPageState extends State<FirstPage> {
  @override
  Widget build(BuildContext context) {
    debugPrint("FirstPage.build");
    return Container(
      child: Center(
        child: Text("First Page"),
      ),
    );
  }
}
  • Extends AutomaticKeepAliveClientMixin on StatefullWidget created State.
class _FirstPageState extends State<FirstPage> with AutomaticKeepAliveClientMixin {
  • Call super on the build method.
@override
  Widget build(BuildContext context) {
    super.build(context);
    debugPrint("FirstPage.build");
    return Container(
      child: Center(
        child: Text("First Page"),
      ),
    );
  }
  • Override wantKeepAlive getter with true returned value.
  @override
  bool get wantKeepAlive => true;

And then your widget tree won't dispose of this widget so it won't rebuild over and over.

Code Example:

class FirstPage extends StatefulWidget {
  FirstPage({Key key}) : super(key: key);

  @override
  _FirstPageState createState() => _FirstPageState();
}

class _FirstPageState extends State<FirstPage>
    with AutomaticKeepAliveClientMixin {
  @override
  Widget build(BuildContext context) {
    super.build(context);
    debugPrint("FirstPage.build");
    return Container(
      child: Center(
        child: Text("First Page"),
      ),
    );
  }

  @override
  bool get wantKeepAlive => true;
}

3. MVVM Architecture with any State-management solution you like

It will save your state on ViewModel away from the View, so your UI can rebuild itself anytime it wants with no worries about your State because the ViewModel is still the same.

like image 183
Abdelazeem Kuratem Avatar answered Sep 30 '22 12:09

Abdelazeem Kuratem


You should always imagine that your build() methods (for both StatefulWidget and StatelessWidget) are being called 60 times per second, so they should be simple and idempotent. Anything else should be moved into a StatefulWidget initState() and friends.

like image 23
Randal Schwartz Avatar answered Sep 30 '22 12:09

Randal Schwartz