← Back to blog
Flutter Fundamentals · Part 9 of 9
July 23, 202628 min read

100 Questions to Master Flutter Fundamentals

FlutterDart

100 Questions to Master Flutter Fundamentals

This is Part 9 — the finale of the Flutter Fundamentals series. The previous eight parts taught the concepts; this is where you prove you own them.

How to use this bank:

  • 100 questions, grouped by topic, tagged [Basic], [Medium], [Advanced] and marked (Theory) or (Coding).
  • Each has a Hint (the idea / where to look) and a separate Solution (the actual answer). Try cold first, peek at the hint if stuck, then check the solution.
  • For (Coding) questions, run your code — dartpad.dev runs Flutter in the browser with zero setup.
  • After the 100 there are 10 coding mini-exercises with full solutions, ending in a capstone app that ties the series together.

If you can explain the why on all 100 without hints, you genuinely understand how Flutter works. Let's go.


Section A — What is Flutter & architecture (Q1–12)

Q1. [Basic] (Theory) What's the single biggest difference between Flutter and React Native?

Hint

Who draws the UI? Part 1.

Solution

Flutter draws its own UI with a bundled engine (Impeller/Skia) instead of wrapping native widgets via a bridge. No native-UI bridge → consistent, fully-controllable UI across platforms.

Q2. [Basic] (Theory) What does "everything is a widget" mean?

Hint

Tree + composition.

Solution

The entire UI — structure, layout, styling, the app itself — is a tree of widgets you build by composing small, single-purpose widgets (padding, centering, color are each their own widget you wrap around children).

Q3. [Basic] (Theory) What language does Flutter use, and what renders the pixels?

Hint

Dart + an engine.

Solution

You write Dart; the engine (C++) rasterizes pixels via Impeller (modern) or Skia, talking nearly directly to the GPU.

Q4. [Basic] (Theory) Why does Flutter look identical across OS versions?

Hint

It ships its own widgets.

Solution

Because it doesn't depend on the platform's system widgets (which change between OS versions) — it ships its widget set with your app and draws everything itself.

Q5. [Medium] (Theory) Name Flutter's main architectural layers.

Hint

Framework / engine / embedder.

Solution

Framework (Dart: widgets, rendering, animation, gestures), Engine (C++: Impeller/Skia, Dart runtime, text, I/O), Embedder (platform-specific: surface + input/lifecycle). Each is replaceable.

Q6. [Medium] (Theory) What's the difference between how Flutter compiles in dev vs release?

Hint

JIT vs AOT.

Solution

Dev uses JIT (Dart VM) → enables hot reload. Release uses AOT → native machine code, fast startup, no interpreter.

Q7. [Medium] (Theory) What does UI = f(state) express?

Hint

Declarative.

Solution

The UI is a pure function of state: you describe the UI for the current state in build, and when state changes Flutter recomputes the UI. Declarative, not imperative mutation.

Q8. [Medium] (Coding) Write the minimal app showing centered "Hi".

Hint

runApp → MaterialApp → Scaffold → Center.

Solution
import 'package:flutter/material.dart';
void main() => runApp(const MaterialApp(
  home: Scaffold(body: Center(child: Text('Hi'))),
));

Q9. [Medium] (Coding) Center a Text with 16px padding by composition only.

Hint

Wrap, don't configure.

Solution
Center(child: Padding(padding: const EdgeInsets.all(16), child: Text('Hi')));

Q10. [Advanced] (Theory) What's the trade-off of Flutter's self-rendering approach, and how is it mitigated?

Hint

Native look vs consistency.

Solution

Trade-off: widgets don't automatically adopt each OS's exact native look/conventions (Flutter favors identical everywhere). Mitigated by shipping Material and Cupertino design systems + full customization.

Q11. [Advanced] (Theory) Why is there no native-UI "bridge" overhead in Flutter?

Hint

No native widgets to talk to.

Solution

Because Flutter doesn't use native UI components, there's nothing to serialize/marshal across a bridge for rendering — it composites the whole scene itself and pushes it to the GPU. (Platform channels exist for non-UI native calls, but the UI hot path has no bridge.)

Q12. [Advanced] (Theory) How can the same Flutter framework run on mobile, web, and desktop?

Hint

Replaceable bottom layers.

Solution

