This commit is contained in:
gitea
2025-11-05 19:01:26 +01:00
commit 5adc1af4ec
59 changed files with 2875 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
/// Configuration class that holds application settings.
///
/// This class contains environment-specific configuration values
/// such as API base URL and logging settings.
class AppConfig {
/// The base URL for API requests.
final String apiBaseUrl;
/// Whether logging is enabled in the application.
final bool enableLogging;
/// Creates an [AppConfig] instance with the provided values.
///
/// [apiBaseUrl] - The base URL for API requests.
/// [enableLogging] - Whether logging should be enabled.
const AppConfig({
required this.apiBaseUrl,
required this.enableLogging,
});
@override
String toString() {
return 'AppConfig(apiBaseUrl: $apiBaseUrl, enableLogging: $enableLogging)';
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is AppConfig &&
other.apiBaseUrl == apiBaseUrl &&
other.enableLogging == enableLogging;
}
@override
int get hashCode => apiBaseUrl.hashCode ^ enableLogging.hashCode;
}
+51
View File
@@ -0,0 +1,51 @@
import 'app_config.dart';
/// Exception thrown when an invalid environment is provided to [ConfigLoader].
class InvalidEnvironmentException implements Exception {
/// The invalid environment that was provided.
final String environment;
/// Creates an [InvalidEnvironmentException] with the provided environment.
InvalidEnvironmentException(this.environment);
@override
String toString() {
return 'InvalidEnvironmentException: Invalid environment "$environment". '
'Valid environments are: dev, prod';
}
}
/// Loads application configuration based on the specified environment.
///
/// This class provides a factory method to load configuration for either
/// 'dev' or 'prod' environments. It throws [InvalidEnvironmentException]
/// if an invalid environment is provided.
class ConfigLoader {
/// Private constructor to prevent instantiation.
ConfigLoader._();
/// Loads configuration for the specified environment.
///
/// [environment] - The environment to load ('dev' or 'prod').
///
/// Returns an [AppConfig] instance for the specified environment.
///
/// Throws [InvalidEnvironmentException] if [environment] is not 'dev' or 'prod'.
static AppConfig load(String environment) {
switch (environment.toLowerCase()) {
case 'dev':
return const AppConfig(
apiBaseUrl: 'https://api-dev.example.com',
enableLogging: true,
);
case 'prod':
return const AppConfig(
apiBaseUrl: 'https://api.example.com',
enableLogging: false,
);
default:
throw InvalidEnvironmentException(environment);
}
}
}
+144
View File
@@ -0,0 +1,144 @@
import 'package:flutter/material.dart';
import 'config/config_loader.dart';
void main() {
// Load configuration based on environment
// In a real app, this would come from environment variables or build config
const String environment = String.fromEnvironment(
'ENV',
defaultValue: 'dev',
);
final config = ConfigLoader.load(environment);
if (config.enableLogging) {
debugPrint('App initialized with config: $config');
}
runApp(const MyApp());
}
/// The root widget of the application.
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
// Load config to display in UI
const String environment = String.fromEnvironment(
'ENV',
defaultValue: 'dev',
);
final config = ConfigLoader.load(environment);
return MaterialApp(
title: 'App Boilerplate',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: Scaffold(
appBar: AppBar(
title: const Text('App Boilerplate'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.check_circle_outline,
size: 64,
color: Colors.green,
),
const SizedBox(height: 24),
const Text(
'Flutter Modular App Boilerplate',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 32),
Card(
margin: const EdgeInsets.symmetric(horizontal: 32),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Configuration',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
_ConfigRow(
label: 'Environment',
value: environment.toUpperCase(),
),
const SizedBox(height: 8),
_ConfigRow(
label: 'API Base URL',
value: config.apiBaseUrl,
),
const SizedBox(height: 8),
_ConfigRow(
label: 'Logging',
value: config.enableLogging ? 'Enabled' : 'Disabled',
),
],
),
),
),
const SizedBox(height: 32),
Text(
'Phase 0: Project Setup Complete ✓',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Colors.grey,
),
),
],
),
),
),
);
}
}
/// Widget to display a configuration row.
class _ConfigRow extends StatelessWidget {
final String label;
final String value;
const _ConfigRow({
required this.label,
required this.value,
});
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 100,
child: Text(
'$label:',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Colors.grey,
),
),
),
Expanded(
child: Text(
value,
style: Theme.of(context).textTheme.bodyMedium,
),
),
],
);
}
}