In Flutter, why does a hot reload not show your change to main(), initState() or a static field's initializer?
answer
- visible only if the code runs again
- reload reruns build, nothing else
- static fields are lazily initialized
- globals and statics count as state
- const or a getter reloads
basics
~20 sHot reload swaps in new code but only reruns build methods. main() and existing initState() calls already ran, and global and static fields keep their stored values as state, so those edits need a hot restart, a const field or a getter.
solid answer
~30 sA change shows after hot reload only if the new code runs again, and hot reload reruns just the widget rebuild. `main()` already ran, so a new root passed to `runApp` is ignored. Existing `State` objects keep what their old `initState` set up. Global and static fields are lazily initialized once and treated as state, so editing a `static final` list of colours leaves the old list in place; the Dart VM flags most such initializer changes as needing a hot restart. Make the field `const` (const values are treated like aliases), expose it through a getter, or hot restart.
code
dart · 21 linesimport 'package:flutter/material.dart';
class Brand {
// Edited and hot reloaded: old list kept, because the field is state.
static final List<Color> paletteFinal = [
const Color(0xFF1565C0),
const Color(0xFF2E7D32),
];
// Edited and hot reloaded: new colours shown, const is treated like an alias.
static const List<Color> paletteConst = [
Color(0xFF1565C0),
Color(0xFF2E7D32),
];
// Edited and hot reloaded: new colours shown, a getter runs on every read.
static List<Color> get paletteGetter => const [
Color(0xFF1565C0),
Color(0xFF2E7D32),
];
}go deeper
Remember that edits to main(), initState and static values need a hot restart to show up.
Explain that reload reruns only build, that statics are lazily initialized state, and why const and getters reload while static final does not.
Recognise reload artefacts quickly, including the unflagged final-global case, and structure configuration so designers can iterate with hot reload.
Set conventions, such as const theme tokens and getters for derived values, that keep the team's edits hot-reloadable without distorting the architecture.
## The rule behind every "hot reload did nothing" After a hot reload the new code is in the VM, but a change is only **visible if that code runs again**. Hot reload re-executes one thing: the rebuild of the existing widget tree. Anything that ran once in the past and is not part of a `build` does not run again. The Flutter docs sum it up: if the modified code is **downstream of the root widget's `build()`**, hot reload behaves as expected; if it will not be re-executed by rebuilding the tree, you will not see it. ## Three common cases 1. **`main()`**. Changing the widget passed to `runApp` has no effect after a hot reload, because `main()` already ran and the tree is rebuilt with the old root instance. 2. **`initState()`**. The existing `State` objects were initialised before the edit and are kept, so the new `initState` code does not run for them. A `State` created after the reload, for example on a newly opened route, does run the new code. 3. **Global and static field initializers**. Hot reload treats these as **state**, not code, and does not reinitialise them. ## The static list of colours Suppose a palette is declared like this and read by several widgets: ```dart class Brand { static final List<Color> palette = [ const Color(0xFF1565C0), const Color(0xFF2E7D32), ]; } ``` You change the first colour and hot reload. Nothing changes. Dart **static fields are lazily initialized**: the first read evaluated the initializer and stored the list, and hot reload keeps that stored value. The docs note that the Dart VM detects changed initializers and, for most of them, flags that a hot restart is needed; the old value stays in place either way. There is one subtle exception that follows from laziness: a static field that had **never been read** before the reload has no stored value yet, so its first read after the reload runs the new initializer. ## Ways to make it reload-friendly | Declaration | After editing the value | Why | |---|---|---| | `static final palette = [...]` | Old value kept | Initialized once, treated as state | | `static const palette = [...]` | New value shown | `const` fields are treated like aliases, not state | | `static List<Color> get palette => [...]` | New value shown | A getter is code and runs on every read | | Value computed in `build` | New value shown | `build` reruns on every hot reload | So the fixes are: - make the field `const` when every element is a constant (`Color` has a `const` constructor); - or expose it through a **getter**; - or accept it and **hot restart**, which reruns all initializers. The docs show a sharper edge: with `const foo = 1; final bar = foo;`, changing `foo` to `2` updates `foo` after a reload but not `bar`, because `bar` is a `final` global whose initializer is not rerun, and this particular pattern is **not** flagged by the VM. Making `bar` `const` or a getter fixes it. ## `initState` in practice For values set up in `initState` (a controller's initial value, a subscription, a computed default): - hot restart, or navigate away and back so that a **new** `State` is created; - or move pure computations that depend only on configuration into `build` or a getter, where they rerun; - do not move expensive work into `build` just for hot reload's sake; controllers and subscriptions belong in `initState`. ## Takeaway for interviews Explain the mechanism, not a list of exceptions: hot reload swaps code but keeps state, and **static and global values, `main()`, and existing `State` initialisation are all state that already exists**. Once that is clear, every "it didn't update" case follows.
- A static final field was edited but had never been read before the hot reload. What value does it have after the reload?The new one. Static fields are lazily initialized, so a field never read has no stored value; its first read after the reload evaluates the edited initializer. Only fields already read keep their old value.
- You changed initState code for a screen, hot reloaded, then opened that screen again from the menu. Do you see the change?Yes, if opening it creates a new `State`. A newly created `State` runs the current `initState`. Only `State` objects that existed before the reload keep the old setup.
saying these in an interview costs you the question
- Hot reload reruns initState for every State in the tree.
- A static final list is re-evaluated on each hot reload.
- If hot reload reports success, every edit is now visible.
- Making the field final instead of const lets reload pick it up.
- Changing the widget passed to runApp shows after a hot reload.