The framework sits on a swappable engine + embedder. Porting to a new platform means providing an embedder (surface, input, lifecycle) and engine backend; the Dart framework code stays the same. (On web, Dart compiles to JS/WASM with a web renderer.)


Section B — The three trees (Q13–25)

Q13. [Basic] (Theory) Name the three trees and one-line jobs.

Hint

Blueprint / bridge / worker. Part 2.

Solution

Widget = immutable description (recreated constantly). Element = persistent bridge holding State + position, decides update vs replace. RenderObject = layout/paint/hit-test.

Q14. [Basic] (Theory) Which tree is immutable and rebuilt constantly?

Hint

The cheap one.

Solution

The Widget tree — immutable blueprints recreated on every rebuild.

Q15. [Basic] (Theory) Which tree is persistent across frames?

Hint

Holds State.

Solution

The Element tree (and the RenderObjects). It persists and is mutated in place, which is why rebuilds are cheap.

Q16. [Medium] (Theory) Where does a StatefulWidget's State live, and why does it survive rebuilds?

Hint

Not on the widget.

Solution

On the Element, which is persistent. Widgets are recreated and discarded each rebuild, but the Element (and its State) is kept alive — so the data survives.

Q17. [Medium] (Theory) On rebuild, how does Flutter decide reuse vs replace at a position?

Hint

Type + key.

Solution

It compares the new widget with the old: same runtimeType and key → reuse the Element (update RenderObject in place). Different → replace the Element/RenderObject (State lost).

Q18. [Medium] (Coding) Reuse or replace? (a) Text('a')Text('b'); (b) Text('a')Icon(...).

Hint

Same type?

Solution

(a) Reuse (same Text type, text updates). (b) Replace (TextIcon, different type).

Q19. [Medium] (Theory) Does every widget create a RenderObject?

Hint

Structural vs render widgets.

Solution

No. Only render widgets (Padding, Center, Text, Opacity…) do. StatelessWidget/StatefulWidget just build others, so the RenderObject tree is shorter than the widget tree.

Q20. [Medium] (Theory) What is BuildContext?

Hint

It's an Element.

Solution

BuildContext is the Element — the widget's position in the live tree. That's why it can look up ancestors (Theme.of, Navigator.of).

Q21. [Advanced] (Theory) Why does reordering same-typed list items sometimes attach state to the wrong item?

Hint

Matching by position.

Solution

Element matching is by position + type. Reordering same-typed widgets keeps the Elements (and their State) in place while the widgets swap, so State sticks to the position, not the logical item.

Q22. [Advanced] (Coding) Fix the reorder-state bug.

Hint

Give identity.

Solution

Add a stable Key: Tile(key: ValueKey(item.id), ...). The key lets Element matching follow logical identity across reordering.

Q23. [Advanced] (Theory) Why does the three-tree design make rebuilds cheap?

Hint

Disposable vs cached.

Solution

Widgets (cheap descriptions) are thrown away and recreated, but the expensive Elements/RenderObjects are reused and mutated in place — so rebuilding thousands of widgets just updates existing machinery.

Q24. [Advanced] (Theory) When do Keys actually matter?

Hint

Same type, moving.

Solution

Only when widgets of the same type change position in a list/collection (and you need to preserve their identity/State). For static, non-moving trees, keys usually aren't needed.

Q25. [Advanced] (Theory) Two Text widgets swap places in a Row with no keys. What happens to their Elements?

Hint

Matched by slot.

Solution

The Elements stay in their slots and are simply handed the other's widget config (they're the same type), so nothing visibly breaks for stateless Text — but if they held State, the State would stay with the position, not the content. Keys fix that.


Section C — Stateless vs Stateful (Q26–38)

Q26. [Basic] (Theory) One-line difference between Stateless and Stateful?

Hint

Memory. Part 3.

Solution

Stateless renders from immutable inputs and can't change itself. Stateful has a State object that holds changing data and can rebuild via setState.

Q27. [Basic] (Theory) Why does a StatefulWidget need two classes?

Hint

Immutable widget + durable data.

Solution

The widget must stay immutable (recreated each rebuild), so a separate State object holds the mutable data and persists on the Element.

Q28. [Basic] (Coding) Write a toggle that shows ON/OFF.

Hint

bool + setState.

Solution
class _S extends State<Toggle> {
  bool on = false;
  @override
  Widget build(c) => TextButton(
    onPressed: () => setState(() => on = !on),
    child: Text(on ? 'ON' : 'OFF'));
}

