Understanding initState() vs didChangeDependencies() in Flutter — With Clear Examples
FlutterPulseThis article was translated specially for the channel FlutterPulseYou'll find lots of interesting things related to Flutter on this channel. Don't hesitate to subscribe!🚀

When building Flutter apps, two lifecycle methods often confuse beginners: initState() and didChangeDependencies().
Although both run…
When building Flutter apps, two lifecycle methods often confuse beginners: initState() and didChangeDependencies().
Although both run when a widget starts, they serve different purposes and behave differently.
In this article, I will explain them in simple English with real examples so you can understand exactly when to use each one.
🟦 What Is initState()?
initState() is the first method that runs when a StatefulWidget is created.
🔸 When does it run?
✔ Only one time — when the widget is inserted into the widget tree.
✔ Before the widget is displayed on the screen.
🔸 What is it used for?
Use initState() when you want to:
- Initialize controllers
(TextEditingController, ScrollController, AnimationController…) - Initialize variables
- Make API calls that do NOT depend on the context
- Start animations
- Subscribe to streams
🔸 Important rule
You cannot use context here for
Provider.of, BlocProvider.of, MediaQuery.of, etc.
because the widget dependencies are not ready yet.
You can only use context for things like Navigator.of(context).
🟦 Example of initState()
Here is a simple example where we initialize a controller:
class ProfileScreen extends StatefulWidget {
@override
_ProfileScreenState createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen> {
late TextEditingController nameController;
@override
void initState() {
super.initState();
print("initState called");
// Initialize the controller
nameController = TextEditingController(text: "Wassim");
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: TextField(controller: nameController),
),
);
}
}🟩 What Is didChangeDependencies()?
didChangeDependencies() runs after initState(), and it also runs whenever something the widget depends on changes.
🔸 When does it run?
✔ Runs right after initState()
✔ Runs again when dependencies change, like:
- Theme changes
- Locale (language) changes
- Provider values update
- InheritedWidget updates
- When using
context.watch()orcontext.dependOnInheritedWidgetOfExactType()
and the data changes
🔸 What is it used for?
Use didChangeDependencies() when you want to:
- Access data from Providers / Blocs
- Access MediaQuery, Theme, Localizations
- Fetch data based on something from the context
This method is 100% safe for context operations.
🟩 Example of didChangeDependencies()
Here's a simple example where we read data from a Provider:
class DashboardScreen extends StatefulWidget {
@override
_DashboardScreenState createState() => _DashboardScreenState();
}
class _DashboardScreenState extends State<DashboardScreen> {
late String userName;
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("didChangeDependencies called");
// Accessing provider here is safe
userName = Provider.of<UserProvider>(context).name;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text("Hello $userName"),
),
);
}
}🟧 Key Differences Between initState() and didChangeDependencies()
Here is a simple comparison table:
FeatureinitState()didChangeDependencies()Runs once?✅ Yes❌ NoCan run again?❌ No✅ Yes, when dependencies changeSafe to use context?⚠️ Limited✅ Fully safeUsed forInitialization, controllers, simple API callsReading Providers, Theme, MediaQueryCalled before build?YesYesCalled before didChangeDependencies?YesNo
🟩 Real Use Case Example — API Call Depending on Theme or User
Imagine you want to call an API based on something from the Provider.
You cannot do this in initState() because Provider is not ready yet.
Correct place:
@override
void didChangeDependencies() {
super.didChangeDependencies();
final userId = Provider.of<UserProvider>(context).id;
fetchUserData(userId);
}
🔥 Which One Should You Use?
Use initState() if:
- You want to initialize values
- You want to create controllers
- Your work does NOT depend on context
Use didChangeDependencies() if:
- You need something from Provider / Bloc
- You need MediaQuery or Theme
- You need to listen to dependencies that can update
🎯 Final Summary
Here is the simplest explanation:
initState()→ I run only one time when the widget is created. I'm used to initialize things.didChangeDependencies()→ I run after initState() and again whenever something above the widget changes. I'm used to read context-based data safely.