"Most beginners open a Flutter project and feel overwhelmed by the file structure. Once you understand what each folder does, it becomes the most logical layout you've ever seen."
The Complete Flutter Project Structure
When you run flutter create my_app, this is what you get:
my_app/
├── android/ ← Android-specific native code
├── ios/ ← iOS-specific native code
├── lib/ ← YOUR Flutter/Dart code lives here
│ └── main.dart ← Entry point of the app
├── test/ ← Automated tests
├── web/ ← Web platform files (if enabled)
├── windows/ ← Windows desktop files (if enabled)
├── assets/ ← Images, fonts, JSON files (you create this)
├── pubspec.yaml ← Project config + dependencies
├── pubspec.lock ← Auto-generated, exact dependency versions
├── README.md ← Documentation
└── .gitignore ← Git ignore rules
The rule is simple: you spend 95% of your time in lib/ and pubspec.yaml. Everything else you rarely touch.
The lib/ Folder — Where Your Code Lives
The lib/ folder is your entire Flutter application. Everything you build goes here.
lib/main.dart — The Entry Point
Every Flutter app starts from main.dart. The main() function is the first thing that runs:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp()); // This launches your app
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
),
home: const HomePage(), // First screen
);
}
}
Recommended lib/ folder structure for real apps
lib/
├── main.dart
├── screens/ ← Full-page screens (HomeScreen, ProfileScreen)
│ ├── home_screen.dart
│ └── profile_screen.dart
├── widgets/ ← Reusable UI components
│ ├── topic_card.dart
│ └── custom_button.dart
├── models/ ← Data classes (Topic, User, etc.)
│ └── topic.dart
├── services/ ← API calls, database, Firebase
│ └── api_service.dart
├── utils/ ← Helper functions, constants
│ └── constants.dart
└── theme/ ← App colours, typography
└── app_theme.dart
home_screen.dart, not HomeScreen.dart. Classes inside use PascalCase: class HomeScreen. This is a Dart convention — follow it consistently.
pubspec.yaml — The Most Important Config File
pubspec.yaml is Flutter's configuration file. It controls the app name, version, dependencies (packages), and assets. Think of it like package.json in Node.js or .csproj in .NET.
name: my_app # App package name (no spaces, lowercase)
description: A new Flutter app. # Short description
version: 1.0.0+1 # Version: 1.0.0 is display version, +1 is build number
environment:
sdk: '>=3.0.0 <4.0.0' # Dart SDK version constraint
dependencies:
flutter:
sdk: flutter # Core Flutter framework (always here)
# Add packages here:
http: ^1.1.0 # HTTP requests
shared_preferences: ^2.2.0 # Local storage
google_fonts: ^6.1.0 # Custom fonts from Google
dev_dependencies:
flutter_test:
sdk: flutter # Testing framework
flutter_lints: ^3.0.0 # Code linting rules
flutter:
uses-material-design: true # Enables Material icons
# Assets — tell Flutter about your image/font files
assets:
- assets/images/ # All files in this folder
- assets/images/logo.png # Or specific files
- assets/data/topics.json # JSON files
fonts:
- family: Poppins
fonts:
- asset: assets/fonts/Poppins-Regular.ttf
- asset: assets/fonts/Poppins-Bold.ttf
weight: 700
How to Add a Package (Dependency)
Flutter has thousands of free packages at pub.dev. Adding one is a two-step process:
Step 1 — Add to pubspec.yaml
dependencies:
flutter:
sdk: flutter
http: ^1.1.0 # Add this line
Step 2 — Run pub get
flutter pub get
This downloads the package and makes it available in your code. VS Code runs this automatically when you save pubspec.yaml.
Step 3 — Import and use
import 'package:http/http.dart' as http;
final response = await http.get(Uri.parse('https://api.example.com/data'));
The ^ version prefix
^1.1.0 means "version 1.1.0 or higher, but less than 2.0.0". This allows minor updates but prevents breaking changes from major versions. Always use ^ unless you have a specific reason not to.
The assets/ Folder
Images, fonts, and data files go in the assets/ folder. You create this folder yourself — it doesn't exist by default.
Creating and using assets
# Create these folders in your project root:
assets/
├── images/
│ ├── logo.png
│ └── banner.jpg
├── fonts/
│ └── CustomFont-Regular.ttf
└── data/
└── topics.json
Then declare them in pubspec.yaml (as shown above), then use in code:
// Image from assets
Image.asset('assets/images/logo.png')
// Image with specific size
Image.asset(
'assets/images/banner.jpg',
width: 300,
height: 200,
fit: BoxFit.cover,
)
// JSON from assets
import 'package:flutter/services.dart';
Future<String> loadJson() async {
return await rootBundle.loadString('assets/data/topics.json');
}
The android/ and ios/ Folders
These contain platform-specific native code. You rarely need to edit these directly, but knowing what they contain helps when you encounter native configuration tasks.
android/
android/app/build.gradle— Android build configuration: min SDK version, target SDK, app IDandroid/app/src/main/AndroidManifest.xml— App permissions (internet, camera, location)android/app/src/main/res/— App icon files for different screen densities
ios/
ios/Runner/Info.plist— iOS app configuration: permissions, app name, bundle IDios/Runner/Assets.xcassets/— iOS app icons and launch images
The test/ Folder
Flutter comes with a testing framework built in. The test/ folder contains automated tests for your app.
// test/widget_test.dart — the default test file
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
await tester.pumpWidget(const MyApp());
expect(find.text('0'), findsOneWidget);
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
expect(find.text('1'), findsOneWidget);
});
}
Run tests with: flutter test
Flutter Project Structure — Quick Reference
- lib/ — All your Dart/Flutter code. This is where you spend your time.
- lib/main.dart — Entry point. Contains
main()and rootMaterialApp. - pubspec.yaml — Config file. Add packages, declare assets, set app version.
- assets/ — Images, fonts, JSON. Create it yourself, declare in pubspec.yaml.
- android/ — Android native config. Edit for permissions and app icon.
- ios/ — iOS native config. Edit for permissions and app icon.
- test/ — Automated tests. Write tests here, run with
flutter test. - pubspec.lock — Auto-generated. Commit to Git. Never edit manually.