Mastering Flutter's StatefulWidget and App Lifecycle
This article explains the complete lifecycle of Android activities, iOS view controllers, and Flutter's StatefulWidget, shows how to place initialization and business logic, demonstrates key lifecycle methods with code examples, and covers Flutter app‑level lifecycle states for robust mobile development.
Android
If you are an Android developer, the Activity lifecycle is familiar:
onCreate
onStart
onResume
onPause
onStop
onDestroy
iOS
If you are an iOS developer, the UIViewController lifecycle is well known:
viewDidLoad
viewWillAppear
viewDidAppear
viewWillDisappear
viewDidDisappear
viewDidUnload
Flutter
Flutter has two main widget types: StatelessWidget (immutable) and StatefulWidget (mutable). This article focuses on the lifecycle of StatefulWidget .
StatelessWidget
Stateless widgets are immutable; they render once and only rebuild when the parent changes configuration, when the widget is first inserted, or when an inherited widget changes.
StatefulWidget
A StatefulWidget consists of two classes: the immutable widget class and a mutable State class created by createState(). The State holds the mutable data and persists throughout the widget’s lifetime.
StatefulWidget Lifecycle
createState : Called when the widget is created; sets mounted to true.
initState : Runs once for one‑time initialization (e.g., variable setup, subscriptions, server calls).
didChangeDependencies : Invoked when the widget’s dependencies (e.g., inherited widgets, locale, theme) change; marks the widget dirty and triggers build.
build : Returns the widget tree; may be called many times, so it should contain only UI construction logic.
reassemble : Called only in debug mode during hot‑reload.
didUpdateWidget : Called when the parent rebuilds with a new widget instance; followed by another build.
deactivate : Called when the widget is removed from the tree but might be reinserted elsewhere.
dispose : Called when the widget is permanently removed; releases resources and sets mounted to false.
Important Concepts (Not Lifecycle Methods)
mounted : Indicates whether the State is currently attached to the widget tree.
dirty : The State needs rebuilding on the next frame.
clean : The State is up‑to‑date and will not rebuild.
Four Main Phases
Initialization: createState and initState.
Creation: didChangeDependencies and build.
Update: Multiple build calls triggered by didChangeDependencies, setState, or didUpdateWidget.
Destruction: deactivate and dispose.
Component First‑Load Process
The following code demonstrates a simple counter widget and logs each lifecycle method to verify the order.
import 'package:flutter/material.dart';
class CountWidget extends StatefulWidget {
CountWidget({Key key}) : super(key: key);
@override
_CountWidgetState createState() {
print('count createState');
return _CountWidgetState();
}
}
class _CountWidgetState extends State<CountWidget> {
int _count = 0;
void _incrementCounter() {
setState(() {
print('count setState');
_count++;
});
}
@override
void initState() {
print('count initState');
super.initState();
}
@override
void didChangeDependencies() {
print('count didChangeDependencies');
super.didChangeDependencies();
}
@override
void didUpdateWidget(CountWidget oldWidget) {
print('count didUpdateWidget');
super.didUpdateWidget(oldWidget);
}
@override
void deactivate() {
print('count deactivate');
super.deactivate();
}
@override
void dispose() {
print('count dispose');
super.dispose();
}
@override
void reassemble() {
print('count reassemble');
super.reassemble();
}
@override
Widget build(BuildContext context) {
print('count build');
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('$_count', style: Theme.of(context).textTheme.headline4),
Padding(
padding: EdgeInsets.only(top: 100),
child: IconButton(
icon: Icon(Icons.add, size: 30),
onPressed: _incrementCounter,
),
),
],
),
);
}
}In main.dart we load the widget:
import 'package:flutter/material.dart';
import './pages/count_widget.dart';
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity),
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) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: CountWidget(),
);
}
}Running the app prints the first four lifecycle calls:
flutter: count createState
flutter: count initState
flutter: count didChangeDependencies
flutter: count buildPressing the + button triggers setState and build:
flutter: count setState
flutter: count buildHot‑reload (⌘ S) invokes reassemble, didUpdateWidget, and build:
flutter: count reassemble
flutter: count didUpdateWidget
flutter: count buildRemoving the widget (commenting it out) triggers reassemble, deactivate, and dispose:
flutter: count reassemble
flutter: count deactivate
flutter: count disposeWhen Does a Widget Re‑build?
Three situations cause a rebuild: setState: Direct state change within the widget. didChangeDependencies: A dependent global state (e.g., locale, theme) changes. didUpdateWidget: The parent rebuilds with a new widget instance.
Component Destruction
When a parent removes a child, the child’s deactivate runs first, followed by its dispose. Finally, the parent’s deactivate and dispose execute.
Flutter App Lifecycle
Beyond widget lifecycles, Flutter apps have lifecycle callbacks via WidgetsBindingObserver. The following example logs app‑level state changes:
import 'package:flutter/material.dart';
class AppLifecycle extends StatefulWidget {
AppLifecycle({Key key}) : super(key: key);
@override
_AppLifecycleState createState() {
print('sub createState');
return _AppLifecycleState();
}
}
class _AppLifecycleState extends State<AppLifecycle> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
print('sub initState');
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
print('didChangeAppLifecycleState');
if (state == AppLifecycleState.resumed) {
print('resumed');
} else if (state == AppLifecycleState.inactive) {
print('inactive');
} else if (state == AppLifecycleState.paused) {
print('paused');
} else if (state == AppLifecycleState.detached) {
print('detached');
}
}
@override
Widget build(BuildContext context) {
print('sub build');
return Container(child: Text('data'));
}
}The four AppLifecycleState values are:
resumed : App is visible and responding to user input (foreground).
inactive : App is in a non‑interactive state (e.g., during a phone call or when a system dialog appears).
paused : App is not visible and does not receive input (background).
detached : App is still attached to the Flutter engine but has no host view.
Other observer callbacks such as didChangeAccessibilityFeatures, didHaveMemoryPressure, didChangeLocales, and didChangeTextScaleFactor can also be overridden for special cases.
Conclusion
This article covered the lifecycle of Android activities, iOS view controllers, and especially Flutter's StatefulWidget , as well as app‑level lifecycle events. Understanding these stages helps you place initialization, business logic, and cleanup code correctly, leading to more stable and maintainable Flutter applications.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
BaiPing Technology
Official account of the BaiPing app technology team. Dedicated to enhancing human productivity through technology. | DRINK FOR FUN!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