Q29. [Basic] (Theory) What two things does setState do?

Hint

Mutate + schedule.

Solution

(1) Runs your callback where you mutate state, (2) marks the Element dirty to schedule a rebuild on the next frame.

Q30. [Medium] (Theory) What's the classic setState mistake?

Hint

Outside the callback.

Solution

Mutating state outside the setState callback — the variable changes but no rebuild is scheduled, so the UI silently goes stale.

Q31. [Medium] (Theory) What runs in initState vs dispose?

Hint

Set up vs tear down.

Solution

initState (once, on creation): set up controllers, subscriptions, initial fetch. dispose (once, on removal): tear those down. They pair to avoid leaks.

Q32. [Medium] (Coding) Fix the leak: a stream subscribed in initState.

Hint

Cancel where?

Solution
@override
void dispose() { _sub.cancel(); super.dispose(); }

Anything subscribed/created in initState must be cleaned up in dispose.

Q33. [Medium] (Theory) When is didUpdateWidget called and what's it for?

Hint

New config from parent.

Solution

When the parent rebuilds and gives this State a new widget configuration. Use it to react to changed input props (if (widget.id != oldWidget.id) refetch();).

Q34. [Medium] (Coding) Why doesn't this update the UI?

class C extends StatelessWidget {
  int n = 0;
  Widget build(c) => TextButton(onPressed: () => n++, child: Text('$n'));
}
Hint

No State, no setState.

Solution

A StatelessWidget has no State/setState, so mutating n never schedules a rebuild; and the widget is recreated (resetting n). It must be a StatefulWidget.

Q35. [Medium] (Theory) Default to Stateless or Stateful, and why?

Hint

Simpler is better.

Solution

Default to Stateless — simpler, cheaper, easier to reason about. Use Stateful only when you have local changing data or a lifecycle (controller/subscription) to manage.

Q36. [Advanced] (Theory) Why must you guard setState with mounted after an await?

Hint

Widget may be gone.

Solution

After an await, the widget may have been disposed (user navigated away). Calling setState on a disposed State throws. Guard with if (!mounted) return;.

Q37. [Advanced] (Theory) A counter is shared by three sibling widgets. Best design?

Hint

One source of truth.

Solution

Lift the state up to a common ancestor (or a state-management solution) and pass it down to Stateless children — single source of truth, no drift.

Q38. [Advanced] (Coding) An animation needs to run for 2s. Stateless or Stateful, and what extra do you manage?

Hint

Controller lifecycle.

Solution

Stateful — create an AnimationController in initState (with a TickerProvider via SingleTickerProviderStateMixin) and dispose it in dispose. The animation rebuilds the subtree each frame.


Section D — The build method (Q39–50)

Q39. [Basic] (Theory) What is build's job?

Hint

Describe, don't do. Part 4.

Solution

Return a widget subtree describing the UI for the current state (UI = f(state)). It must be fast and side-effect-free.

Q40. [Basic] (Theory) Does build paint pixels?

Hint

Just blueprints.

Solution

No — it constructs immutable widget blueprints; Elements reconcile and RenderObjects paint.

Q41. [Basic] (Theory) Name three triggers that call build.

Hint

setState, ancestor, inherited.

Solution

setState; an ancestor rebuilding; an InheritedWidget it depends on changing (Theme/MediaQuery/Provider). Also: first mount, new parent config, animation frames, hot reload.

Q42. [Medium] (Coding) Fix the duplicate-request bug in build.

Widget build(c) => FutureBuilder(future: api.get(), builder: ...);
Hint

Create once.

Solution
late final _f = api.get(); // or assign in initState
Widget build(c) => FutureBuilder(future: _f, builder: ...);

Creating the future inside build re-fires on every rebuild.

Q43. [Medium] (Theory) Why must build be side-effect-free?

Hint

Called constantly.

Solution

It can run 60+ times a second (animations, ancestor rebuilds, inherited changes). Side effects (I/O, setState, timers) would fire repeatedly, causing duplicate work and bugs.

Q44. [Medium] (Theory) What does marking a subtree const buy you?

Hint

Skipped on rebuild.

Solution

A const widget is created once and reused/skipped on rebuild instead of being reconstructed — free performance for unchanging subtrees.

Q45. [Medium] (Coding) During a 1s 60fps animation with nothing else changing, ~how many builds on the animated subtree?

