5 exercises — Practice Flutter and Dart vocabulary in English: widget tree, state management, BuildContext, async/await, pubspec.yaml, and rendering pipeline.
Core Flutter & Dart vocabulary clusters
Widget types: StatelessWidget, StatefulWidget, InheritedWidget, BuildContext, Widget tree, render tree
State: setState(), State lifecycle, initState, dispose, didChangeDependencies, provider, Riverpod, BLoC
Navigation: Navigator, Route, MaterialPageRoute, named routes, go_router, push/pop
Tooling: pubspec.yaml, flutter pub get, hot reload, hot restart, flutter run, flutter build
0 / 5 completed
1 / 5
A Flutter developer explains the widget tree to a new teammate: "Everything in Flutter is a widget — even padding and layout. There are two fundamental types: StatelessWidget describes a part of the UI that doesn't change; build() is called once and returns an immutable tree. StatefulWidget has a companion State object whose state can change; when you call setState(), Flutter marks the widget as dirty and calls build() again. The key rule: keep as much of your UI in StatelessWidgets as possible — only use StatefulWidget where state changes are actually needed." What is the core difference between a StatelessWidget and a StatefulWidget?
StatelessWidget: immutable. The build() method is a pure function of the input — given the same constructor arguments, it always produces the same widget tree. No internal mutable state. Use for: static content, presentational components, text, icons. StatefulWidget: paired with a State<T> object that persists between builds. The State object holds mutable data. setState() schedules a rebuild. Widget lifecycle: StatelessWidget: constructor → build() → unmount. StatefulWidget State lifecycle: createState() → initState() → didChangeDependencies() → build() → setState() → build() (repeated) → deactivate() → dispose(). BuildContext: represents a location in the widget tree. Used to look up inherited widgets (Theme.of(context), MediaQuery.of(context)), navigate (Navigator.of(context)). Dirty widget: a widget Flutter knows needs to rebuild. setState() marks a widget dirty. Widget tree vs. element tree vs. render tree: Flutter builds three trees. Widget tree: blueprints (immutable). Element tree: instances linking widgets and render objects. Render tree: actual layout and painting. In conversation: 'Rule of thumb: start with StatelessWidget. Only upgrade to StatefulWidget when you need to call setState(). Too many StatefulWidgets creates performance and complexity problems.'
2 / 5
A Flutter architect reviews state management options: "For simple local state, setState() is fine. For shared state across widgets that don't share a direct ancestor, InheritedWidget is the base mechanism — but it's verbose. Provider wraps InheritedWidget with a cleaner API. Riverpod fixes Provider's limitations around context dependency. BLoC separates UI from business logic using streams — good for complex event-driven state. The choice depends on complexity: setState for local, Provider/Riverpod for medium complexity, BLoC for complex reactive systems." What problem does Provider solve that setState() cannot?
Prop drilling problem: with setState(), to share data between two widgets that aren't in a parent-child relationship, you'd need to lift state up to their lowest common ancestor, then pass it down through every widget in between — even ones that don't need it. Provider (based on InheritedWidget): places data at a point in the tree; any descendant can access it with context.watch<T>() or context.read<T>() without the intermediate widgets needing to know about it. State management options: setState() — local widget state; simple; no architecture required. InheritedWidget — the low-level mechanism; verbose to use directly. Provider — wraps InheritedWidget with ChangeNotifier; simple reactive UI. Riverpod — rethink of Provider; doesn't depend on BuildContext; supports code generation; more testable. BLoC (Business Logic Component) — streams-based; separates UI events from state transitions; more boilerplate; excellent for complex flows. ChangeNotifier — a Flutter class; call notifyListeners() to trigger rebuilds in watching widgets. context.watch<T>() — subscribe to changes. context.read<T>() — read once without subscribing. In conversation: 'Provider is the pragmatic choice for 80% of apps. BLoC is worth the overhead for apps with complex, multi-step user flows where state transitions need to be fully testable.'
3 / 5
A Flutter developer explains async patterns: "Flutter uses Dart's async/await model. A Future represents a single value that will be available later. An async function returns a Future — you can await it to pause execution until it completes. A Stream is like a Future but delivers multiple values over time — useful for real-time data. FutureBuilder and StreamBuilder are widgets that rebuild their subtree when the Future resolves or the Stream emits." When would you use a StreamBuilder instead of a FutureBuilder?
Future: represents a single async computation. Resolves once with a value or an error. Pattern: fetch data → display it. await on a Future suspends the async function until it completes. Stream: a sequence of async events. Can emit zero or more values before completing. Examples: WebSocket messages, Firebase real-time snapshots, file download progress, timer ticks. FutureBuilder: rebuilds its subtree when the Future resolves. Shows loading state, error state, or data. Best for: HTTP GET, file read, database query (one-time). StreamBuilder: rebuilds its subtree on each stream event. Maintains ConnectionState (waiting, active, done). Best for: real-time updates, form validation streams, BLoC output. StreamBuilder snapshot states: ConnectionState.waiting — connected, no data yet. ConnectionState.active — data is flowing. ConnectionState.done — stream closed. ConnectionState.none — no stream. Async vocabulary: await — pause execution until a Future resolves. async — marks a function as returning a Future. then() — callback when Future resolves (older style; prefer await). catchError() — catch Future errors (prefer try/catch with await). In conversation: 'If your data source can update without user action — use a Stream. If the user requests data and waits for one answer — use a Future.'
4 / 5
A developer explains Flutter's rendering pipeline at a tech talk: "Flutter doesn't use platform UI components. Instead, it has its own rendering engine (Skia/Impeller) and paints everything from scratch at 60 or 120fps. The rendering pipeline is: build phase (creates the widget tree), layout phase (calculates sizes and positions), paint phase (draws to canvas). Jank happens when a frame takes longer than 16ms — you'll see it as dropped frames. The main causes: expensive build() methods, blocking the UI thread with sync operations, or rebuilding too much of the tree." What is jank in Flutter and what are the most common causes?
Jank: visible frame drops. At 60fps, each frame must complete in ≤16.7ms. At 120fps, ≤8.3ms. When a frame takes longer, the user sees a stutter. Rendering pipeline phases: Build: Flutter calls build() on dirty widgets, creating the widget tree. Expensive build() methods cause build phase overruns. Layout: calculates sizes and positions top-down. Complex layouts cause layout overruns. Paint: draws to a canvas. Complex paints cause paint phase overruns. Common jank causes: 1) Expensive computation in build() — move to a separate isolate or precompute. 2) Synchronous I/O on the UI thread — always use async. 3) Excessive rebuilds — use const constructors, RepaintBoundary, or selective rebuilds. Profiling tools: Flutter DevTools performance tab: frame timeline showing build/layout/paint durations. Raster thread: handles GPU drawing (Skia/Impeller). UI thread: handles Dart code (build/layout). Both can cause jank. Performance vocabulary: const constructor — creates a compile-time constant widget; Flutter skips rebuilding it. Biggest performance win. RepaintBoundary — isolates part of the tree from the rest's repaints. Isolate — Dart's concurrency primitive; separate memory; communicate via messages. Impeller — Flutter's next-generation rendering engine; pre-compiles shaders. In conversation: 'First optimisation: add const to every widget that can take it. Eliminating unnecessary rebuilds is worth 80% of Flutter performance gains.'
5 / 5
An engineer is onboarding a new mobile developer: "Hot reload is Flutter's killer feature. It injects your Dart code changes into the running VM without restarting the app — state is preserved, so you see your UI change instantly. Hot restart restarts the Dart VM and resets state — it's slower but handles changes that hot reload can't: static initializers, main() changes, platform channel changes. When you change pubspec.yaml — adding a dependency — you need to run flutter pub get to download it, then a full restart." What is the difference between hot reload and hot restart in Flutter?
Hot reload: injects updated Dart source into the running VM. The Dart VM patches the existing isolate. State is preserved. Widget build() methods are called again with the new code. Takes: ~1 second. Limitations: cannot handle changes to static fields, global variables, main() method, initState(). Hot restart: destroys and recreates the Dart VM isolate. App restarts from main(). State is lost. Takes: 2–5 seconds. Works for all code changes. pubspec.yaml workflow: Add dependency → flutter pub get (downloads packages to .dart_tool/) → hot restart or full restart. Flutter CLI vocabulary: flutter run — start a debug build on a device/emulator. flutter build apk/ipa/web — production builds. flutter pub get — resolve and download dependencies from pubspec.yaml. flutter pub upgrade — update packages to latest compatible versions. flutter analyze — static analysis (like dart analyze). flutter test — run widget and unit tests. pubspec.yaml vocabulary: dependencies — runtime packages. dev_dependencies — build/test packages. flutter.assets — asset paths to bundle. flutter.fonts — custom font definitions. In conversation: 'Hot reload is why Flutter development feels so fast — the feedback loop for UI changes is almost instant. You tweak a widget and see the result before you can look up from your keyboard.'