You save your file. Nothing changes. You press hot reload again. Still nothing. So you hit hot restart — and suddenly the app works, but your counter reset to zero and you lost your place three screens deep. Sound familiar?
This is the single most common frustration for Flutter beginners, and it comes down to one misunderstanding: hot reload and hot restart are not interchangeable. They do fundamentally different things. Once you understand the difference — really understand it — you will know exactly which one to reach for every single time, and you will stop losing state by accident.
This guide covers everything: what each tool actually does under the hood, every situation where hot reload silently fails, when you must use hot restart, the keyboard shortcuts, the most common beginner mistakes with ❌/✅ code pairs, and six interview questions you will almost certainly be asked.
- Flutter installed and a working app running in debug mode (simulator or device)
- Basic understanding of Stateful vs Stateless widgets — especially what
setState()andinitState()do - Familiarity with running
flutter runfrom your terminal
1. The Core Difference (One Sentence Each)
Hot reload injects your updated Dart code into the running app and rebuilds the widget tree — without restarting the app or touching its current state.
Hot restart stops the running app, compiles your latest code, and relaunches it from main() — as if you pressed the Play button for the first time.
That is the entire distinction. Everything else — when each one works, when each one fails, why your counter reset — follows directly from those two definitions.
2. Quick Comparison Table
| Feature | 🔄 Hot Reload | 🔁 Hot Restart |
|---|---|---|
| Speed | Sub-second for most UI changes | Slower — typically 2–5 seconds |
| App state | ✅ Preserved | ❌ Reset to initial values |
Re-runs main() | ❌ No | ✅ Yes |
Re-runs initState() | ❌ No | ✅ Yes |
| Rebuilds widget tree | ✅ Yes (from root) | ✅ Yes (fresh) |
| Resets global variables | ❌ No | ✅ Yes |
| Applies native code changes | ❌ No | ❌ No (need full restart) |
| Works in release mode | ❌ Debug only | ❌ Debug only |
| Terminal shortcut | r | R (capital) |
| Best for | UI tweaks, text, colors, layout, styling | Startup logic, initState, global state, dependency injection |
3. What Hot Reload Does — And How It Works
When you trigger a hot reload, Flutter compiles your changed Dart files and injects the updated bytecode directly into the already-running Dart VM. It then calls build() on every widget from the root down, so your UI reflects the new code.
The critical word is inject. The app never stops. The Dart VM keeps running. All variables that were holding values keep their values. All controllers, streams, and navigation history stay intact. Only the widget tree is rebuilt.
If you have used browser dev tools where changing CSS updates the page without reloading it — hot reload is the Dart equivalent. The app is still running; you are just updating the presentation layer.
Here is a mental model of what happens step by step:
1. You save your Dart file
2. Flutter's tooling detects the change
3. Only the changed files are recompiled
4. Updated bytecode is injected into the running Dart VM
5. Flutter calls build() on the root widget downward
6. Your UI updates — your state is untouched
4. Hot Reload: Best Use Cases with Code Examples
Hot reload shines for anything that lives purely in the widget build() method. Here are the concrete use cases with runnable examples.
Changing UI text, colors, and sizing
Run the app below, tap the button a few times to increment the counter, then change "Hot Reload Demo" to "My Updated App" and change Colors.blue to Colors.green. Save. The title and color update instantly — and your counter value stays exactly where it was.
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(home: CounterPage()));
}
class CounterPage extends StatefulWidget {
const CounterPage({super.key});
@override
State<CounterPage> createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Hot Reload Demo'), // ← change this text, hot reload
backgroundColor: Colors.blue, // ← change this color, hot reload
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Count: $count',
style: const TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () => setState(() => count++),
child: const Text('Tap to Increase'),
),
],
),
),
);
}
}
Iterating on a screen deep in the navigation stack
Without hot reload you would need to: stop the app → edit the code → restart → tap through multiple screens. With hot reload you stay on that screen and iterate freely:
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(home: ScreenOne()));
}
class ScreenOne extends StatelessWidget {
const ScreenOne({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Screen 1')),
body: Center(
child: ElevatedButton(
onPressed: () => Navigator.push(
context, MaterialPageRoute(builder: (_) => const ScreenTwo()),
),
child: const Text('Go to Screen 2'),
),
),
);
}
}
class ScreenTwo extends StatelessWidget {
const ScreenTwo({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Screen 2')),
body: Center(
child: ElevatedButton(
onPressed: () => Navigator.push(
context, MaterialPageRoute(builder: (_) => const ScreenThree()),
),
child: const Text('Go to Screen 3'),
),
),
);
}
}
class ScreenThree extends StatelessWidget {
const ScreenThree({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Screen 3')),
body: const Center(
// Navigate here once, then freely edit this widget and hot reload.
// You stay on Screen 3 — no need to navigate back every time.
child: Text(
'Edit me freely with hot reload!',
style: TextStyle(fontSize: 24, color: Colors.deepPurple), // ← tweak
),
),
);
}
}
In VS Code, go to Settings → Extensions → Flutter and enable Hot Reload On Save. Now every Cmd+S / Ctrl+S automatically triggers hot reload. You barely need to think about it — the UI just stays in sync as you type.
5. When Hot Reload Silently Fails
This is the section most beginners never read — and it explains almost every “hot reload didn’t work” complaint in forums or on Stack Overflow. Flutter reports a successful reload even when none of your visible changes took effect.
| Situation | Why hot reload won’t show it | Fix |
|---|---|---|
Changed main() | main() is not re-executed | Hot restart |
Changed initState() | initState() is not re-executed | Hot restart |
| Changed a global variable’s initial value | Global variables already initialized in memory | Hot restart |
| Changed a static field initializer | Static fields initialized once and held | Hot restart |
| Changed an enum definition | Structural type changes need a full re-run | Hot restart |
| Changed native Kotlin / Swift / Java code | Hot reload only covers Dart code | Full stop + run |
Added a new asset or font to pubspec.yaml | Asset bundles compiled at startup | Full stop + run |
Flutter will print
Reloaded X of Y libraries in Z ms. even when none of your visible changes took effect. If the code you changed is in initState() or a global initializer, hot reload will look like it worked but show nothing different.
6. What Hot Restart Does — And When You Need It
Hot restart discards the current VM state entirely, compiles your latest Dart code, and runs the app from main() again — exactly like pressing the Play button. Everything in memory is gone: all variable values return to their declared initial values, navigation history is cleared, and every widget’s initState() runs fresh. Hot restart still completes in 2–5 seconds because it skips rebuilding the native shell.
7. Hot Restart: Best Use Cases with Code Examples
Changes to initState()
Run this app and watch the debug console. Tap the button a few times. Then change the string assigned in initState() and hot reload — nothing happens. Now press R (hot restart) — the updated message appears immediately and the counter resets to 0:
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(home: RestartDemoPage()));
}
class RestartDemoPage extends StatefulWidget {
const RestartDemoPage({super.key});
@override
State<RestartDemoPage> createState() => _RestartDemoPageState();
}
class _RestartDemoPageState extends State<RestartDemoPage> {
int count = 0;
String welcomeMessage = '';
@override
void initState() {
super.initState();
// Try changing this string and hot reloading — nothing happens.
// Hot restart (R) will apply the change.
welcomeMessage = 'App started!';
debugPrint('initState ran: $welcomeMessage');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Hot Restart Demo')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(welcomeMessage, style: const TextStyle(fontSize: 18)),
const SizedBox(height: 16),
Text('Count: $count', style: const TextStyle(fontSize: 32, fontWeight: FontWeight.bold)),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () => setState(() => count++),
child: const Text('Increase'),
),
],
),
),
);
}
}
Changes to global variables
import 'package:flutter/material.dart';
// Global variable — hot reload will NOT update this initial value.
// Change 'Guest' to 'Alice' and hot reload: still shows 'Guest'.
// Hot restart: correctly shows 'Alice'.
String currentUser = 'Guest';
void main() {
runApp(const MaterialApp(home: GlobalVarDemo()));
}
class GlobalVarDemo extends StatelessWidget {
const GlobalVarDemo({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Global Variable Demo')),
body: Center(
child: Text(
'Logged in as: $currentUser',
style: const TextStyle(fontSize: 22),
),
),
);
}
}
Changes to app-level configuration in main()
import 'package:flutter/material.dart';
void main() {
// Any change here requires hot restart — not hot reload.
WidgetsFlutterBinding.ensureInitialized();
runApp(
MaterialApp(
// Change this seed color — hot reload will NOT apply it.
// Press R to hot restart and see the new theme.
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(),
),
);
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Text(
'Change the theme seed color above and hot restart',
textAlign: TextAlign.center,
),
),
);
}
}
If you hot reload many times without ever doing a hot restart, stale state can accumulate — old variable values, controllers that were never disposed, or widget trees in inconsistent states. If your app starts behaving oddly after many reloads, a hot restart is almost always the fastest fix.
8. Keyboard Shortcuts and IDE Buttons
| Action | Terminal (flutter run) |
VS Code | Android Studio |
|---|---|---|---|
| Hot Reload | r (lowercase) | ⚡ Lightning bolt, or save with auto-reload on | 🔄 Hot Reload button |
| Hot Restart | R (uppercase) | 🔁 Restart button in debug toolbar | 🔁 Hot Restart button |
| Full Stop + Run | q then flutter run | ⏹ Stop, then ▶ Run | ⏹ Stop, then ▶ Run |
| Show all commands | h | — | — |
Lowercase r = hot reload. Uppercase R = hot restart. When in doubt: the bigger action (restart) gets the bigger letter (capital R).
9. Hot Restart vs Full Restart — Not the Same Thing
| Type | What it restarts | Speed | When you need it |
|---|---|---|---|
| Hot Restart | Dart VM only. Native shell stays running. | 2–5 seconds | Dart-only changes that affect startup or state |
| Full Restart | Everything — Dart + native Android/iOS layers | 30–120 seconds | Native code changes, new plugins, pubspec asset changes |
10. Common Beginner Mistakes
Mistake 1: Expecting hot reload to apply initState() changes
❌ Changing initState() and pressing hot reload — the change is silently ignored:
@override
void initState() {
super.initState();
// You changed 'App started!' to 'Welcome back!' and hot reloaded.
// Nothing happens — initState() does not re-run on hot reload.
welcomeMessage = 'Welcome back!';
}
✅ Press R (hot restart) when you change anything inside initState():
@override
void initState() {
super.initState();
welcomeMessage = 'Welcome back!';
// After saving this, press R in the terminal or click Hot Restart.
// initState() runs fresh and the new message appears immediately.
}
Mistake 2: Hot restarting when you only changed a visual — losing all state
❌ Changed a button color but pressed R — counter reset to 0 unnecessarily:
ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red), // changed color
onPressed: () => setState(() => count++),
child: const Text('Tap'),
)
// You pressed R — count reset to 0 unnecessarily.
✅ For purely visual changes, always reach for lowercase r (hot reload) first:
ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red), // changed color
onPressed: () => setState(() => count++),
child: const Text('Tap'),
)
// Press r (lowercase) — color updates instantly, count stays where it was.
Mistake 3: Using hot restart after adding a new plugin
# ❌ pubspec.yaml — you just added this plugin and tried hot restart:
dependencies:
shared_preferences: ^2.2.2 # NEW — hot restart will NOT pick this up
# ✅ Stop the app first (q), then:
# flutter pub get
# flutter run
Mistake 4: Hot reloading after changing a global variable’s initial value
// ❌ You change this to 100 and hot reload. The app still shows 0.
int startingScore = 0;
// ✅ Change the value, then press R (hot restart).
// The Dart VM starts fresh and reads the new initial value.
int startingScore = 100;
11. Interview Q&A
These are the questions you will be asked in Flutter interviews as a beginner or junior developer.
A: Hot reload injects updated Dart bytecode into the running app and rebuilds the widget tree, preserving all current state. Hot restart discards the running Dart VM, re-runs
main(), and rebuilds everything from scratch — resetting all state. Hot reload is faster and best for UI changes; hot restart is necessary when changes affect app startup or initialization logic.
main() or initState()?A: No, neither. Hot reload injects code without re-running the application entry point or widget lifecycle methods that already ran. This is by design — it is what allows state to be preserved. If your change is in
main() or initState(), you must use hot restart.
A: Use hot restart when: (1) you changed code in
main() or initState(), (2) you changed a global variable’s initial value or a static field initializer, (3) you changed an enum definition or generic type parameters, or (4) the app is behaving inconsistently after many hot reloads.
A: No. Both only apply to Dart code. Native code changes in Kotlin, Java, Swift, or Objective-C require a full stop and re-run with
flutter run. The same applies to new plugins added in pubspec.yaml.
A: Flutter reports a successful reload even when the changed code is not part of the widget rebuild path. This happens when the change is in
initState(), main(), a global variable, or any code that only runs at app startup. The fix is hot restart.
A: No. Both are debug-mode-only features. In release mode (
flutter run --release), the Dart VM is replaced by AOT-compiled native code, which cannot accept injected updates.
12. Related Posts
| Post | Why it’s relevant |
|---|---|
| Flutter Widgets Explained: Stateless vs Stateful | Understand initState() and setState() — the exact lifecycle methods that determine when hot reload is enough vs when you need hot restart. |
| Flutter Tutorial for Beginners: From Install to First App | Where you first encounter hot reload in a real running app. Follow this to have a working project to experiment with all the examples in this post. |
| Handling User Input: TextField, Forms and Validation | Hot reload keeps your typed text and validation state intact as you tweak form UI — exactly when you need it most. |
| Building a Notes App in Flutter – Part 1 | A hands-on project where you will use hot reload constantly — the best practice for applying what you learned here. |
| Flutter for Beginners in 2026: Everything You Need to Know | The big picture guide — where hot reload and hot restart fit into the overall Flutter development workflow. |

Pingback: Build a Flutter Tic-Tac-Toe Game from Scratch (Beginner Tutorial)