Hint

Per frame.

Solution

~60 — once per frame. Hence build must be cheap.

Q46. [Medium] (Theory) What scope does setState rebuild?

Hint

Not the whole app.

Solution

Only the calling widget and its descendants — targeted, not app-wide.

Q47. [Advanced] (Theory) A setState high in the tree janks. Two fixes?

Hint

Push down + const.

Solution

(1) Push state down — extract the changing bit into its own widget and setState there (rebuild a small subtree). (2) Mark unchanging subtrees const. (Also: lazy ListView.builder.)

Q48. [Advanced] (Theory) How does Theme.of(context) cause rebuilds?

Hint

Registers a dependency.

Solution

.of(context) registers the widget as a dependent of that InheritedWidget. When the Theme changes, Flutter rebuilds all dependents automatically.

Q49. [Advanced] (Coding) Where should a one-time data fetch go, not build?

Hint

Lifecycle.

Solution

In initState (store the Future), then reuse it via FutureBuilder in build. build describes; initState/handlers do work.

Q50. [Advanced] (Theory) Why use ListView.builder over a ListView with 10,000 children?

Hint

Lazy.

Solution

ListView.builder is lazy — it builds only the items currently visible (plus a small buffer), not all 10,000, keeping each frame cheap. A plain ListView with all children builds them all upfront.


Section E — Layouts I: Row, Column & flex (Q51–63)

Q51. [Basic] (Theory) Row vs Column?

Hint

Direction. Part 5.

Solution

Row lays children out horizontally; Column vertically. Same flex concept, rotated 90°.

Q52. [Basic] (Theory) For a Column, which axis is the main axis?

Hint

Vertical.

Solution

The vertical axis. So mainAxisAlignment moves children up/down; crossAxisAlignment moves them left/right.

Q53. [Basic] (Coding) Put "Back" far-left and "Next" far-right in a Row.

Hint

spaceBetween / Spacer.

Solution
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,
    children: const [Text('Back'), Text('Next')]);

Q54. [Basic] (Theory) What does crossAxisAlignment: stretch do?

Hint

Fill perpendicular.

Solution

Forces children to fill the cross axis (e.g. full width in a Column).

Q55. [Medium] (Theory) What does Expanded do?

Hint

Take leftover.

Solution

Forces the child to fill all remaining main-axis space (ignoring its natural size). Multiple Expanded split space by flex ratio.

Q56. [Medium] (Coding) Two boxes splitting width 2:1.

Hint

flex.

Solution
Row(children: [
  Expanded(flex: 2, child: Container(color: Colors.red, height: 40)),
  Expanded(flex: 1, child: Container(color: Colors.blue, height: 40)),
]);

Q57. [Medium] (Theory) Expanded vs Flexible?

Hint

tight vs loose.

Solution

Expanded = Flexible(fit: tight) — must fill its share. Flexible (loose) takes only as much as needed, capped at its share.

Q58. [Medium] (Theory) What's Spacer?

Hint

Flexible gap.

Solution

A flexible empty space — shorthand for Expanded(child: SizedBox()). Pushes siblings apart.

Q59. [Medium] (Coding) Fix the overflow:

Row(children: [Icon(Icons.person), Text(longName), Icon(Icons.star)]);
Hint

Confine the text.

Solution
Row(children: [
  const Icon(Icons.person),
  Expanded(child: Text(longName, overflow: TextOverflow.ellipsis)),
  const Icon(Icons.star),
]);

Q60. [Medium] (Theory) What does "RenderFlex overflowed" mean?

Hint

Not enough main-axis space.

Solution

A Row/Column's children wanted more main-axis space than available; Flutter overflows instead of shrinking. Fix with Expanded/Flexible, scrolling, or Wrap.

Q61. [Advanced] (Theory) Why does mainAxisAlignment: center sometimes do nothing?

Hint

No leftover space.

Solution

It needs leftover main-axis space to distribute. With MainAxisSize.min the flex wraps its children exactly, leaving no space — so alignment has no visible effect.

Q62. [Advanced] (Theory) What does mainAxisSize control?

Hint

Fill or wrap.

Solution

Whether the Row/Column takes the full available main-axis extent (max, default) or shrinks to wrap its children (min).

Q63. [Advanced] (Coding) When would Wrap beat Row?

Hint

Multiple lines.

Solution

