Week 1: Dart Fundamentals & Your First Flutter App

Flutter apps are written entirely in Dart, so before touching a single widget it's worth spending real time on the language itself. This week covers the Dart you'll use constantly — variables, null safety, functions and classes — then installs the Flutter SDK and gets a real app running on an actual device or simulator.

Phase 1 of 8 Week 1 of 20 ~3–4 Hours Hands-on Exercise Included

By the end of this week, you'll be able to

  • Read and write core Dart — variables, null safety, functions & classes
  • Install the Flutter SDK & confirm your setup with flutter doctor
  • Scaffold a project and run it on a simulator, emulator or physical device

1. Dart in Five Minutes

Dart is the language Flutter is built on — a curly-brace, statically-typed language that will feel immediately familiar if you've written TypeScript, Java, C#, or Kotlin. A few things matter from day one:

variables & null safety
// var infers the type; the type is fixed once assigned
var name = 'Ada';        // inferred as String
int age = 36;
final city = 'London';   // final: assigned once, at runtime
const pi = 3.14159;      // const: a compile-time constant

// Dart is sound null-safe: a type is non-nullable unless you add ?
String greeting = 'Hello';   // can never be null
String? nickname;            // can be null -- must be declared explicitly

// The ?? and ?. operators handle nullable values safely
print(nickname ?? 'no nickname set');   // prints the fallback if null
print(nickname?.length);                // null instead of crashing

Every variable in Dart is non-nullable by default — this is the single most important difference from JavaScript. If a function expects a String, passing null is a compile-time error, not a runtime surprise. Anywhere a value is genuinely optional, mark the type with ? explicitly.

functions & classes
// A function with a typed parameter and typed return value
int add(int a, int b) {
  return a + b;
}

// Named parameters (common in Flutter's own APIs) use {} and can be required
String describe({required String name, int age = 0}) {
  return '$name is $age years old';
}
describe(name: 'Ada', age: 36);

// A simple class with a constructor
class Person {
  final String name;
  final int age;

  Person({required this.name, required this.age});

  String greet() => 'Hi, I\'m $name';
}

final ada = Person(name: 'Ada', age: 36);
print(ada.greet());
Named parameters are everywhere in Flutter

Almost every widget constructor you'll write from Week 2 onward uses named parameters (Container(color: ..., width: ...)) rather than positional ones — get comfortable with the {required this.x} pattern now, since it's the shape of nearly every widget you'll build.

2. Installing Flutter

Download the Flutter SDK for your OS from flutter.dev, add it to your PATH, then verify the whole toolchain in one command:

terminal
flutter doctor

flutter doctor checks the Flutter SDK itself, plus every platform toolchain it can build for — Android Studio/SDK for Android, Xcode for iOS (Mac only), and your connected devices/emulators — and prints a checklist of what's missing. Don't move on until at least one platform shows a green checkmark.

Android-only is enough to start

iOS builds require a Mac with Xcode — if you're on Windows or Linux, install Android Studio and target the Android emulator or a physical Android phone for this entire course. Everything you learn transfers directly once you do have access to a Mac for iOS builds.

Install the Flutter and Dart extensions for VS Code (or the Flutter plugin for Android Studio) — they add syntax highlighting, a device picker, and one-click debugging, and are what this course assumes you're using throughout.

3. Creating a Project

Scaffold a new project and step into it:

terminal
flutter create my_app
cd my_app
flutter run
project layout
my_app/
├── lib/
│   └── main.dart        ← the entry point -- your app starts here
├── pubspec.yaml          ← dependencies, assets, app metadata
├── android/               ← native Android project (edited rarely)
├── ios/                   ← native iOS project (edited rarely)
├── test/                  ← unit & widget tests
└── web/                   ← native web project, if enabled

pubspec.yaml is Flutter's equivalent of package.json — it lists your dependencies (fetched from pub.dev, Dart's package registry) and declares any asset files (images, fonts) your app bundles:

pubspec.yaml — the essentials
name: my_app
description: A new Flutter project.
version: 1.0.0+1

environment:
  sdk: '>=3.3.0 <4.0.0'

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.6

flutter:
  uses-material-design: true

Adding a package is one command — flutter pub add http adds http as a dependency and writes it into pubspec.yaml automatically, the same way npm install would.

