GardenMobile & interfaces

Observing Flutter rebuilds with setState

A nested-widget experiment that records build and lifecycle calls around several buttons.

Logging lifecycle methods is a useful way to observe which state owns a rebuild. This older experiment nested stateful and stateless widgets, then placed buttons at different levels.

The widget tree

/// App
/// WgFullState
///   + Button
///   + Container
///     + Button 
///   + SubWgStateless
///     + Container
///   + SubWgFullState
///     + Button
///     + SubWgStateless
/// WgStateless
///   + WgFullState
///     + Button
///     + Container
///       + Button 
///     + SubWgStateless
///       + Container
///     + SubWgFullState
///       + Button
///       + SubWgStateless

The recorded startup

Restarted application in 1,955ms.
I/flutter (19233): app build
I/flutter (19233): initState WgFullState
I/flutter (19233): build WgFullState
I/flutter (19233): build SubWgStateless
I/flutter (19233): initState SubWgFullState
I/flutter (19233): build SubWgFullState
I/flutter (19233): build SubWgStateless
I/flutter (19233): build WgStateless
I/flutter (19233): initState WgFullState
I/flutter (19233): build WgFullState
I/flutter (19233): build SubWgStateless
I/flutter (19233): initState SubWgFullState
I/flutter (19233): build SubWgFullState
I/flutter (19233): build SubWgStateless
I/flutter (19233): app build
I/flutter (19233): didUpdateWidget WgFullState
I/flutter (19233): build WgFullState
I/flutter (19233): build SubWgStateless
I/flutter (19233): didUpdateWidget SubWgFullState
I/flutter (19233): build SubWgFullState
I/flutter (19233): build SubWgStateless
I/flutter (19233): build WgStateless
I/flutter (19233): didUpdateWidget WgFullState
I/flutter (19233): build WgFullState
I/flutter (19233): build SubWgStateless
I/flutter (19233): didUpdateWidget SubWgFullState
I/flutter (19233): build SubWgFullState
I/flutter (19233): build SubWgStateless

The log shows more than one build pass in that run. It does not establish that every Flutter application always builds twice at startup or that initState itself is a general cause of a second application build.

Compare button ownership

The two parent-level buttons produced these recorded sequences:

I/flutter (19233): setState WgFullState
I/flutter (19233): build WgFullState
I/flutter (19233): build SubWgStateless
I/flutter (19233): didUpdateWidget SubWgFullState
I/flutter (19233): build SubWgFullState
I/flutter (19233): build SubWgStateless
I/flutter (19233): setState WgFullState Btn2
I/flutter (19233): build WgFullState
I/flutter (19233): build SubWgStateless
I/flutter (19233): didUpdateWidget SubWgFullState
I/flutter (19233): build SubWgFullState
I/flutter (19233): build SubWgStateless

The nested stateful widget’s button produced a narrower sequence:

I/flutter (19233): setState SubWgFullState
I/flutter (19233): build SubWgFullState
I/flutter (19233): build SubWgStateless

The next three source logs repeated these patterns and are omitted. setState marks the owning state for a rebuild; how descendants update depends on the resulting widget tree and framework reconciliation, not an unconditional rule that every descendant must do identical work.

The preserved experiment

Read the historical widget implementation

import 'package:flutter/material.dart';

class RDApp extends StatefulWidget {
  @override
  _RDAppState createState() => _RDAppState();
}

/// WgFullState
///  + Wg1Statless
///  + Wg1FullState
/// WgStatless
class _RDAppState extends State<RDApp> {
  @override
  Widget build(BuildContext context) {
    print('app build');
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('AppBar'),
        ),
        body: Column(
          children: <Widget>[
            WgFullState(),
            WgStateless(),
          ],
        ),
      ),
    );
  }
}

class WgFullState extends StatefulWidget {
  @override
  _WgFullState1State createState() => _WgFullState1State();
}

class _WgFullState1State extends State<WgFullState> {
  @override
  void initState() {
    print('initState WgFullState');
    super.initState();
  }

  @override
  void didUpdateWidget(WgFullState oldWidget) {
    print('didUpdateWidget WgFullState');
    super.didUpdateWidget(oldWidget);
  }

  @override
  Widget build(BuildContext context) {
    print('build WgFullState');
    return Container(
        child: Column(
          children: <Widget>[
            RaisedButton(
              child: Text('WgFullState'),
              onPressed: () {
                setState(() {
                  print('setState WgFullState');
                });
              },
            ),
            Container(
              child: RaisedButton(
                child: Text('WgFullState Btn2'),
                onPressed: () {
                  setState(() {
                    print('setState WgFullState Btn2');
                  });
                },
              ),
            ),
            SubWgStateless(),
            SubWgFullState(),
          ],
        )
    );
  }
}

class SubWgFullState extends StatefulWidget {
  @override
  _SubWgFullStateState createState() => _SubWgFullStateState();
}

class _SubWgFullStateState extends State<SubWgFullState> {
  @override
  void initState() {
    print('initState SubWgFullState');
    super.initState();
  }

  @override
  void didUpdateWidget(SubWgFullState oldWidget) {
    print('didUpdateWidget SubWgFullState');
    super.didUpdateWidget(oldWidget);
  }

  @override
  Widget build(BuildContext context) {
    print('build SubWgFullState');
    return Container(
      child: Column(
        children: <Widget>[
          RaisedButton(
            child: Text('SubWgFullState'),
            onPressed: () {
              setState(() {
                print('setState SubWgFullState');
              });
            },
          ),
          SubWgStateless()
        ],
      )
    );
  }
}

class WgStateless extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('build WgStateless');
    return Container(
      child: WgFullState(),
    );
  }
}

class SubWgStateless extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('build SubWgStateless');
    return Container();
  }
}

Source: original Gist revision. The old widget APIs and recorded timings were not rerun for this revision.

← Back to the garden