Skip to content
Learni
View all tutorials
Développement Mobile

How to Create Your First Flutter App in 2026

Lire en français

Introduction

Flutter, Google's open-source framework, is revolutionizing mobile development in 2026 with its ultra-performant native rendering and declarative UI paradigm. Unlike hybrid frameworks like React Native, Flutter compiles to native ARM code, delivering performance on par with Swift or Kotlin—all from a single codebase for iOS, Android, web, and desktop.

Why adopt it now? In 2026, 70% of Fortune 500 apps use Flutter for its rapid development (hot reload in <1s) and rich widget kit (Material 3, Cupertino). This beginner tutorial guides you step by step to create an evolving Counter app into a Todo List, with 100% copy-pasteable code. By the end, you'll master the basics: stateless/stateful widgets, simple state management, navigation, and deployment. Ready to boost your mobile dev career? (128 words)

Prerequisites

  • Computer with macOS, Windows, or Linux (8 GB RAM min.)
  • Flutter SDK version 3.24+ (2026 stable)
  • Android Studio (for Android emulator) or Xcode (macOS for iOS)
  • VS Code with Flutter and Dart extensions
  • Basic programming knowledge (no Dart experience required—we'll cover it along the way)

Install and Verify Flutter

terminal
git clone https://github.com/flutter/flutter.git -b stable

export PATH="$PATH:`pwd`/flutter/bin"
flutter precache
flutter doctor -v

These commands clone the stable Flutter SDK, add it to your PATH, and verify the installation. flutter doctor lists any missing tools (e.g., Android SDK). Stick to stable versions as a beginner; restart your terminal after setting the PATH.

Create Your First Project

Analogy: Creating a Flutter project is like initializing a new Git repo, but it generates a ready-to-code app skeleton. This includes lib/main.dart (entry point), pubspec.yaml (dependencies), and iOS/Android configs.

Generate the Project

terminal
flutter create ma_premiere_app
cd ma_premiere_app
flutter pub get

flutter create scaffolds a complete project with default Material Design. pub get resolves dependencies. Run flutter run to test on an emulator; if you hit errors, check flutter doctor.

Basic App: main.dart

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: 'Ma Première App',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const MyHomePage(title: 'Accueil'),
    );
  }
}

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: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Vous avez appuyé tant de fois:'),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

This code creates a complete Counter app: runApp launches the app, MaterialApp handles theme and routing, StatefulWidget updates the UI via setState. Copy-paste into lib/main.dart, then run flutter run. Tip: Use const for performance; key prevents unnecessary rebuilds.

Understanding Widgets

Flutter is built on immutable widgets: everything is a widget (UI + logic). Use Stateless for static content (e.g., icons), Stateful for dynamic (e.g., counter). The widget tree rebuilds on every setState, like a reactive DOM but faster.

Add a ListView

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: 'Ma App ListView',
      theme: ThemeData(primarySwatch: Colors.green),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Ma ListView')),
      body: ListView.builder(
        itemCount: 20,
        itemBuilder: (context, index) {
          return ListTile(
            leading: const Icon(Icons.star),
            title: Text('Item $index'),
            subtitle: Text('Sous-titre $index'),
            trailing: const Icon(Icons.arrow_forward_ios),
          );
        },
      ),
    );
  }
}

Replace the body with ListView.builder for an optimized infinite list (lazy loading). itemBuilder generates items dynamically; itemCount sets the limit. Perfect for long lists. Avoid fixed ListView for better mobile performance.

Todo List with State

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: 'Todo App',
      theme: ThemeData(primarySwatch: Colors.orange),
      home: const TodoListScreen(),
    );
  }
}

class TodoListScreen extends StatefulWidget {
  const TodoListScreen({super.key});

  @override
  State<TodoListScreen> createState() => _TodoListScreenState();
}

class _TodoListScreenState extends State<TodoListScreen> {
  final List<String> _todos = [];
  final TextEditingController _controller = TextEditingController();

  void _addTodo() {
    if (_controller.text.isNotEmpty) {
      setState(() {
        _todos.add(_controller.text);
        _controller.clear();
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Mes Todos')),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _controller,
                    decoration: const InputDecoration(hintText: 'Nouvelle todo'),
                  ),
                ),
                IconButton(
                  onPressed: _addTodo,
                  icon: const Icon(Icons.add),
                ),
              ],
            ),
          ),
          Expanded(
            child: ListView.builder(
              itemCount: _todos.length,
              itemBuilder: (context, index) => ListTile(
                title: Text(_todos[index]),
                trailing: IconButton(
                  icon: const Icon(Icons.delete),
                  onPressed: () => setState(() => _todos.removeAt(index)),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

This Todo App manages state with List, TextEditingController for input, and dispose for cleanup. setState triggers targeted rebuilds. Adds and removes items dynamically. Pitfall: Forgetting dispose causes memory leaks; use Expanded for smooth scrolling.

Run the App

terminal
flutter run -d chrome  # Pour web
# ou
flutter run  # Pour émulateur connecté

# Hot reload: tapez 'r'
# Hot restart: 'R'

flutter run builds and launches on a device, emulator, or web. Use -d chrome for quick web testing. Hot reload preserves state. Check flutter devices to list available targets.

Best Practices

  • Hot Reload: Save (Ctrl+S) to iterate quickly, but use Hot Restart for state changes.
  • Use const everywhere to optimize rebuilds.
  • State Management: setState for beginners; upgrade to Provider/Riverpod for complex apps.
  • Test on real devices: Emulators are slow for animations.
  • Pubspec.yaml: Add deps like http: ^1.2.0 for APIs, then flutter pub get.

Common Errors to Avoid

  • Indentation/missing {}: Dart is strict; use VS Code auto-format (Ctrl+Shift+I).
  • No setState: UI won't update (classic beginner mistake).
  • Undisposed controller: Memory leaks on Android/iOS.
  • Forgot flutter clean: After pubspec changes, run flutter clean && flutter pub get.

Next Steps