In plain words
Flutter is a toolkit for building apps. You write your app once, in a language called Dart, and Flutter turns it into an Android app, an iPhone app, a website and a desktop program for Windows, macOS or Linux.
Think of Flutter as a painter who brings their own canvas and paint to every house. Other tools ask each house (Android, iOS, the browser) to lend them its furniture: its buttons, its lists, its text boxes. Flutter does not borrow. It paints every pixel itself. That is why a Flutter app looks the same on a cheap Android phone and on a new iPhone, unless you decide it should not.
You build the screen out of widgets. A widget is a small description of a piece of UI: “some text”, “a row of things”, “16 pixels of padding”, “a whole page with an app bar”. You nest widgets inside widgets, like boxes inside boxes, until you have a full screen.
Why it matters
Most companies need their app on Android and iOS, and often on the web too. Building each one separately means two or three teams, two or three codebases and bugs fixed two or three times. With Flutter, a small team ships one codebase to all of them. That is why agencies and start-ups like it: a single developer can deliver a client app for both stores.
For you as a developer, Flutter has three big benefits:
- Speed of development. Hot reload shows your change in about a second without losing where you are in the app.
- Full control of the design. Because Flutter paints everything, a custom design from a designer is not harder than a standard one.
- Good performance. Release builds are compiled to native machine code, and the engine is built to draw smooth animations.
Flutter jobs usually ask for the same core skills this track teaches: Dart, layouts, state management (often BLoC or Provider), REST APIs, local storage, Firebase, testing and publishing to the stores.
How it works
To understand Flutter you need four ideas: its layers, the role of Dart, widgets as descriptions, and the trees Flutter keeps behind the scenes.
The layers
A Flutter app has three layers. You mostly work in the top one.
| Layer | What it does | Written in |
|---|---|---|
| Framework | Widgets, layout, Material Design components, animation, gestures. This is the code you import with package:flutter/material.dart. | Dart |
| Engine | Draws pixels (with the Impeller renderer on iOS and modern Android), lays out text, runs Dart, talks to the GPU. | C++ |
| Embedder | Hosts the engine inside each platform: creates the window, passes touches and keyboard input, gives access to plugins. | Kotlin/Java, Swift/Objective-C, C++ and others |
Where Dart fits
Dart was designed for building user interfaces, and Flutter uses two of its tricks:
- JIT (just-in-time) compilation in debug mode. The Dart VM can swap in changed code while the app runs. This is what powers hot reload.
- AOT (ahead-of-time) compilation in release mode. Your code is compiled to native ARM or x64 machine code before it ships, so it starts fast and runs predictably. For the web, Dart compiles to JavaScript or WebAssembly.
Dart is also type-safe and null-safe, which catches many mistakes in the editor before you even run the app. Module 1 teaches Dart properly before you touch widgets.
Widgets and the declarative idea
In older UI toolkits you created a label, kept a reference to it, and later said “label, change your text”. Flutter is declarative: you write a build method that describes what the screen looks like for the current data. When the data changes, Flutter calls build again and compares the new description with the old one. It then updates only the parts of the screen that really changed.
A common way to say this is UI = f(state): the interface is a function of the state.
Here is a complete Flutter app. Read it from the top: main calls runApp with the root widget, MaterialApp sets up the theme, Scaffold gives a standard page with an app bar, and Center, Column, Icon and Text build the body.
Screen 1: Hello Flutter lib/main.dart
import 'package:flutter/material.dart';
void main() {
runApp(const HelloApp());
}
class HelloApp extends StatelessWidget {
const HelloApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
),
home: Scaffold(
appBar: AppBar(title: const Text('Hello Flutter')),
body: const Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.flutter_dash, size: 64),
SizedBox(height: 12),
Text('Everything here is a widget'),
],
),
),
),
);
}
}
A complete app: MaterialApp gives the theme, Scaffold gives the page, and Center, Column, Icon and Text are all widgets nested in a tree.
Notice three things you will see in every Flutter file:
StatelessWidgetwith abuild(BuildContext context)method that returns widgets.constin front of widgets that never change, which lets Flutter skip rebuilding them.ColorScheme.fromSeed: you give one colour and Material 3 generates a full, matching palette.
Three trees (a first look)
Widgets are cheap, short-lived descriptions. Behind them Flutter keeps an element tree (which widget is mounted where, and which state belongs to it) and a render tree (objects that know their size and position and paint themselves). You write widgets; Flutter manages the other two. Lesson 2.1 goes deep on this.
Example: a tour of the course apps
Each module ends with a small app you can show in a portfolio. The first ones are console programs, so you can focus on Dart without any UI.
| Module | App | What it teaches |
|---|---|---|
| 1 Dart in depth | Grade Book, Library Manager, Weather Fetcher (console) | Types, null safety, classes, async code |
| 2 Flutter fundamentals | Business Card, counter and tip calculator | MaterialApp, Scaffold, stateless and stateful widgets |
| 3 Layout | Recipe Book | Rows, columns, constraints, lists and grids |
| 4 Material and theming | Travel Explorer | Material 3 components, dark mode, custom widgets |
| 5 Navigation and forms | Shop Catalog, Event Registration | Navigator, go_router, validated forms |
| 6 State management | Shopping Cart (Provider), Task Tracker (BLoC) | App state, Cubit, Bloc, testing blocs |
| 7 Data and backend | News Reader, Notes, Chat | HTTP and JSON, local storage, Firebase |
| 8 Polish and ship | Onboarding | Animations, tests, performance, publishing |
Below is a small version of the Task Tracker. Switch between the two frames: tapping a checkbox changes the data in _tasks, setState asks Flutter to rebuild, and the “tasks left” text updates by itself. You never write “change that label”.
Screen 2: Task Tracker preview lib/main.dart
import 'package:flutter/material.dart';
void main() => runApp(const TaskApp());
class TaskApp extends StatelessWidget {
const TaskApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Task Tracker',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
),
home: const TaskScreen(),
);
}
}
class TaskScreen extends StatefulWidget {
const TaskScreen({super.key});
@override
State<TaskScreen> createState() => _TaskScreenState();
}
class _TaskScreenState extends State<TaskScreen> {
final _tasks = <String, bool>{
'Buy groceries': true,
'Finish Dart lesson': false,
'Call the client': false,
};
@override
Widget build(BuildContext context) {
final left = _tasks.values.where((done) => !done).length;
final label = left == 1 ? '1 task left' : '$left tasks left';
return Scaffold(
appBar: AppBar(title: const Text('My tasks')),
body: ListView(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Text(label,
style: Theme.of(context).textTheme.titleMedium),
),
for (final entry in _tasks.entries)
CheckboxListTile(
title: Text(entry.key),
value: entry.value,
onChanged: (value) {
setState(() {
_tasks[entry.key] = value ?? false;
});
},
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.add),
),
);
}
}
A small finished app. Tapping a checkbox calls setState, Flutter rebuilds the list and the “tasks left” count updates. You will rebuild this app properly with BLoC in module 6. States: Start → After one tap.
You do not need to understand every line yet. By the end of module 2 you will be able to write this from memory, and in module 6 you will rebuild it with BLoC, the architecture many production teams use.
Common mistakes
- Thinking Flutter is a web view. It is not. Flutter apps are compiled and draw with their own engine; they do not run HTML inside the app.
- Skipping Dart. Many beginners jump straight into widgets and then struggle with null safety, async code and classes. Learn the language first; widgets become much easier.
- Judging performance in debug mode. Debug builds use JIT and extra checks, so they can feel slow. Measure speed with a profile or release build (
flutter run --profileor--release). - Expecting native look automatically. Material widgets look like Material on iOS too. If a client wants an iOS look, use the Cupertino widgets or adaptive constructors on purpose.
- Treating widgets like objects you edit. You do not keep a reference to a Text and change it. You change the data and rebuild.
Interview and real-world notes
- “How is Flutter different from React Native?” A good short answer: React Native renders the platform’s own native components and runs JavaScript; Flutter paints its own widgets with its own engine and compiles Dart to native code. Avoid claiming one is always faster; say it depends on the app.
- “Why Dart?” Mention JIT for hot reload in development, AOT for fast release builds, sound null safety, and a single language for UI and logic.
- “What is a widget?” An immutable description of part of the UI. The framework compares widgets between builds and updates the element and render trees.
- In client work, Flutter shines for custom-branded apps that must ship on Android and iOS together. For apps that are mostly heavy native features (for example deep system integrations), you may still write some platform code through plugins or platform channels, covered in module 8.