4. Running on a Device

List whatever's available, then run against a specific target:

terminal
# See every connected device/emulator/simulator Flutter can see
flutter devices

# Run on a specific device by ID
flutter run -d emulator-5554

# Just run -- Flutter picks a device automatically if only one is connected
flutter run

Once the app is running, two commands drive the entire development loop, both triggered from the terminal Flutter is running in (or a single keypress/button in your editor):

  • Hot reload (press r) — injects updated source code into the running app in under a second, preserving current app state (like which screen you're on). This is what you'll use for the vast majority of edits.
  • Hot restart (press R) — rebuilds and restarts the app from scratch, resetting all state. Needed when a change hot reload can't handle, like editing main() or adding a new class field.
Hot reload is the whole point

This sub-second feedback loop is one of Flutter's defining features — you'll edit a widget's padding or color and see it change on a real device almost instantly, without losing your place in the app. Get used to leaving flutter run running in a terminal the entire time you work.

5. Your First Widget

lib/main.dart is the entry point. The single governing idea in Flutter, which every week of this course builds on, is this: everything is a widget — not just buttons and text, but padding, alignment, and layout are all widgets too, composed into a tree.

lib/main.dart
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My App',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const 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: 20, fontWeight: FontWeight.w600),
        ),
      ),
    );
  }
}

Reading this top to bottom, in the order Flutter actually needs it:

  • runApp() takes a widget and makes it the root of the entire app — everything else nests inside it
  • MaterialApp sets up app-wide concerns: theming, navigation, and Material Design defaults (a CupertinoApp variant exists for iOS-styled apps)
  • Scaffold provides the basic visual structure of a screen — an app bar, a body, and slots for things like floating action buttons and navigation drawers
  • Center and Text are themselves widgets, nested inside Scaffold's body — this nesting is the layout, there's no separate stylesheet describing it

Every widget class overrides a build(BuildContext context) method that returns the widget(s) it renders — Flutter calls build again whenever a widget needs to redraw, which is the mechanism behind hot reload and, starting next week, interactivity.

6. Hands-on Exercise

Hands-on

Scaffold a project and get it running on your own device

The whole point of this week is proving the full loop works end to end before any real code gets written.

Requirements:

  1. Run flutter doctor and resolve every issue for at least one platform (Android is enough on Windows/Linux).
  2. Run flutter create my_app, then flutter run and confirm the default counter app loads on an emulator, simulator, or physical device.
  3. Replace main.dart with the HomeScreen example above, changing the title, the greeting text, and the font size.
  4. With flutter run still active, edit the text again and press r in the terminal to hot reload — confirm the change appears without losing app state.
  5. Write a small Person-style Dart class of your own (e.g. Book with title, author, a named constructor, and a method that returns a formatted string) and print an instance of it to the debug console with debugPrint().
Hint

If flutter run can't find a device, run flutter emulators --launch <emulator_id> to start one, or plug in a physical phone with USB debugging (Android) or via Xcode (iOS) enabled.

7. Knowledge Check

Four quick questions. Expand each to check your answer.

Q1

What does adding ? to a type, like String? nickname, actually change?

Dart types are non-nullable by default, so String nickname can never legally hold null — the compiler rejects it. Adding ? explicitly opts the type into being nullable, which then requires handling the null case (with ??, ?., or an explicit check) wherever the value is used.

Q2

What's the practical difference between hot reload and hot restart?

Hot reload injects your changed source code into the already-running app in under a second, keeping the app's current state (like which screen is open). Hot restart rebuilds and relaunches the entire app from scratch, resetting all state — necessary for changes reload can't apply, like edits to main() or new fields on an existing class.

Q3

What does the phrase "everything is a widget" actually mean in Flutter?

Not just visible things like buttons and text are widgets — layout, padding, alignment and centering are widgets too (Center, Padding, Row). A screen is a tree of nested widgets, and the nesting itself is the layout; there's no separate stylesheet or layout language describing it the way CSS does on the web.

Q4

What does a widget's build(BuildContext context) method do, and when does Flutter call it?

It returns the widget (or tree of widgets) that particular widget renders as. Flutter calls it once when the widget first appears, and again any time that widget needs to redraw — which is the same mechanism hot reload relies on, and starting next week, the mechanism setState uses to make widgets interactive.