didUpdateWidget called immediately after initState in Flutter

Viewed 1840

According to Flutter Documentation:

didUpdateWidget called whenever the widget configuration changes

But, in the following code, didUpdateWidget is called immediately after initState on the first time.

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: Test(),
    );
  }
}

class Test extends StatefulWidget {
  @override
  _TestState createState() => _TestState();
}

class _TestState extends State<Test> {

  @override
  void initState() {
    print("initState called");
    super.initState();
  }

  @override
  void didUpdateWidget(Test oldWidget) {
    print("didUpdateWidget called");
    super.didUpdateWidget(oldWidget);
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}


// output
//
// initState called
// didUpdateWidget called

Can someone describe why this happens? and how can I compare the whole oldWidget with widget

Thank you

update
as @pskink mentioned, didUpdateWidget is not called immediately after initState, it's after the first build

Yet another question is why it is called after the first build with the following code:

    print("didUpdateWidget called");   <--
    super.didUpdateWidget(oldWidget);  <--

but if I call print after super.didUpdateWidget(oldWidget);, it works fine.

1 Answers

In my test using Flutter 2.10.1 stable version, didUpdateWidget was only called after calling setState() to update the data displayed on the Widget. This behavior matches the description mentioned on the docs where the didUpdateWidget method is called whenever the widget configuration changes. After the didUpdateWidget method was called, build will then be called.

Here's how the flow can look like after calling setState() once.

  1. initState()
  2. build()
  3. setState() called
  4. didUpdateWidget()
  5. build()

This can be replicated on this simple Flutter app.

import 'package:flutter/material.dart';

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

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

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

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

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  void initState() {
    debugPrint('State initState');
    super.initState();
  }


  @override
  void didUpdateWidget(MyHomePage oldWidget) {
    debugPrint('State didUpdateWidget');
    super.didUpdateWidget(oldWidget);
  }

  @override
  Widget build(BuildContext context) {
    debugPrint('State Widget build');
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}
Related