Your First App

Time to create a real Flutter project and understand exactly what Flutter generates for you. Every new project starts from the same scaffolded "counter app" — a small but genuinely complete example that touches widgets, state, and layout all at once. Understanding it line by line here will make every later lesson land faster.

Creating a New Project

flutter create my_app
cd my_app

This scaffolds a full project directory. Here's what matters at a glance:

PathWhat it is
lib/main.dartWhere your Dart code lives and where the app starts running. This is the file you'll edit the most, by far.
pubspec.yamlThe project's manifest — name, dependencies, assets. Covered in full in the next lesson.
android/The native Android wrapper project. Generated for you; you rarely need to open it unless configuring platform-specific settings like app icons or permissions.
ios/The native iOS wrapper project (Xcode project files). Same idea as android/, rarely touched directly.
test/Where automated tests live — covered later in the Testing lesson.
Note: You will spend the overwhelming majority of your time in lib/, writing Dart. The native folders exist so flutter run can build a real Android/iOS app around your Dart code, but day-to-day Flutter development rarely requires touching them.

The Entry Point: main() and runApp()

Every Dart program starts executing at a top-level function named main(). In a Flutter app, main()'s job is simply to call runApp(), handing it the root widget of your entire application.

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

runApp() takes that widget, attaches it to the screen, and hands control over to the Flutter framework, which takes it from there — rendering, handling input, and rebuilding widgets as needed for the lifetime of the app.

Walking Through the Generated Counter App

Here is a trimmed-down version of what flutter create generates in lib/main.dart, with each piece explained below it.

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

MyApp is the root widget, and it's a StatelessWidget — it has no internal state of its own to manage. Its only job is to return a MaterialApp, which sets up app-wide concerns (title, theme, and the starting screen) and provides Material Design behavior throughout. The home: parameter points at the actual first screen, MyHomePage.

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body: Center(
        child: Text('Count: $_counter'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        child: const Icon(Icons.add),
      ),
    );
  }
}

MyHomePage is a StatefulWidget — unlike MyApp, it has data that changes over time (the counter), so it needs somewhere to hold that mutable state. That's what _MyHomePageState is for: a matching State object that actually holds _counter and rebuilds the UI whenever it changes.

  • _counter — a private field (the leading underscore makes it private to this file) holding the current count.
  • _incrementCounter() — increments the counter, but crucially wraps the change in setState(() { ... }). Calling setState is how you tell Flutter "state changed, please rebuild this widget" — without it, _counter would change internally but the screen would never update.
  • build() — describes the UI as a tree of widgets: a Scaffold (the basic page structure), an AppBar (top title bar), a centered Text showing the count, and a FloatingActionButton whose onPressed calls _incrementCounter.
Foreshadowing: StatelessWidget vs StatefulWidget and exactly how setState works under the hood get their own dedicated lessons later in this track — for now, just recognize the shape of the pattern.

Running the App

flutter run

With a device or emulator connected, this builds the app and installs it, then keeps a live session open in your terminal. That session listens for single-key commands while the app is running.

Hot Reload: Flutter's Standout Feature

While flutter run is active, saving a change to your Dart code and pressing a single key updates the running app almost instantly — typically well under a second — without restarting the app or losing its current state. If a form had text typed into it, or the counter was at 7, it stays that way after a hot reload; only your code changes are applied.

KeyAction
rHot reload — injects updated source code into the running app and rebuilds the widget tree, preserving state
RHot restart — fully restarts the app from scratch, losing state, but re-runs main(). Needed for changes hot reload can't handle, like changes to main() itself or to native code
qQuit the running session
Note: This is real, correct Flutter behavior described accurately here, but since this website's own project doesn't run a Flutter build, you'll want to try the next example inside your own my_app project to actually see it.

Worked Example: See Hot Reload In Action

With flutter run still active from earlier, open lib/main.dart and make two small changes to the counter app: give the button a different color, and change the starting label text.

// Before
body: Center(
  child: Text('Count: $_counter'),
),
floatingActionButton: FloatingActionButton(
  onPressed: _incrementCounter,
  child: const Icon(Icons.add),
),
// After -- new label text and a custom button color
body: Center(
  child: Text('Taps so far: $_counter'),
),
floatingActionButton: FloatingActionButton(
  onPressed: _incrementCounter,
  backgroundColor: Colors.deepPurple,
  child: const Icon(Icons.add),
),

Save the file, then press r in the terminal running flutter run. The label text and button color update in place within a second — and if you had already tapped the button a few times before saving, the counter's current value is preserved rather than reset to zero, because hot reload only re-applies your code changes on top of the existing running state.

Next steps: Now that you can create, run, and iterate on a Flutter app, move on to Packages & pubspec.yaml to learn how to add third-party functionality to your project.