Project Structure & Configuration
Creating a New Flutter Project
Every Flutter journey starts with creating a new project. The flutter create command scaffolds a complete project with all the necessary files and directories for building apps on multiple platforms. Let’s explore how to create a project and understand every file and folder it generates.
Creating a Project with flutter create
# Basic project creation
flutter create my_app
# Create with a specific organization (reverse domain)
flutter create --org com.example my_app
# Create with specific platforms only
flutter create --platforms android,ios,web my_app
# Create a project with a specific language
flutter create --android-language kotlin --ios-language swift my_app
# Create different project types
flutter create --template=app my_app # Full application (default)
flutter create --template=package my_pkg # Dart package
flutter create --template=plugin my_plugin # Platform plugin
flutter create --template=module my_module # Add-to-app module
my_app and flutter_todo are valid, but MyApp, my-app, and 123app are not.Project Directory Layout
After running flutter create my_app, you get a project directory with the following structure. Understanding each file and folder is essential for effective Flutter development.
Complete Project Structure
my_app/
+-- android/ # Android-specific code and config
| +-- app/
| | +-- build.gradle # App-level build config
| | +-- src/
| | +-- main/
| | +-- AndroidManifest.xml
| | +-- kotlin/... # Android native code
| +-- build.gradle # Project-level build config
| +-- settings.gradle # Gradle settings
+-- ios/ # iOS-specific code and config
| +-- Runner/
| | +-- AppDelegate.swift
| | +-- Info.plist # iOS app configuration
| | +-- Assets.xcassets # App icons and images
| +-- Runner.xcworkspace # Xcode workspace
+-- lib/ # YOUR DART CODE GOES HERE
| +-- main.dart # App entry point
+-- test/ # Unit and widget tests
| +-- widget_test.dart # Default widget test
+-- web/ # Web-specific files
| +-- index.html # Web entry point
| +-- manifest.json # Web app manifest
+-- linux/ # Linux desktop files
+-- macos/ # macOS desktop files
+-- windows/ # Windows desktop files
+-- pubspec.yaml # PROJECT CONFIGURATION
+-- pubspec.lock # Locked dependency versions
+-- analysis_options.yaml # Dart analyzer rules
+-- .gitignore # Git ignore rules
+-- README.md # Project readme
The lib/ Directory
The lib/ directory is where all your Dart source code lives. This is the most important directory in your project. When your project grows, you will organize code into subdirectories here.
Typical lib/ Organization for Larger Projects
lib/
+-- main.dart # Entry point
+-- app.dart # MaterialApp configuration
+-- config/ # App configuration, themes, routes
| +-- theme.dart
| +-- routes.dart
+-- models/ # Data models
| +-- user.dart
| +-- product.dart
+-- screens/ # Full screen pages
| +-- home_screen.dart
| +-- profile_screen.dart
+-- widgets/ # Reusable widgets
| +-- custom_button.dart
| +-- product_card.dart
+-- services/ # API calls, database, etc.
| +-- api_service.dart
| +-- auth_service.dart
+-- providers/ # State management
| +-- auth_provider.dart
+-- utils/ # Utility functions and constants
+-- constants.dart
+-- helpers.dart
The main.dart Entry Point
Every Flutter app starts with the main.dart file inside the lib/ directory. The main() function is the entry point of the application, and it calls runApp() to start the Flutter framework.
Default 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: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
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(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:'),
Text(
'\$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
Understanding runApp()
The runApp() function takes a widget and makes it the root of the widget tree. It inflates the given widget and attaches it to the screen. This widget becomes the top-level ancestor of all other widgets in your app.
How runApp() Works
// runApp() does the following:
// 1. Takes your root widget (e.g., MyApp())
// 2. Creates the root Element
// 3. Attaches it to the rendering pipeline
// 4. Triggers the first build/layout/paint cycle
// 5. Sets up the event loop for user interactions
void main() {
// You can do initialization before runApp()
WidgetsFlutterBinding.ensureInitialized();
// For example: initialize Firebase, load config, etc.
// await Firebase.initializeApp();
runApp(const MyApp());
}
// The simplest possible Flutter app:
void main() {
runApp(
const Center(
child: Text(
'Hello, Flutter!',
textDirection: TextDirection.ltr,
),
),
);
}
The MaterialApp Widget
The MaterialApp widget is typically the root widget of a Flutter app that uses Material Design. It wraps several essential widgets that most apps need: navigation, theming, localization, and more.
MaterialApp Configuration
MaterialApp(
// App identity
title: 'My App', // Used by OS task switcher
debugShowCheckedModeBanner: false, // Remove debug banner
// Theming
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
fontFamily: 'Roboto',
),
darkTheme: ThemeData.dark(useMaterial3: true),
themeMode: ThemeMode.system, // Follow system setting
// Navigation
home: const HomeScreen(), // Default route
routes: {
'/home': (context) => const HomeScreen(),
'/profile': (context) => const ProfileScreen(),
'/settings': (context) => const SettingsScreen(),
},
// Localization
locale: const Locale('en', 'US'),
supportedLocales: const [
Locale('en', 'US'),
Locale('ar', 'SA'),
],
// Error handling
builder: (context, child) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaler: TextScaler.noScaling,
),
child: child!,
);
},
)
CupertinoApp instead of MaterialApp. For a fully custom design system, use WidgetsApp which provides the core functionality without any design language.pubspec.yaml Deep Dive
The pubspec.yaml file is the heart of your Flutter project’s configuration. It defines your project’s name, dependencies, assets, fonts, and more. Understanding every section is crucial.
Complete pubspec.yaml Example
name: my_app
description: A Flutter application for task management.
publish_to: 'none' # Remove this line if publishing to pub.dev
version: 1.0.0+1 # version: major.minor.patch+buildNumber
environment:
sdk: '>=3.2.0 <4.0.0' # Dart SDK version constraint
dependencies:
flutter:
sdk: flutter
# UI packages
cupertino_icons: ^1.0.6 # iOS-style icons
google_fonts: ^6.1.0 # Google Fonts integration
# State management
provider: ^6.1.1 # Simple state management
flutter_bloc: ^8.1.3 # BLoC pattern
# Networking
http: ^1.2.0 # HTTP requests
dio: ^5.4.0 # Advanced HTTP client
# Local storage
shared_preferences: ^2.2.2 # Key-value storage
hive: ^2.2.3 # NoSQL database
# Utilities
intl: ^0.19.0 # Internationalization
url_launcher: ^6.2.2 # Open URLs
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.1 # Lint rules
build_runner: ^2.4.7 # Code generation
mockito: ^5.4.4 # Testing mocks
flutter:
uses-material-design: true # Enable Material icons
# Asset declarations
assets:
- assets/images/ # Entire directory
- assets/icons/ # Another directory
- assets/data/config.json # Specific file
# Custom font declarations
fonts:
- family: CustomFont
fonts:
- asset: assets/fonts/CustomFont-Regular.ttf
- asset: assets/fonts/CustomFont-Bold.ttf
weight: 700
- asset: assets/fonts/CustomFont-Italic.ttf
style: italic
Version Constraints
Understanding Version Constraints
# Dependency version syntax:
dependencies:
# Caret syntax (recommended) - allows minor and patch updates
provider: ^6.1.1 # >=6.1.1 <7.0.0
# Range syntax - explicit range
http: '>=1.0.0 <2.0.0' # Same as ^1.0.0
# Exact version (not recommended)
dio: 5.4.0 # Exactly 5.4.0
# Any version (dangerous)
intl: any # Any version, avoid this!
# Git dependency
my_package:
git:
url: https://github.com/user/repo.git
ref: main # branch, tag, or commit hash
# Path dependency (local package)
my_local_pkg:
path: ../my_local_pkg
Managing Dependencies
Dependency Management Commands
# Add a dependency
flutter pub add provider
flutter pub add dio
# Add a dev dependency
flutter pub add --dev mockito
flutter pub add --dev build_runner
# Remove a dependency
flutter pub remove provider
# Get all dependencies (downloads them)
flutter pub get
# Upgrade dependencies to latest compatible versions
flutter pub upgrade
# Check for outdated packages
flutter pub outdated
# Resolve dependency conflicts
flutter pub upgrade --major-versions
^) for version constraints. It allows bug fixes and minor updates while preventing breaking changes. Run flutter pub outdated regularly to keep your dependencies up to date.Working with Assets
Assets are files bundled with your app, such as images, fonts, JSON files, and other data. They must be declared in pubspec.yaml before they can be used.
Asset Directory Structure and Usage
# Directory structure:
my_app/
+-- assets/
| +-- images/
| | +-- logo.png
| | +-- 2.0x/logo.png # 2x resolution
| | +-- 3.0x/logo.png # 3x resolution
| +-- icons/
| | +-- home.svg
| +-- data/
| +-- countries.json
# pubspec.yaml declaration:
flutter:
assets:
- assets/images/
- assets/icons/
- assets/data/countries.json
# Using assets in Dart code:
// Images
Image.asset('assets/images/logo.png')
// JSON data
final String jsonString = await rootBundle.loadString(
'assets/data/countries.json'
);
final data = json.decode(jsonString);
pubspec.yaml, you must run flutter pub get and perform a hot restart (not just hot reload) for the changes to take effect. Hot reload cannot detect new asset declarations.The .gitignore File
Flutter generates a comprehensive .gitignore file that excludes build artifacts, IDE files, and platform-specific generated files from version control.
Important .gitignore Entries
# Flutter/Dart specific
.dart_tool/ # Dart tooling cache
.packages # Legacy package config
build/ # Build output directory
.flutter-plugins # Generated plugin registrations
.flutter-plugins-dependencies
# IDE specific
.idea/ # Android Studio / IntelliJ
.vscode/ # VS Code (optional, some teams track this)
*.iml # IntelliJ module files
# Platform build artifacts
**/android/.gradle/
**/android/captures/
**/android/local.properties
**/ios/Pods/
**/ios/.symlinks/
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.podspec
# Generated files
*.g.dart # Build runner generated files
*.freezed.dart # Freezed generated files
*.mocks.dart # Mockito generated files
pubspec.lock # Include this for apps, exclude for packages
# Sensitive files
.env # Environment variables
*.jks # Java keystores
*.keystore # Android keystores
pubspec.lock to version control to ensure all team members use the same dependency versions. For Flutter packages, do not commit pubspec.lock so that consumers can resolve their own dependency versions.analysis_options.yaml
The analysis_options.yaml file configures the Dart analyzer, which provides static analysis, linting, and code quality checks. It helps catch errors and enforce coding standards before your code even runs.
analysis_options.yaml Configuration
include: package:flutter_lints/flutter.yaml
analyzer:
exclude:
- "**/*.g.dart" # Exclude generated files
- "**/*.freezed.dart"
errors:
invalid_annotation_target: ignore
missing_required_param: error # Treat as error
missing_return: error # Treat as error
language:
strict-casts: true # No implicit casts
strict-raw-types: true # No raw generic types
linter:
rules:
# Style rules
- prefer_const_constructors
- prefer_const_declarations
- prefer_final_fields
- prefer_final_locals
- avoid_print # Use logging instead
- sort_constructors_first
- sort_unnamed_constructors_first
# Safety rules
- always_declare_return_types
- avoid_dynamic_calls
- cancel_subscriptions
- close_sinks
# Documentation
- public_member_api_docs: false # Disable for apps
build.gradle Basics (Android)
The build.gradle files configure the Android build process. There are two: a project-level file and an app-level file. You rarely need to modify these, but understanding them helps when adding Android-specific features.
android/app/build.gradle Key Sections
plugins {
id "com.android.application"
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}
android {
namespace "com.example.my_app"
compileSdk flutter.compileSdkVersion
defaultConfig {
applicationId "com.example.my_app" // Unique app identifier
minSdk flutter.minSdkVersion // Minimum Android version
targetSdk flutter.targetSdkVersion // Target Android version
versionCode 1 // Internal version number
versionName "1.0.0" // Display version string
}
buildTypes {
release {
signingConfig signingConfigs.debug // Change for production!
minifyEnabled true // Enable code shrinking
proguardFiles getDefaultProguardFile(
'proguard-android-optimize.txt'
), 'proguard-rules.pro'
}
}
}
flutter {
source '../..' // Points to the Flutter project root
}
dependencies {
// Android-specific dependencies go here
}
applicationId, minSdk, or targetSdk in build.gradle, also update the corresponding values in Flutter’s configuration. The applicationId is what uniquely identifies your app on the Google Play Store and must never change after publishing.Running and Building Your App
Now that you understand the project structure, let’s review the essential commands for running and building your Flutter app.
Essential Flutter Commands
# Run in debug mode (with hot reload)
flutter run
# Run on a specific device
flutter devices # List available devices
flutter run -d chrome # Run on Chrome
flutter run -d emulator-5554 # Run on specific emulator
# Run in release mode
flutter run --release
# Run in profile mode (for performance profiling)
flutter run --profile
# Build for production
flutter build apk # Android APK
flutter build appbundle # Android App Bundle (for Play Store)
flutter build ios # iOS
flutter build web # Web
flutter build windows # Windows desktop
flutter build macos # macOS desktop
flutter build linux # Linux desktop
# Clean build cache
flutter clean
# Analyze code for issues
flutter analyze
Summary
In this lesson, you learned how to create a Flutter project with flutter create and explored every file and directory in the generated project structure. You understood the role of lib/ for your Dart code, main.dart as the entry point, runApp() for bootstrapping, and MaterialApp for app-wide configuration. You mastered pubspec.yaml for dependency and asset management, learned about .gitignore for version control, analysis_options.yaml for code quality, and build.gradle for Android configuration. You are now ready to start building your Flutter application with confidence.