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).

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

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

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.