Flutter Fundamentals
The widget tree, hot reload, and how Flutter compares to React — for developers who know web
Flutter renders its own pixel-perfect UI using Skia/Impeller — it does not use native iOS or Android controls. Everything is a widget, widgets compose into trees, and the framework re-renders efficiently when state changes. If you know React, the mental model maps cleanly: StatelessWidget is a functional component, StatefulWidget is useState, and the widget tree is the virtual DOM.
The widget system
- StatelessWidget — pure function of its inputs — Like a React functional component with no hooks. Given the same props (constructor args), always renders the same UI. Use for everything that does not change.
- StatefulWidget — widget with local state — Pairs with a State object that holds mutable data. When you call setState(() {}), Flutter rebuilds only the affected subtree. Like useState in React.
- Everything is a widget — even layout and spacing — Padding, Center, Row, Column, Container, SizedBox — all widgets. There are no CSS classes. Layout is explicit and composable.
- BuildContext — Flutter passes a BuildContext to every build method. It represents the widget's location in the tree. Used to read inherited data (Theme, Navigator, Providers).
Flutter vs React: the mental model map
- Widget = Component — StatelessWidget = functional component. StatefulWidget = component with useState.
- build() = return <JSX> — The build method returns the widget tree just like returning JSX.
- setState = useState setter — setState triggers a rebuild of the State subtree, like calling a useState setter.
- Constructor parameters = props — Widget properties are passed via the constructor, typed like TypeScript props.
- Dart = TypeScript — Null-safe, typed, async/await, generic, class-based. Feels familiar within a day.
Your first Flutter widget
Create a Flutter project and build a minimal screen
Install Flutter and create a project
flutter create my_app
cd my_app
flutter runReplace main.dart with a minimal stateless screen
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('My App')),
body: const Center(
child: Text('Hello, Flutter', style: TextStyle(fontSize: 24)),
),
);
}
}Save and observe hot reload
Press r in the terminal to hot reload (re-renders without losing state) or R to hot restart (full reset). This is Flutter's fastest feedback loop.
Try this
Create a new Flutter project and replace the default counter app with a custom screen of your own design. Add at minimum: a Scaffold with an AppBar, a Column with three Text widgets of different sizes and weights, and a Padding widget wrapping the Column. Hot reload after each change and observe the immediate update. Time yourself — how fast can you go from zero to a custom screen?