When children should flow onto multiple lines when they run out of width (chips/tags), instead of overflowing. Wrap wraps to the next line; Row doesn't.


Section F — Layouts II: Stack & constraints (Q64–76)

Q64. [Basic] (Theory) What does Stack do?

Hint

Layers. Part 6.

Solution

Layers children on top of each other (later children paint over earlier). For overlaps — badges, text over images, overlays.

Q65. [Basic] (Coding) Pin a red dot to the top-right of an avatar.

Hint

Stack + Positioned.

Solution
Stack(children: [
  const CircleAvatar(radius: 30),
  Positioned(right: 0, top: 0, child: Container(width: 12, height: 12,
    decoration: const BoxDecoration(color: Colors.red, shape: BoxShape.circle))),
]);

Q66. [Basic] (Theory) Where can Positioned be used?

Hint

Parent-specific.

Solution

Only as a direct child of a Stack. It's a parent-data widget meaningful only inside a Stack.

Q67. [Medium] (Theory) State Flutter's three-part layout rule.

Hint

The famous sentence.

Solution

"Constraints go down. Sizes go up. Parent sets position." Constraints flow down; each child picks a size within them and reports up; the parent positions it.

Q68. [Medium] (Theory) Tight vs loose constraints?

Hint

min==max?

Solution

Tight: min==max — parent forces an exact size (child has no choice). Loose: min 0 — child may be any size up to max.

Q69. [Medium] (Theory) Why does Container(width: 100) fill the screen as a Scaffold body?

Hint

Tight constraints.

Solution

The body passes tight (full-screen) constraints, and a tightly-constrained widget can't choose its size — so width: 100 is overridden.

Q70. [Medium] (Coding) Fix it so the box is actually 100×100.

Hint

Loosen.

Solution
Center(child: Container(width: 100, height: 100, color: Colors.red));

Center passes loose constraints down, letting the Container be 100×100.

Q71. [Medium] (Theory) Can a widget choose its own position?

Hint

Parent decides.

Solution

No — the parent sets position. To move a widget, wrap it in Align/Center/Padding/Positioned or use a flex alignment.

Q72. [Medium] (Coding) Responsive: two columns ≥600px, else one.

Hint

LayoutBuilder.

Solution
LayoutBuilder(builder: (c, cons) =>
  cons.maxWidth < 600 ? const OneCol() : const TwoCol());

Q73. [Advanced] (Theory) Why is the constraints model O(n)?

Hint

Single pass.

Solution

Layout is one pass: constraints flow down once, sizes flow up once — each RenderObject is visited essentially once, no backtracking or multiple passes.

Q74. [Advanced] (Theory) Why does ConstrainedBox sometimes ignore its constraints?

Hint

Only adds.

Solution

ConstrainedBox can only add (tighten) constraints, not loosen incoming ones. Under tight parent constraints it passes them through, so its own min/max have no effect — wrap in Center to loosen first.

Q75. [Advanced] (Theory) Why does an empty Container fill its parent but one with a small child shrinks?

Hint

Widget's own rule.

Solution

An unconstrained, childless Container chooses to be as big as possible; given a child (with loose constraints) it sizes to the child. It's a design choice of the Container widget — the answer is always "what constraints come in, and what this widget does with them."

Q76. [Advanced] (Coding) Which tool reads incoming constraints to build adaptively?

Hint

Builder.

Solution

LayoutBuilder — its builder receives the BoxConstraints so you can branch on available space.


Section G — Hot reload vs hot restart (Q77–88)

Q77. [Basic] (Theory) Hot reload vs hot restart in one line?

Hint

State preserved? Part 7.

Solution

Hot reload injects code + rebuilds the tree preserving state (no main/initState re-run). Hot restart resets state and re-runs main/initState.

Q78. [Basic] (Theory) Does hot reload re-run initState?

Hint

No.

Solution

No — existing State objects are preserved, so initState (which runs only on creation) does not re-run. Only build re-runs.

Q79. [Basic] (Coding) You edit text inside build. Reload or restart?

Hint

build re-runs.

Solution

Hot reloadbuild re-runs, change appears instantly.

Q80. [Medium] (Theory) Why is state preserved across hot reload? (Three trees.)

Hint

Elements live.

Solution

Hot reload keeps the running app's objects — the persistent Element tree and its State — alive, swapping in new code and rebuilding. State lives on the durable Element, so it survives.

