Mobile Development 9 min read

Understanding Flutter Widget Lifecycle: StatelessWidget and StatefulWidget

This article explains the Flutter widget lifecycle, covering the differences between StatelessWidget and StatefulWidget, detailing each lifecycle method, providing a runnable demo with code and logs, and discussing app‑level lifecycle handling and common pitfalls for mobile developers.

360 Tech Engineering
360 Tech Engineering
360 Tech Engineering
Understanding Flutter Widget Lifecycle: StatelessWidget and StatefulWidget

The widget lifecycle in Flutter describes the complete process from a widget being loaded to its removal, and understanding it helps developers perform actions at the appropriate moments.

StatelessWidget is a widget without mutable state; it only implements the build method, which is called on every UI refresh. Because it cannot call setState , its internal properties should be declared final , and any business logic should be placed in the constructor rather than in build .

StatefulWidget has an associated State object that can change over time. Its lifecycle is managed mainly inside the State class and includes the following callbacks:

initState : called once when the state is inserted into the widget tree; used for one‑time initialization.

didChangeDependencies : invoked when an inherited widget the state depends on changes (e.g., locale or theme).

build : constructs the widget UI; called after initState , didUpdateWidget , setState , didChangeDependencies , or when the state is re‑attached.

reassemble : called only during hot‑reload in development mode.

didUpdateWidget : triggered when the parent widget rebuilds and provides a new configuration.

deactivate : runs when the state is removed from the tree; may be followed by a re‑attachment.

dispose : final callback when the state is permanently removed; used to release resources.

The article includes a complete example that displays a random number and prints lifecycle callbacks:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: RandomPage(),
      ),
    );
  }
}

class RandomPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _RandomPageState();
}

class _RandomPageState extends State<RandomPage> {
  final _randomBuild = Random();
  int _randomValue = 0;

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

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print('didChangeDependencies');
  }

  @override
  Widget build(BuildContext context) {
    print('build');
    return Container(
      child: Padding(
        padding: EdgeInsets.only(top: 100),
        child: Center(
          child: Column(
            children: [
              Text('随机数: $_randomValue'),
              SizedBox(height: 200),
              TextButton(
                child: Text('切换随机数'),
                onPressed: () {
                  setState(() {
                    _randomValue = _randomBuild.nextInt(10000);
                  });
                },
              )
            ],
          ),
        ),
      ),
    );
  }

  @override
  void reassemble() {
    super.reassemble();
    print('reassemble');
  }

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

  @override
  void deactivate() {
    super.deactivate();
    print('deactivate');
  }

  @override
  void dispose() {
    super.dispose();
    print('dispose');
  }
}

Running the app prints:

flutter: initState
flutter: didChangeDependencies
flutter: build

Pressing the "切换随机数" button triggers setState and prints:

flutter: build

Performing a hot‑reload prints:

flutter: reassemble
flutter: didUpdateWidget
flutter: build

For app‑level lifecycle, implement WidgetsBindingObserver and override didChangeAppLifecycleState . The three most common AppLifecycleState values are resumed , inactive , and paused :

class _RandomPageState extends State<RandomPage> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance?.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance?.removeObserver(this);
    super.dispose();
  }

  void didChangeAppLifecycleState(AppLifecycleState state) async {
    if (state == AppLifecycleState.resumed) {
      getData();
    }
  }

  @override
  Widget build(BuildContext context) {
    throw UnimplementedError();
  }

  void getData() {}
}

Additional notes:

addPostFrameCallback can be used in initState to run code after the first frame is rendered.

The mounted property indicates whether the state is currently attached to the widget tree; checking if (mounted) { setState(...); } prevents exceptions when the widget is no longer in the tree.

A state can be dirty (needs a rebuild) or clean (no rebuild pending).

dartflutterMobile developmentstatefulwidgetStatelessWidgetwidget-lifecycle
360 Tech Engineering
Written by

360 Tech Engineering

Official tech channel of 360, building the most professional technology aggregation platform for the brand.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.