Project Structure
Flutter Bunny CLI creates projects with a consistent and organized structure based on your chosen architecture. This guide explains the project structure for each supported architecture pattern.
Clean Architecture
Clean Architecture divides your application into layers with clear responsibilities, promoting separation of concerns and testability.
Directory Structure
lib/
├── core/
│ ├── errors/
│ │ ├── exceptions.dart
│ │ └── failures.dart
│ ├── network/
│ │ ├── network_info.dart
│ │ └── api_client.dart
│ ├── utils/
│ │ ├── constants.dart
│ │ └── app_utils.dart
│ └── theme/
│ ├── app_theme.dart
│ └── app_colors.dart
├── data/
│ ├── datasources/
│ │ ├── local/
│ │ │ └── user_local_data_source.dart
│ │ └── remote/
│ │ └── user_remote_data_source.dart
│ ├── models/
│ │ └── user_model.dart
│ └── repositories/
│ └── user_repository_impl.dart
├── domain/
│ ├── entities/
│ │ └── user.dart
│ ├── repositories/
│ │ └── user_repository.dart
│ └── usecases/
│ ├── get_user.dart
│ └── update_user.dart
├── presentation/
│ ├── blocs/
│ │ └── user/
│ │ ├── user_bloc.dart
│ │ ├── user_event.dart
│ │ └── user_state.dart
│ ├── pages/
│ │ ├── home_page.dart
│ │ └── profile_page.dart
│ └── widgets/
│ └── user_profile_card.dart
└── main.dart
Layer Responsibilities
Core
The core/
directory contains application-wide utilities, configurations, and shared functionality:
errors/
: Exception and failure classesnetwork/
: Network-related functionalityutils/
: Utility functions and constantstheme/
: App theme and styling
Data
The data/
directory implements data retrieval and storage logic:
datasources/
: Local and remote data sourcesmodels/
: Data models (typically implements entities from domain layer)repositories/
: Implementation of repositories defined in domain layer
Domain
The domain/
directory contains the core business logic:
entities/
: Business objectsrepositories/
: Abstract repository interfacesusecases/
: Business logic use cases
Presentation
The presentation/
directory handles UI and user interaction:
blocs/
: State management (can be changed based on your choice)pages/
: Screen and page widgetswidgets/
: Reusable UI components
MVVM (Model-View-ViewModel)
MVVM separates UI logic from business logic through ViewModels that act as intermediaries.
Directory Structure
lib/
├── models/
│ ├── user_model.dart
│ └── product_model.dart
├── services/
│ ├── api_service.dart
│ ├── auth_service.dart
│ └── storage_service.dart
├── view_models/
│ ├── home_view_model.dart
│ └── profile_view_model.dart
├── views/
│ ├── screens/
│ │ ├── home_screen.dart
│ │ └── profile_screen.dart
│ └── widgets/
│ ├── custom_button.dart
│ └── product_card.dart
└── main.dart
Component Responsibilities
Models
The models/
directory contains data structures and business logic:
- Data models
- Business entities
- Data validation logic
Services
The services/
directory handles external interactions:
- API communication
- Local storage
- Authentication
- Other services
ViewModels
The view_models/
directory contains the presentation logic:
- Data processing for views
- Business logic coordination
- State management
- View interactions
Views
The views/
directory contains UI components:
screens/
: Full-page screenswidgets/
: Reusable UI components
MVC (Model-View-Controller)
MVC is a classic pattern that divides an application into three interconnected components.
Directory Structure
lib/
├── models/
│ ├── user_model.dart
│ └── product_model.dart
├── services/
│ ├── api_service.dart
│ ├── auth_service.dart
│ └── storage_service.dart
├── views/
│ ├── controllers/
│ │ ├── home_controller.dart
│ │ └── profile_controller.dart
│ ├── models/
│ │ ├── home_view_model.dart
│ │ └── profile_view_model.dart
│ └── views/
│ ├── home_view.dart
│ └── profile_view.dart
└── main.dart
Component Responsibilities
Models
The models/
directory contains data structures and business logic:
- Data models
- Business entities
- Data validation logic
Services
The services/
directory handles external interactions:
- API communication
- Local storage
- Authentication
- Other services
Views
The views/
directory contains all view-related components:
controllers/
: Handle user input and update models/viewsmodels/
: View-specific data modelsviews/
: UI components and screens
Feature-First Structure
Flutter Bunny also supports a feature-first structure, which organizes code by features rather than architectural layers.
Directory Structure
lib/
├── core/
│ ├── config/
│ ├── theme/
│ ├── network/
│ └── utils/
├── features/
│ ├── auth/
│ │ ├── data/
│ │ ├── domain/
│ │ └── presentation/
│ ├── home/
│ │ ├── data/
│ │ ├── domain/
│ │ └── presentation/
│ └── profile/
│ ├── data/
│ ├── domain/
│ └── presentation/
├── shared/
│ ├── widgets/
│ └── models/
└── main.dart
Component Responsibilities
Core
The core/
directory contains application-wide utilities and configurations.
Features
The features/
directory contains feature-specific modules, each with its own layers:
data/
: Data sources and repositoriesdomain/
: Business logic and entitiespresentation/
: UI components and state management
Shared
The shared/
directory contains components shared across features:
widgets/
: Reusable UI componentsmodels/
: Shared data models
Test Structure
Flutter Bunny also creates a consistent test structure:
test/
├── core/
│ └── utils/
│ └── app_utils_test.dart
├── data/
│ ├── datasources/
│ │ └── user_remote_data_source_test.dart
│ ├── models/
│ │ └── user_model_test.dart
│ └── repositories/
│ └── user_repository_impl_test.dart
├── domain/
│ └── usecases/
│ └── get_user_test.dart
├── presentation/
│ ├── blocs/
│ │ └── user_bloc_test.dart
│ └── pages/
│ └── home_page_test.dart
└── widget_test.dart
Additional Files (Continued)
Flutter Bunny creates these additional files to ensure your project is properly configured:
analysis_options.yaml
: Lint and static analysis rulesREADME.md
: Project documentation.gitignore
: Configured for Flutter projectsLICENSE
: Default MIT license (or as configured).vscode/launch.json
: VS Code launch configurationsios/
andandroid/
: Platform-specific configurationsweb/
: Web platform support if enabledassets/
: Directory for project assets
Dependency Management
The pubspec.yaml
file is generated with common dependencies based on your chosen architecture and state management solution:
Clean Architecture + Bloc
dependencies:
flutter:
sdk: flutter
# Core
equatable: ^2.0.5
dartz: ^0.10.1
get_it: ^7.6.0
internet_connection_checker: ^1.0.0+1
# Data layer
http: ^1.1.0
shared_preferences: ^2.2.0
# Presentation layer
flutter_bloc: ^8.1.3
dev_dependencies:
flutter_test:
sdk: flutter
mocktail: ^1.0.0
bloc_test: ^9.1.4
MVVM + Provider
dependencies:
flutter:
sdk: flutter
# Core
provider: ^6.0.5
http: ^1.1.0
shared_preferences: ^2.2.0
dev_dependencies:
flutter_test:
sdk: flutter
mocktail: ^1.0.0
Navigation Structure
Flutter Bunny also sets up navigation based on your preferences:
Named Routes
Standard Flutter named routes in main.dart
:
return MaterialApp(
title: 'My App',
theme: AppTheme.lightTheme,
initialRoute: '/',
routes: {
'/': (context) => const HomePage(),
'/profile': (context) => const ProfilePage(),
'/settings': (context) => const SettingsPage(),
},
);
Auto Route
If using auto_route for navigation:
lib/
├── core/
│ └── router/
│ ├── app_router.dart
│ └── app_router.gr.dart
// app_router.dart
@AdaptiveAutoRouter(
replaceInRouteName: 'Page,Route',
routes: <AutoRoute>[
AutoRoute(page: HomePage, initial: true),
AutoRoute(page: ProfilePage),
AutoRoute(page: SettingsPage),
],
)
class $AppRouter {}
Go Router
If using go_router for navigation:
lib/
├── core/
│ └── router/
│ └── app_router.dart
// app_router.dart
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomePage(),
),
GoRoute(
path: '/profile',
builder: (context, state) => const ProfilePage(),
),
GoRoute(
path: '/settings',
builder: (context, state) => const SettingsPage(),
),
],
);
State Management Structure
The state management structure varies based on your chosen solution:
BLoC/Cubit
lib/
├── presentation/
│ ├── blocs/
│ │ ├── auth/
│ │ │ ├── auth_bloc.dart
│ │ │ ├── auth_event.dart
│ │ │ └── auth_state.dart
│ │ └── theme/
│ │ ├── theme_cubit.dart
│ │ └── theme_state.dart
Provider
lib/
├── providers/
│ ├── auth_provider.dart
│ └── theme_provider.dart
Riverpod
lib/
├── providers/
│ ├── auth_providers.dart
│ └── theme_providers.dart
GetX
lib/
├── controllers/
│ ├── auth_controller.dart
│ └── theme_controller.dart
├── bindings/
│ └── app_bindings.dart
Dependency Injection
Flutter Bunny sets up dependency injection based on your preferences:
Get It
lib/
├── core/
│ └── di/
│ └── service_locator.dart
// service_locator.dart
final sl = GetIt.instance;
Future<void> init() async {
// Blocs
sl.registerFactory(() => AuthBloc(login: sl(), register: sl()));
// Use cases
sl.registerLazySingleton(() => Login(sl()));
sl.registerLazySingleton(() => Register(sl()));
// Repositories
sl.registerLazySingleton<AuthRepository>(
() => AuthRepositoryImpl(
remoteDataSource: sl(),
localDataSource: sl(),
networkInfo: sl(),
),
);
// Data sources
sl.registerLazySingleton<AuthRemoteDataSource>(
() => AuthRemoteDataSourceImpl(client: sl()),
);
sl.registerLazySingleton<AuthLocalDataSource>(
() => AuthLocalDataSourceImpl(sharedPreferences: sl()),
);
// External
final sharedPreferences = await SharedPreferences.getInstance();
sl.registerLazySingleton(() => sharedPreferences);
sl.registerLazySingleton(() => http.Client());
sl.registerLazySingleton<NetworkInfo>(() => NetworkInfoImpl(sl()));
sl.registerLazySingleton(() => InternetConnectionChecker());
}
Riverpod
// providers.dart
final authRepositoryProvider = Provider<AuthRepository>((ref) {
final apiClient = ref.watch(apiClientProvider);
return AuthRepositoryImpl(apiClient);
});
final authStateProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) {
final repository = ref.watch(authRepositoryProvider);
return AuthNotifier(repository);
});
GetX
// app_bindings.dart
class AppBindings extends Bindings {
@override
void dependencies() {
Get.lazyPut<ApiClient>(() => ApiClientImpl());
Get.lazyPut<AuthRepository>(() => AuthRepositoryImpl(Get.find()));
Get.lazyPut<AuthController>(() => AuthController(Get.find()));
}
}
Generated Code
If you choose to use code generation features, Flutter Bunny will set up the appropriate structure:
JSON Serialization
// user_model.dart
@JsonSerializable()
class UserModel {
final int id;
final String name;
final String email;
UserModel({required this.id, required this.name, required this.email});
factory UserModel.fromJson(Map<String, dynamic> json) => _$UserModelFromJson(json);
Map<String, dynamic> toJson() => _$UserModelToJson(this);
}
Freezed
// user.dart
@freezed
class User with _$User {
const factory User({
required int id,
required String name,
required String email,
}) = _User;
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}
Platform-Specific Structure
Flutter Bunny also configures platform-specific structures:
Android
android/
├── app/
│ ├── src/
│ │ ├── main/
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── example/
│ │ │ │ └── myapp/
│ │ │ │ └── MainActivity.kt
│ │ │ └── res/
│ │ │ ├── drawable/
│ │ │ ├── mipmap/
│ │ │ └── values/
│ ├── build.gradle
│ └── AndroidManifest.xml
├── gradle/
├── build.gradle
└── settings.gradle
iOS
ios/
├── Runner/
│ ├── AppDelegate.swift
│ ├── Assets.xcassets/
│ ├── Base.lproj/
│ └── Info.plist
├── Runner.xcodeproj/
└── Runner.xcworkspace/
Customizing Project Structure
You can customize the project structure using Flutter Bunny's configuration:
# Set a custom template
flutter_bunny config set templates.project_structure custom_structure
# Use a custom template for a new project
flutter_bunny create app --template custom_structure
For more details on customizing the project structure, see the Customization Guide.