starter_kit_flutter 1.0.0
starter_kit_flutter: ^1.0.0 copied to clipboard
A batteries-included foundational Flutter package for networking, connectivity, storage, theming, app updates, and core utilities.
StarterKit #
A batteries-included Flutter package that sets up common app foundations from one place:
- networking (Dio + interceptors + result-based errors)
- storage (preferences + secure storage)
- theming (light/dark/system with persistence)
- centralized design-system tokens
- feature flags + conditional slots
- safe JSON parsing against API key drift
- debug-only in-app Dev Panel
- connectivity monitoring
- app update/maintenance checks
- utility extensions and validators
The package is designed so teams can reuse the same setup across multiple projects with minimal wiring.
Installation #
dependencies:
starter_kit_flutter:
git:
url: https://gitlab.com/moin-quantana/starter_kit.git
ref: main
Or if published:
dependencies:
starter_kit_flutter: ^1.0.0
Then run:
flutter pub get
App Icon Generation (CLI Wrapper) #
You can generate launcher icons from a logo path using StarterKit's CLI wrapper around flutter_launcher_icons.
- In your app's
pubspec.yaml, add:
dev_dependencies:
flutter_launcher_icons: ^0.14.4
- Run:
flutter pub get
dart run starter_kit_flutter:starter_kit_icons --logo assets/logo/app_logo.png
Optional flags:
# Android only
dart run starter_kit_flutter:starter_kit_icons --logo assets/logo/app_logo.png --no-ios
# iOS only
dart run starter_kit_flutter:starter_kit_icons --logo assets/logo/app_logo.png --no-android
# Adaptive icon options (Android)
dart run starter_kit_flutter:starter_kit_icons --logo assets/logo/app_logo.png --adaptive-bg "#FFFFFF" --adaptive-fg assets/logo/fg.png
Notes:
- Run the command from the consuming Flutter app root (where the app
pubspec.yamlexists). - The command creates a temporary launcher config file and removes it after execution.
- Generated platform icon files are the same output path behavior as
flutter_launcher_icons.
One-Place Setup (Recommended) #
Use StarterKitApp as the single integration point.
import 'package:flutter/material.dart';
import 'package:starter_kit_flutter/starter_kit_flutter.dart';
void main() {
runApp(
StarterKitApp(
config: StarterKitConfig(
networkConfig: const NetworkConfig(
baseUrl: 'https://api.example.com',
authTokenKey: 'auth_token',
),
storageConfig: const StorageConfig(),
themeConfig: const ThemeConfig(
defaultThemeMode: ThemeMode.system,
),
updaterConfig: const UpdaterConfig(
versionCheckUrl: 'https://api.example.com/version',
enableAutoCheck: true,
enableMaintenanceMode: true,
),
modules: const StarterKitModulesConfig(
network: StarterKitModuleMode.enabled,
storage: StarterKitModuleMode.enabled,
theme: StarterKitModuleMode.enabled,
connectivity: StarterKitModuleMode.enabled,
updater: StarterKitModuleMode.enabled,
featureFlags: StarterKitModuleMode.enabled,
),
featureFlagConfig: const FeatureFlagConfig(
initialFlags: {'promo_banner': true},
),
ui: const StarterKitUiConfig(
showConnectivityBanner: true,
showForceUpdateScreen: true,
showMaintenanceScreen: true,
enableGlobalLoader: true,
enableDevPanel: true,
devPanelTrigger: DevPanelTrigger.floatingButton,
),
designSystemConfig: const DesignSystemConfig(
spacing: DesignSpacingConfig(
xxs: 4,
xs: 8,
sm: 12,
md: 16,
lg: 20,
xl: 24,
xxl: 32,
section: 40,
),
radius: DesignRadiusConfig(
sm: 8,
md: 12,
lg: 16,
xl: 24,
),
typography: DesignTypographyConfig(
titleLargeSize: 24,
bodyLargeSize: 16,
bodyMediumSize: 14,
),
),
),
appBuilder: (context, data) {
return MaterialApp(
navigatorKey: data.navigatorKey,
theme: data.lightTheme,
darkTheme: data.darkTheme,
themeMode: data.themeMode,
home: const HomePage(),
);
},
),
);
}
Module Toggles #
StarterKitModulesConfig lets you explicitly control each module:
enabled: register real implementationstub: register stub implementationdisabled: unregister module (accessor falls back to stub behavior if accessed)
Example:
const StarterKitModulesConfig(
network: StarterKitModuleMode.enabled,
storage: StarterKitModuleMode.enabled,
theme: StarterKitModuleMode.enabled,
connectivity: StarterKitModuleMode.enabled,
updater: StarterKitModuleMode.disabled,
)
UI Controls #
StarterKitUiConfig centralizes global UI behavior:
const StarterKitUiConfig(
showConnectivityBanner: true,
showForceUpdateScreen: true,
showMaintenanceScreen: true,
enableGlobalLoader: true,
enableDevPanel: true, // Only active in debug mode
devPanelTrigger: DevPanelTrigger.floatingButton, // or .tripleTap / .both
)
These flags are applied by StarterKit.builder/StarterKitApp.
Safe JSON Parsing (API Breaking-Change Guardrails) #
Use SafeJsonReader / Map extensions to tolerate backend key renames:
final json = response as Map<String, dynamic>;
final id = json.asString(['account_id', 'user_id', 'id'], fallback: '');
final total = json.asInt(['total', 'count'], fallback: 0);
final active = json.asBool(['is_active', 'active', 'enabled'], fallback: false);
final nested = json.asMap(['data', 'payload'], fallback: {});
Or via helper class:
SafeJsonReader.asString(json, ['account_id', 'user_id', 'id']);
Feature Flags #
Enable the module and seed flags from one place:
StarterKitConfig(
featureFlagConfig: const FeatureFlagConfig(
initialFlags: {
'new_checkout': false,
'promo_banner': true,
},
),
modules: const StarterKitModulesConfig(
featureFlags: StarterKitModuleMode.enabled,
// ...other modules
),
)
Access at runtime:
final enabled = StarterKit.I.featureFlags.isEnabled('new_checkout');
await StarterKit.I.featureFlags.setOverride('new_checkout', true);
Render conditionally with StarterKitSlot:
StarterKitSlot(
featureFlag: 'promo_banner',
flaggedChild: const PromoBanner(),
fallback: const SizedBox.shrink(),
child: const SizedBox.shrink(),
)
In-App Dev Panel (Debug Only) #
StarterKitDevPanel is mounted only when kDebugMode == true and enableDevPanel: true.
Tabs:
- Network — request logs, artificial delay, force 500/401/offline
- Storage — search/edit/delete SharedPreferences keys (+ secure read helper)
- Design — live theme mode + spacing/radius token tweaks
- Overlays — simulate offline/maintenance/force-update + toast
- Flags — toggle feature flags + base URL override
Open via floating debug FAB and/or triple-tap top-left corner depending on devPanelTrigger.
ui: const StarterKitUiConfig(
enableDevPanel: true,
devPanelTrigger: DevPanelTrigger.both,
)
Release safety: Dev Panel host and Dio debug interceptor are wrapped in kDebugMode checks so release builds do not activate debug tooling.
Centralized Design System #
DesignSystemConfig gives one-place control for visual consistency across projects:
- spacing scale
- corner radius scale
- shadow/elevation tokens
- typography and font scale
const DesignSystemConfig(
spacing: DesignSpacingConfig(
xxs: 4,
xs: 8,
sm: 12,
md: 16,
lg: 20,
xl: 24,
xxl: 32,
section: 40,
),
radius: DesignRadiusConfig(
sm: 8,
md: 12,
lg: 16,
xl: 24,
),
shadow: DesignShadowConfig(
cardElevation: 2,
),
typography: DesignTypographyConfig(
fontFamily: null, // Set custom family if available
titleLargeSize: 24,
bodyLargeSize: 16,
bodyMediumSize: 14,
labelMediumSize: 12,
),
)
Access tokens anywhere:
final spacing = context.dsSpacing;
final radius = context.dsRadius;
final shadows = context.dsShadow;
final typography = context.dsTypography;
Use reusable spacing widgets (no inline magic numbers):
const VSpace(DesignSpacingSize.md);
const HSpace(DesignSpacingSize.sm);
SKSizedBox.h16;
SKSizedBox.w8;
Service Access #
Access all services through the facade:
final storage = StarterKit.I.storage;
final network = StarterKit.I.network;
final theme = StarterKit.I.theme;
final connectivity = StarterKit.I.connectivity;
final updater = StarterKit.I.updater;
Error Model #
All storage/network operations return Result<T, AppError>.
final result = await StarterKit.I.network.get<Map<String, dynamic>>(
'/users/1',
fromJson: (json) => json as Map<String, dynamic>,
);
result.fold(
(error) => debugPrint('Error: ${error.message}'),
(data) => debugPrint('Success: $data'),
);
Behavior When Modules Are Not Enabled #
StarterKit is explicit and predictable:
- Stub mode: calls succeed structurally but return controlled fallback behavior (e.g.
NETWORK_NOT_INITIALIZED). - Disabled mode: module is not registered; accessor fallback still prevents hard crashes.
- Enabled mode + missing config: init validation throws with a clear configuration error.
Manual/Advanced Path (Backward Compatible) #
You can still initialize manually:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await StarterKit.init(
StarterKitConfig(
networkConfig: const NetworkConfig(baseUrl: 'https://api.example.com'),
storageConfig: const StorageConfig(),
themeConfig: const ThemeConfig(),
),
);
runApp(
MaterialApp(
navigatorKey: StarterKit.navigatorKey,
builder: (context, child) => StarterKit.builder(child: child!),
home: const HomePage(),
),
);
}
If you need to rebuild registrations at runtime:
await StarterKit.init(newConfig, true);
Common Examples #
Storage #
await StarterKit.I.storage.setString('username', 'john_doe');
final username = await StarterKit.I.storage.getString('username');
Theme #
await StarterKit.I.theme.setThemeMode(ThemeMode.dark);
await StarterKit.I.theme.toggleTheme();
Connectivity #
StarterKit.I.connectivity.connectionStream.listen((state) {
debugPrint('Connectivity state: $state');
});
Updater #
final status = await StarterKit.I.updater.checkForUpdates();
debugPrint('Update status: $status');
Public API (Beginner First) #
Primary:
StarterKitAppStarterKitConfigStarterKitModulesConfigStarterKitUiConfigStarterKit.I.<service>
Advanced:
StarterKit.init(...)StarterKit.builder(...)- low-level overlays/utilities/extensions
Requirements #
- Flutter SDK
>=3.0.0 - Dart SDK
>=3.0.0 <4.0.0
License #
MIT
StarterKit #
A batteries-included foundational Flutter package that handles boilerplate infrastructure for any Flutter app. StarterKit provides networking, connectivity monitoring, storage, theming, app updates, and core utilities through a simple, unified API.
Features #
- 🌐 Networking: Dio-based HTTP client with interceptors, retry logic, and unified error handling
- 📡 Connectivity Monitoring: Real-time internet connection state monitoring with automatic UI overlays
- 💾 Storage: Unified storage interface supporting SharedPreferences and Secure Storage
- 🎨 Theming: Light/Dark mode management with persistence
- 🔄 App Updates: Force update and maintenance mode detection
- 🛠️ Utilities: Extensions, validators, and helper functions
- 🏗️ Architecture: Facade pattern with dependency injection (GetIt)
- ✅ Error Handling: Sealed Result type ensuring no raw exceptions reach UI layer
Installation #
Add starter_kit_flutter to your pubspec.yaml:
dependencies:
starter_kit_flutter:
git:
url: https://gitlab.com/moin-quantana/starter_kit.git
ref: main
Or if published to pub.dev:
dependencies:
starter_kit_flutter: ^1.0.0
Then run:
flutter pub get
Quick Start #
1. Initialize StarterKit #
In your main.dart, initialize StarterKit before running the app:
import 'package:flutter/material.dart';
import 'package:starter_kit_flutter/starter_kit_flutter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize StarterKit
await StarterKit.init(
StarterKitConfig(
networkConfig: NetworkConfig(
baseUrl: 'https://api.example.com',
authTokenKey: 'auth_token', // Optional: for auto token injection
),
storageConfig: const StorageConfig(),
themeConfig: const ThemeConfig(
defaultThemeMode: ThemeMode.system,
),
updaterConfig: UpdaterConfig(
versionCheckUrl: 'https://api.example.com/version',
enableAutoCheck: true,
),
),
);
runApp(const MyApp());
}
2. Wrap Your MaterialApp #
Use StarterKit.builder to enable automatic overlays:
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: StarterKit.navigatorKey,
builder: (context, child) => StarterKit.builder(
child: child!,
),
home: const HomePage(),
);
}
}
Usage Examples #
Networking #
All network operations return Result<T, AppError> to ensure safe error handling:
// GET request
final result = await StarterKit.I.network.get<Map<String, dynamic>>(
'/users/123',
fromJson: (json) => json as Map<String, dynamic>,
);
result.fold(
(error) {
// Handle error
print('Error: ${error.message}');
},
(data) {
// Handle success
print('User data: $data');
},
);
// POST request
final createResult = await StarterKit.I.network.post<Map<String, dynamic>>(
'/users',
data: {
'name': 'John Doe',
'email': 'john@example.com',
},
fromJson: (json) => json as Map<String, dynamic>,
);
// PUT, PATCH, DELETE are also available
Storage #
Storage operations also return Result<T, AppError>:
// Save data
final saveResult = await StarterKit.I.storage.setString('username', 'john_doe');
saveResult.fold(
(error) => print('Failed to save: ${error.message}'),
(_) => print('Saved successfully'),
);
// Read data
final readResult = await StarterKit.I.storage.getString('username');
readResult.fold(
(error) => print('Failed to read: ${error.message}'),
(value) => print('Username: $value'),
);
// Other operations
await StarterKit.I.storage.setInt('count', 42);
await StarterKit.I.storage.setBool('isLoggedIn', true);
await StarterKit.I.storage.setDouble('rating', 4.5);
await StarterKit.I.storage.setStringList('tags', ['flutter', 'dart']);
// Note: Secure storage methods are available through StorageServiceImpl
// For tokens, use the authTokenKey in NetworkConfig for automatic injection
Theming #
Switch between light and dark themes:
// Get theme manager
final themeManager = StarterKit.I.theme;
// Toggle theme
await themeManager.toggleTheme();
// Set specific theme
await themeManager.setThemeMode(ThemeMode.dark);
// Access theme data
final lightTheme = themeManager.lightTheme;
final darkTheme = themeManager.darkTheme;
In your MaterialApp:
MaterialApp(
theme: StarterKit.I.theme.lightTheme,
darkTheme: StarterKit.I.theme.darkTheme,
themeMode: StarterKit.I.theme.currentThemeMode,
// ...
)
Connectivity Monitoring #
The connectivity banner appears automatically when connection is lost:
// Listen to connectivity changes
final connectivityService = StarterKit.I.connectivity;
connectivityService.connectionStream.listen((state) {
switch (state) {
case NetworkConnectionState.connected:
print('Connected to internet');
break;
case NetworkConnectionState.disconnected:
print('No internet connection');
break;
case NetworkConnectionState.unknown:
print('Connection state unknown');
break;
}
});
// Check current state
final currentState = connectivityService.currentState;
App Updates #
Force update and maintenance mode are handled automatically:
// Check for updates manually
final updaterService = StarterKit.I.updater;
final status = await updaterService.checkForUpdates();
switch (status) {
case UpdateStatus.upToDate:
print('App is up to date');
break;
case UpdateStatus.updateAvailable:
print('Update available');
break;
case UpdateStatus.forceUpdateRequired:
print('Force update required');
break;
case UpdateStatus.maintenanceMode:
print('App is in maintenance mode');
break;
case UpdateStatus.unknown:
print('Update status unknown');
break;
}
// Listen to update status changes
updaterService.updateStatusStream.listen((status) {
// Handle status changes
});
Utilities #
String Extensions
// Email validation
final email = 'user@example.com';
if (email.isValidEmail) {
print('Valid email');
}
// Capitalize
final name = 'john doe';
print(name.capitalize()); // "John doe"
print(name.capitalizeWords()); // "John Doe"
// Blank check
final text = ' ';
if (text.isBlank) {
print('Text is blank');
}
Context Extensions
// Screen dimensions
final width = context.screenWidth;
final height = context.screenHeight;
// Show snackbars
context.showSnackBar('Hello!');
context.showSuccessSnackBar('Success!');
context.showErrorSnackBar('Error occurred!');
// Access theme
final theme = context.theme;
final textTheme = context.textTheme;
Responsive Sizing
// Height percentage
Container(
height: 20.h(context), // 20% of screen height
width: 50.w(context), // 50% of screen width
child: Text('Responsive'),
)
// Smaller dimension percentage
Container(
size: 10.sp(context), // 10% of smaller dimension
)
Date Extensions
final date = DateTime.now();
// Readable formats
print(date.toReadable()); // "January 1, 2024"
print(date.toShortReadable()); // "Jan 1, 2024"
print(date.toReadableWithTime()); // "January 1, 2024 at 3:30 PM"
// Checks
if (date.isToday) print('Today');
if (date.isYesterday) print('Yesterday');
if (date.isPast) print('In the past');
Validators
Use validators in your FormFields:
TextFormField(
decoration: const InputDecoration(labelText: 'Email'),
validator: emailValidator(),
)
TextFormField(
decoration: const InputDecoration(labelText: 'Password'),
validator: combineValidators([
requiredValidator('Password'),
minLengthValidator(8, 'Password'),
]),
)
TextFormField(
decoration: const InputDecoration(labelText: 'Phone'),
validator: phoneValidator(),
)
Global Loader #
Show a global loading overlay:
// In your widget
GlobalLoader(
isLoading: _isLoading,
child: YourContent(),
)
Toast Overlay #
Show global toasts:
// Using context
ToastOverlay.show(context, 'Hello!');
ToastOverlay.showSuccess(context, 'Success!');
ToastOverlay.showError(context, 'Error!');
ToastOverlay.showWarning(context, 'Warning!');
ToastOverlay.showInfo(context, 'Info!');
// Or using navigator key
final context = StarterKit.navigatorKey.currentContext;
if (context != null) {
ToastOverlay.showSuccess(context, 'Operation completed!');
}
Configuration #
Network Configuration #
NetworkConfig(
baseUrl: 'https://api.example.com',
connectTimeout: 30, // seconds
receiveTimeout: 30,
sendTimeout: 30,
enableLogging: true,
enableRetry: true,
maxRetries: 3,
authTokenKey: 'auth_token', // Optional: enables auth interceptor
headers: {
'X-API-Key': 'your-api-key',
},
)
Storage Configuration #
StorageConfig(
keyPrefix: 'my_app_', // Optional: prefix for all keys
enableSecureStorage: true,
enablePreferencesStorage: true,
)
Theme Configuration #
ThemeConfig(
defaultThemeMode: ThemeMode.system,
lightTheme: ThemeData.light().copyWith(
// Customize light theme
),
darkTheme: ThemeData.dark().copyWith(
// Customize dark theme
),
themeStorageKey: 'theme_mode',
)
Updater Configuration #
UpdaterConfig(
versionCheckUrl: 'https://api.example.com/version',
currentVersion: '1.0.0', // Optional: auto-detected from package_info
checkInterval: 3600, // seconds (1 hour)
enableAutoCheck: true,
enableMaintenanceMode: true,
headers: {
'Authorization': 'Bearer token',
},
)
Error Handling #
All operations return Result<T, AppError>. Handle errors safely:
final result = await StarterKit.I.network.get('/data');
result.fold(
(error) {
// Handle different error types
switch (error) {
case NetworkError():
print('Network error: ${error.message}');
print('Status code: ${error.statusCode}');
break;
case ConnectionError():
print('Connection error: ${error.message}');
break;
case StorageError():
print('Storage error: ${error.message}');
break;
case ValidationError():
print('Validation error: ${error.message}');
break;
default:
print('Unknown error: ${error.message}');
}
},
(data) {
// Handle success
print('Data: $data');
},
);
Architecture #
StarterKit follows a strict facade pattern:
- Single Entry Point:
StarterKit.init()andStarterKit.builder - Service Access: All services via
StarterKit.I.<service> - Dependency Injection: Uses GetIt internally
- Interface-Based: Host app only interacts with interfaces
- Error Safety: All operations return
Result<T, AppError>
Directory Structure #
lib/
├── src/
│ ├── core/
│ │ ├── data/ # Network & Storage implementations
│ │ ├── services/ # Connectivity & Updater services
│ │ ├── design_system/ # Theme & Responsive sizing
│ │ ├── widgets/ # UI Components & Overlays
│ │ ├── models/ # Configs & Response models
│ │ ├── exceptions/ # Result type & Error handling
│ │ ├── utils/ # Extensions & Validators
│ │ └── helpers/ # Service locator & Logger
│ └── facade/
│ └── starter_kit.dart # Main facade
└── starter_kit_flutter.dart # Public export
API Reference #
StarterKit #
static Future<void> init(StarterKitConfig config)- Initialize the packagestatic Widget builder({required Widget child, ...})- Builder widget for overlaysstatic GlobalKey<NavigatorState> navigatorKey- Global navigation keystatic ServiceAccessor I- Service accessor
ServiceAccessor #
IStorageService storage- Storage serviceINetworkClient network- Network clientThemeManager theme- Theme managerIConnectivityService connectivity- Connectivity serviceIUpdaterService updater- Updater service
Requirements #
- Flutter SDK: >=3.0.0
- Dart SDK: >=3.0.0
Dependencies #
dio: ^5.4.0 - HTTP clientget_it: ^7.6.4 - Dependency injectionshared_preferences: ^2.2.2 - Local storageflutter_secure_storage: ^9.0.0 - Secure storageconnectivity_plus: ^5.0.2 - Connectivity monitoringpackage_info_plus: ^5.0.1 - App version info
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Merge Request
License #
This project is licensed under the MIT License.
Support #
For issues, questions, or contributions, please open an issue on GitLab.
Authors #
- Moinuddin - SDE-III - Quantana India
Made with ❤️ for the Flutter community