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.
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: buildPressing the "切换随机数" button triggers setState and prints:
flutter: buildPerforming a hot‑reload prints:
flutter: reassemble
flutter: didUpdateWidget
flutter: buildFor 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).
360 Tech Engineering
Official tech channel of 360, building the most professional technology aggregation platform for the brand.
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.