Q81. [Medium] (Coding) You change a value in initState, reload, but see the old value. Fix?

Hint

Recreate State.

Solution

Hot restart — it recreates the State and re-runs initState. Reload preserves State and skips initState.

Q82. [Medium] (Theory) Why won't edits to a top-level final myList = [...] show on reload?

Hint

Lazy once.

Solution

Static/global fields are lazily initialized once; reload doesn't re-init them. Make it const or a getter (or hot restart).

Q83. [Medium] (Theory) What change needs a full restart?

Hint

Native.

Solution

Native code (Kotlin/Java/Swift/Obj-C), new plugins, or new assets/dependencies — they need the native layer/bundle recompiled.

Q84. [Medium] (Coding) Categorize: (a) button color, (b) main(), (c) Kotlin method, (d) enum→class.

Hint

reload/restart/full/restart.

Solution

(a) hot reload; (b) hot restart; (c) full restart; (d) hot restart (type shape changed).

Q85. [Advanced] (Theory) Why does production have no hot reload?

Hint

AOT.

Solution

Release builds are AOT-compiled to native machine code, which can't accept new source at runtime. Hot reload relies on the dev JIT VM that can load new code into the running program.

Q86. [Advanced] (Theory) Why do enum→class or generic-arity changes need a restart?

Hint

Type shape.

Solution

They change the structure/shape of a type, which the running VM can't reconcile by swapping code in place — so the app must restart to rebuild type information.

Q87. [Advanced] (Theory) Even on a successful reload, why might a change still not appear?

Hint

Not re-executed.

Solution

If the modified code isn't re-executed as part of rebuilding the widget tree (e.g. it only runs in main/initState/a one-time branch), its effects won't show. Hot restart to be sure.

Q88. [Advanced] (Theory) How can state preservation across reloads hide a bug?

Hint

Stale setup.

Solution

A controller/data initialized once in initState keeps its old value across reloads, so your new setup code never runs and you don't notice a mistake — until a hot restart runs initState honestly and reveals it.


Section H — Navigation (Q89–100)

Q89. [Basic] (Theory) What data structure does Navigator manage?

Hint

LIFO. Part 8.

Solution

A stack of routes (screens). push adds on top; pop removes the top.

Q90. [Basic] (Coding) Open a SecondScreen.

Hint

push + MaterialPageRoute.

Solution
Navigator.push(context,
  MaterialPageRoute<void>(builder: (_) => const SecondScreen()));

Q91. [Basic] (Theory) What does MaterialPageRoute provide?

Hint

Transition.

Solution

It wraps the destination widget and gives a platform-appropriate transition animation; its builder returns the screen.

Q92. [Basic] (Coding) Go back programmatically.

Hint

pop.

Solution
Navigator.pop(context);

Q93. [Medium] (Theory) Cleanest way to pass data to a screen?

Hint

Constructor.

Solution

Through the destination widget's constructor — typed, compiler-checked Dart, no casting.

Q94. [Medium] (Coding) Get an int back from a pushed screen.

Hint

push returns a Future.

Solution
final v = await Navigator.push<int>(context,
  MaterialPageRoute<int>(builder: (_) => const Picker()));
if (!mounted) return;
if (v != null) setState(() => _n = v);
// Picker: Navigator.pop(context, 7);

Q95. [Medium] (Theory) How do named routes pass arguments, and what's the downside?

Hint

settings.arguments.

Solution

Via pushNamed(context, '/x', arguments: data) and reading ModalRoute.of(context)!.settings.arguments — but it's untyped (Object? you cast), less safe than constructor args.

Q96. [Medium] (Coding) Define a routes table for / and /profile.

Hint

Map in MaterialApp.

Solution
MaterialApp(routes: {
  '/': (_) => const Home(),
  '/profile': (_) => const Profile(),
});
// Navigator.pushNamed(context, '/profile');

Don't also set home:.

Q97. [Medium] (Theory) push vs pushReplacement?

Hint

Stack vs replace.

Solution

push stacks a new route on top (back returns to current). pushReplacement replaces the current route (back skips it) — e.g. splash → home.

Q98. [Advanced] (Theory) After login, how do you prevent "back" returning to the auth flow?

Hint

Clear the stack.

Solution

Navigator.pushAndRemoveUntil(context, homeRoute, (route) => false) — pushes home and removes all routes below, erasing the auth flow from history.

