Project Structure - utourismboard/explore-uganda-application-documentation GitHub Wiki

Project Structure Guide

Table of Contents

  1. Directory Structure
  2. Core Components
  3. State Management
  4. Asset Organization
  5. Best Practices

Directory Structure

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

Core Components

Core Layer

// 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)';
}

Data Layer

// 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
  }
}

Domain Layer

// 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);
  }
}

Presentation Layer

// 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
      },
    );
  }
}

State Management

Provider Setup

// 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();
    }
  }
}

Asset Organization

Asset Management

# 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

Image Naming Convention

assets/images/
├── attractions/
│   ├── attraction_thumbnail.png
│   └── attraction_detail.png
├── icons/
│   ├── ic_home.svg
│   └── ic_profile.svg
└── backgrounds/
    ├── bg_splash.png
    └── bg_login.png

Best Practices

Code Organization

  1. Feature-First Organization

    • Group related files by feature
    • Keep features independent
    • Share common utilities through core
  2. File Naming

    • Use snake_case for files
    • Use PascalCase for classes
    • Use camelCase for variables and functions
  3. 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';

Dependency Injection

// 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>()),
  );
}

Error Handling

// 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');
  }
}

Related Documentation

⚠️ **GitHub.com Fallback** ⚠️