Project Structure - utourismboard/explore-uganda-application-documentation GitHub Wiki
explore_uganda/
├── lib/
│ ├── core/
│ │ ├── constants/
│ │ ├── errors/
│ │ ├── network/
│ │ └── utils/
│ ├── data/
│ │ ├── models/
│ │ ├── repositories/
│ │ └── services/
│ ├── domain/
│ │ ├── entities/
│ │ ├── repositories/
│ │ └── usecases/
│ ├── presentation/
│ │ ├── screens/
│ │ ├── widgets/
│ │ └── providers/
│ ├── config/
│ │ ├── routes/
│ │ ├── themes/
│ │ └── app_config.dart
│ └── main.dart
├── assets/
│ ├── images/
│ ├── fonts/
│ └── icons/
├── test/
│ ├── unit/
│ ├── widget/
│ └── integration/
└── pubspec.yaml
// Example of core constants
class AppConstants {
static const String appName = 'Explore Uganda';
static const String apiBaseUrl = 'https://api.exploreuganda.app/v1';
static const Duration timeoutDuration = Duration(seconds: 30);
}
// Example of error handling
class AppException implements Exception {
final String message;
final String? code;
AppException(this.message, {this.code});
@override
String toString() => 'AppException: $message (Code: $code)';
}
// Example model
class TouristSite {
final String id;
final String name;
final String description;
final Location location;
final List<String> images;
TouristSite({
required this.id,
required this.name,
required this.description,
required this.location,
required this.images,
});
factory TouristSite.fromJson(Map<String, dynamic> json) {
// JSON parsing implementation
}
}
// Example repository interface
abstract class ITouristSiteRepository {
Future<List<TouristSite>> getAllSites();
Future<TouristSite> getSiteById(String id);
Future<void> addSite(TouristSite site);
Future<void> updateSite(TouristSite site);
Future<void> deleteSite(String id);
}
// Example use case
class GetTouristSiteUseCase {
final ITouristSiteRepository repository;
GetTouristSiteUseCase(this.repository);
Future<TouristSite> execute(String id) async {
return await repository.getSiteById(id);
}
}
// Example screen
class TouristSiteScreen extends StatelessWidget {
final String siteId;
const TouristSiteScreen({required this.siteId});
@override
Widget build(BuildContext context) {
return Consumer<TouristSiteProvider>(
builder: (context, provider, child) {
// UI implementation
},
);
}
}
// Example provider
class TouristSiteProvider extends ChangeNotifier {
final GetTouristSiteUseCase _getTouristSiteUseCase;
TouristSite? _currentSite;
bool _isLoading = false;
TouristSiteProvider(this._getTouristSiteUseCase);
Future<void> loadSite(String id) async {
_isLoading = true;
notifyListeners();
try {
_currentSite = await _getTouristSiteUseCase.execute(id);
} catch (e) {
// Error handling
} finally {
_isLoading = false;
notifyListeners();
}
}
}
# pubspec.yaml
flutter:
assets:
- assets/images/
- assets/icons/
- assets/fonts/
fonts:
- family: Poppins
fonts:
- asset: assets/fonts/Poppins-Regular.ttf
- asset: assets/fonts/Poppins-Bold.ttf
weight: 700
assets/images/
├── attractions/
│ ├── attraction_thumbnail.png
│ └── attraction_detail.png
├── icons/
│ ├── ic_home.svg
│ └── ic_profile.svg
└── backgrounds/
├── bg_splash.png
└── bg_login.png
-
Feature-First Organization
- Group related files by feature
- Keep features independent
- Share common utilities through core
-
File Naming
- Use snake_case for files
- Use PascalCase for classes
- Use camelCase for variables and functions
-
Import Organization
// Dart imports
import 'dart:async';
import 'dart:io';
// Package imports
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// Local imports
import 'package:explore_uganda/core/constants.dart';
import 'package:explore_uganda/data/models/tourist_site.dart';
// Example dependency injection setup
void setupDependencies() {
// Repositories
GetIt.I.registerLazySingleton<ITouristSiteRepository>(
() => TouristSiteRepository(),
);
// Use Cases
GetIt.I.registerLazySingleton(
() => GetTouristSiteUseCase(GetIt.I<ITouristSiteRepository>()),
);
// Providers
GetIt.I.registerFactory(
() => TouristSiteProvider(GetIt.I<GetTouristSiteUseCase>()),
);
}
// Example error handling middleware
Future<T> handleApiError<T>(Future<T> Function() apiCall) async {
try {
return await apiCall();
} on DioError catch (e) {
throw AppException(
'Network error occurred',
code: e.response?.statusCode.toString(),
);
} on Exception catch (e) {
throw AppException('An unexpected error occurred');
}
}