Q99. [Advanced] (Theory) Why does Navigator.push need a context below MaterialApp?

Hint

Walk up to Navigator.

Solution

context is the Element; push walks up to the nearest Navigator (provided by MaterialApp). A context above MaterialApp can't find one → "No Navigator" error. Use a descendant context (or a Builder).

Q100. [Advanced] (Theory) What is onGenerateRoute for?

Hint

Dynamic routes.

Solution

A central hook to build routes dynamically — parse parameters/IDs, guard access, handle unknown routes and deep links, and apply custom transitions — instead of (or alongside) a static routes table.


Coding Mini-Exercises

Ten larger problems combining multiple parts. Build and run each (DartPad supports Flutter), then compare.

Exercise 1 — Counter from scratch. A StatefulWidget with a number and a "+1" FloatingActionButton.

Show solution
class Counter extends StatefulWidget {
  const Counter({super.key});
  @override
  State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
  int _n = 0;
  @override
  Widget build(BuildContext context) => Scaffold(
    body: Center(child: Text('$_n', style: const TextStyle(fontSize: 48))),
    floatingActionButton: FloatingActionButton(
      onPressed: () => setState(() => _n++),
      child: const Icon(Icons.add)),
  );
}

State on the Element (Part 3); setState mutates + rebuilds (Part 4).

Exercise 2 — Profile header. A Row: avatar, an Expanded two-line name/subtitle Column, trailing chevron — no overflow.

Show solution
Row(children: [
  const CircleAvatar(child: Icon(Icons.person)),
  const SizedBox(width: 12),
  Expanded(child: Column(
    mainAxisSize: MainAxisSize.min,
    crossAxisAlignment: CrossAxisAlignment.start,
    children: const [
      Text('Vivek', style: TextStyle(fontWeight: FontWeight.bold)),
      Text('Tap to view', overflow: TextOverflow.ellipsis),
    ])),
  const Icon(Icons.chevron_right),
]);

Expanded confines the text block so it never overflows (Part 5).

Exercise 3 — Badge. A Stack placing a count badge at the top-right of an icon.

Show solution
Stack(clipBehavior: Clip.none, children: [
  const Icon(Icons.notifications, size: 40),
  Positioned(right: 0, top: 0, child: Container(
    padding: const EdgeInsets.all(4),
    decoration: const BoxDecoration(color: Colors.red, shape: BoxShape.circle),
    child: const Text('5', style: TextStyle(color: Colors.white, fontSize: 10)))),
]);

Positioned (direct Stack child) pins the badge (Part 6).

Exercise 4 — Two screens + back. Push a detail screen and return.

Show solution
// Home:
ElevatedButton(
  onPressed: () => Navigator.push(context,
    MaterialPageRoute<void>(builder: (_) => const Detail())),
  child: const Text('Open'));
// Detail: Scaffold(appBar: AppBar(title: Text('Detail'))) // auto back

The AppBar's auto back button pops (Part 8).

Exercise 5 — Pass + return data. Open a picker, return the chosen string, show it.

Show solution
Future<void> _pick() async {
  final c = await Navigator.push<String>(context,
    MaterialPageRoute<String>(builder: (_) => const Picker()));
  if (!mounted) return;
  if (c != null) setState(() => _color = c);
}
// Picker tile: onTap: () => Navigator.pop(context, 'green');

push returns a Future; pop(context, value) completes it (Part 8 + async/await).

Exercise 6 — Responsive grid count. Use LayoutBuilder to show 2 columns under 600px and 4 above.

Show solution
LayoutBuilder(builder: (context, c) {
  final cols = c.maxWidth < 600 ? 2 : 4;
  return GridView.count(crossAxisCount: cols, children: tiles);
});

LayoutBuilder exposes incoming constraints (Part 6).

Exercise 7 — No-leak ticker. A State that updates a label every second and cleans up.

Show solution
Timer? _t;
int _s = 0;
@override
void initState() {
  super.initState();
  _t = Timer.periodic(const Duration(seconds: 1), (_) {
    if (!mounted) return;
    setState(() => _s++);
  });
}
@override
void dispose() { _t?.cancel(); super.dispose(); }
@override
Widget build(BuildContext context) => Text('$_s');

initState/dispose pair to avoid leaks (Part 3).

Exercise 8 — Predict & explain. Why does this never show 100, and how do you fix it?

class _S extends State<W> {
  int x = 0;
  @override
  void initState() { super.initState(); x = 100; }
  @override
  Widget build(BuildContext c) => Text('$x'); // after hot reload still '0'?
}
Show solution

If you originally ran with x = 0 in initState and then edited it to 100 and hot reloaded, the label keeps the old value because hot reload preserves State and doesn't re-run initState (Part 7). Hot restart to recreate the State and run initState, showing 100.

Exercise 9 — Lift state up. Two buttons in separate widgets must show a shared count. Sketch the design.

Show solution
// Parent owns the state; children are stateless and receive value + callback.
class _ParentState extends State<Parent> {
  int _count = 0;
  @override
  Widget build(BuildContext c) => Column(children: [
    CountLabel(count: _count),
    IncrementButton(onTap: () => setState(() => _count++)),
  ]);
}

Single source of truth in the parent; children stay Stateless (Part 3).

Exercise 10 — Capstone: a mini notes app. Build a screen that: (1) shows a scrollable list of note titles (ListView.builder), (2) tapping a note pushes a detail screen receiving the note via constructor, (3) a FAB opens an "add note" screen that returns a new title, which is added to the list with setState, and (4) cleans up properly. Exercise the whole series.

Show solution
import 'package:flutter/material.dart';

void main() => runApp(const MaterialApp(home: NotesScreen()));

class NotesScreen extends StatefulWidget {
  const NotesScreen({super.key});
  @override
  State<NotesScreen> createState() => _NotesScreenState();
}

class _NotesScreenState extends State<NotesScreen> {
  final List<String> _notes = ['Welcome', 'Buy milk'];

  Future<void> _addNote() async {
    final title = await Navigator.push<String>(
      context,
      MaterialPageRoute<String>(builder: (_) => const AddNoteScreen()),
    );
    if (!mounted) return;                         // guard after await
    if (title != null && title.isNotEmpty) {
      setState(() => _notes.add(title));          // mutate + rebuild
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Notes')),
      body: ListView.builder(                     // lazy list
        itemCount: _notes.length,
        itemBuilder: (context, i) => ListTile(
          title: Text(_notes[i]),
          trailing: const Icon(Icons.chevron_right),
          onTap: () => Navigator.push(
            context,
            MaterialPageRoute<void>(
              builder: (_) => NoteDetailScreen(title: _notes[i]), // pass via ctor
            ),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _addNote,
        child: const Icon(Icons.add),
      ),
    );
  }
}

class NoteDetailScreen extends StatelessWidget {       // stateless: just displays
  final String title;
  const NoteDetailScreen({super.key, required this.title});
  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(title: Text(title)),
    body: Center(child: Text(title, style: const TextStyle(fontSize: 24))),
  );
}

class AddNoteScreen extends StatefulWidget {
  const AddNoteScreen({super.key});
  @override
  State<AddNoteScreen> createState() => _AddNoteScreenState();
}

class _AddNoteScreenState extends State<AddNoteScreen> {
  final _controller = TextEditingController();        // created in State

  @override
  void dispose() {
    _controller.dispose();                            // cleaned up — no leak
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Add note')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(children: [
          TextField(controller: _controller, autofocus: true),
          const SizedBox(height: 12),
          ElevatedButton(
            onPressed: () => Navigator.pop(context, _controller.text), // return result
            child: const Text('Save'),
          ),
        ]),
      ),
    );
  }
}

This one app exercises the whole series: Stateless vs Stateful and the State lifecycle with a disposed TextEditingController (Part 3); setState/build discipline and lazy ListView.builder (Part 4); flex layout with Column/Padding (Part 5); and Navigator push/pop, passing data via constructor, and returning a result through the push Future (Part 8). Build it, run it, and you've proven you understand Flutter's fundamentals.


You made it

Nine parts, one hundred questions, and a capstone app. You now understand Flutter from the foundation up:

  • What Flutter is and why drawing its own UI makes it different (Part 1).
  • The three trees — Widget, Element, Render — and why rebuilds are cheap (Part 2).
  • Stateless vs Stateful and the State lifecycle (Part 3).
  • The build method — when it runs and how to keep it fast (Part 4).
  • Layout with flex, Stack, and the constraints model (Part 5, Part 6).
  • Hot reload vs hot restart internals (Part 7).
  • Navigation with Navigator 1.0 (Part 8).

Revisit any question you needed a hint for in a week — then go build a real app.