diff --git a/ios/build/Logs/Build/LogStoreManifest.plist b/ios/build/Logs/Build/LogStoreManifest.plist
new file mode 100644
index 0000000..f38de44
--- /dev/null
+++ b/ios/build/Logs/Build/LogStoreManifest.plist
@@ -0,0 +1,10 @@
+
+
+
+
+ logFormatVersion
+ 11
+ logs
+
+
+
diff --git a/ios/build/Logs/Launch/LogStoreManifest.plist b/ios/build/Logs/Launch/LogStoreManifest.plist
new file mode 100644
index 0000000..f38de44
--- /dev/null
+++ b/ios/build/Logs/Launch/LogStoreManifest.plist
@@ -0,0 +1,10 @@
+
+
+
+
+ logFormatVersion
+ 11
+ logs
+
+
+
diff --git a/ios/build/Logs/Localization/LogStoreManifest.plist b/ios/build/Logs/Localization/LogStoreManifest.plist
new file mode 100644
index 0000000..f38de44
--- /dev/null
+++ b/ios/build/Logs/Localization/LogStoreManifest.plist
@@ -0,0 +1,10 @@
+
+
+
+
+ logFormatVersion
+ 11
+ logs
+
+
+
diff --git a/ios/build/Logs/Package/LogStoreManifest.plist b/ios/build/Logs/Package/LogStoreManifest.plist
new file mode 100644
index 0000000..f38de44
--- /dev/null
+++ b/ios/build/Logs/Package/LogStoreManifest.plist
@@ -0,0 +1,10 @@
+
+
+
+
+ logFormatVersion
+ 11
+ logs
+
+
+
diff --git a/ios/build/Logs/Test/LogStoreManifest.plist b/ios/build/Logs/Test/LogStoreManifest.plist
new file mode 100644
index 0000000..f38de44
--- /dev/null
+++ b/ios/build/Logs/Test/LogStoreManifest.plist
@@ -0,0 +1,10 @@
+
+
+
+
+ logFormatVersion
+ 11
+ logs
+
+
+
diff --git a/lib/components/dark_mode_map.dart b/lib/components/dark_mode_map.dart
new file mode 100644
index 0000000..69e9dfa
--- /dev/null
+++ b/lib/components/dark_mode_map.dart
@@ -0,0 +1,456 @@
+import 'package:flutter/material.dart';
+import 'package:google_navigation_flutter/google_navigation_flutter.dart';
+import '../models/delivery.dart';
+import '../theme/color_system.dart';
+
+/// Enhanced dark-mode aware map component with custom styling
+class DarkModeMapComponent extends StatefulWidget {
+ final List deliveries;
+ final Delivery? selectedDelivery;
+ final ValueChanged? onDeliverySelected;
+
+ const DarkModeMapComponent({
+ super.key,
+ required this.deliveries,
+ this.selectedDelivery,
+ this.onDeliverySelected,
+ });
+
+ @override
+ State createState() => _DarkModeMapComponentState();
+}
+
+class _DarkModeMapComponentState extends State {
+ GoogleNavigationViewController? _navigationController;
+ bool _isNavigating = false;
+ LatLng? _destinationLocation;
+
+ @override
+ void initState() {
+ super.initState();
+ _initializeNavigation();
+ }
+
+ Future _initializeNavigation() async {
+ try {
+ final termsAccepted = await GoogleMapsNavigator.areTermsAccepted();
+ if (!termsAccepted) {
+ await GoogleMapsNavigator.showTermsAndConditionsDialog(
+ 'Plan B Logistics',
+ 'com.goutezplanb.planbLogistic',
+ );
+ }
+ await GoogleMapsNavigator.initializeNavigationSession();
+ } catch (e) {
+ debugPrint('Map initialization error: $e');
+ }
+ }
+
+ @override
+ void didUpdateWidget(DarkModeMapComponent oldWidget) {
+ super.didUpdateWidget(oldWidget);
+ if (oldWidget.selectedDelivery != widget.selectedDelivery) {
+ _updateDestination();
+ }
+ }
+
+ void _updateDestination() {
+ if (widget.selectedDelivery != null) {
+ final address = widget.selectedDelivery!.deliveryAddress;
+ if (address?.latitude != null && address?.longitude != null) {
+ setState(() {
+ _destinationLocation = LatLng(
+ latitude: address!.latitude!,
+ longitude: address.longitude!,
+ );
+ });
+ _navigateToLocation(_destinationLocation!);
+ }
+ }
+ }
+
+ Future _navigateToLocation(LatLng location) async {
+ if (_navigationController == null) return;
+ try {
+ await _navigationController!.animateCamera(
+ CameraUpdate.newLatLngZoom(location, 15),
+ );
+ } catch (e) {
+ debugPrint('Camera navigation error: $e');
+ }
+ }
+
+ Future _applyDarkModeStyle() async {
+ if (_navigationController == null) return;
+ try {
+ // Apply dark mode style configuration for Google Maps
+ // This reduces eye strain in low-light environments
+ final isDarkMode = Theme.of(context).brightness == Brightness.dark;
+ if (isDarkMode) {
+ // Dark map style with warm accent colors
+ await _navigationController!.setMapStyle(_getDarkMapStyle());
+ }
+ } catch (e) {
+ debugPrint('Error applying map style: $e');
+ }
+ }
+
+ String _getDarkMapStyle() {
+ // Google Maps style JSON for dark mode with warm accents
+ return '''[
+ {
+ "elementType": "geometry",
+ "stylers": [{"color": "#212121"}]
+ },
+ {
+ "elementType": "labels.icon",
+ "stylers": [{"visibility": "off"}]
+ },
+ {
+ "elementType": "labels.text.fill",
+ "stylers": [{"color": "#757575"}]
+ },
+ {
+ "elementType": "labels.text.stroke",
+ "stylers": [{"color": "#212121"}]
+ },
+ {
+ "featureType": "administrative",
+ "elementType": "geometry",
+ "stylers": [{"color": "#757575"}]
+ },
+ {
+ "featureType": "administrative.country",
+ "elementType": "labels.text.fill",
+ "stylers": [{"color": "#9e9e9e"}]
+ },
+ {
+ "featureType": "administrative.land_parcel",
+ "stylers": [{"visibility": "off"}]
+ },
+ {
+ "featureType": "administrative.locality",
+ "elementType": "labels.text.fill",
+ "stylers": [{"color": "#bdbdbd"}]
+ },
+ {
+ "featureType": "administrative.neighborhood",
+ "stylers": [{"visibility": "off"}]
+ },
+ {
+ "featureType": "administrative.province",
+ "elementType": "labels.text.fill",
+ "stylers": [{"color": "#9e9e9e"}]
+ },
+ {
+ "featureType": "landscape",
+ "elementType": "geometry",
+ "stylers": [{"color": "#000000"}]
+ },
+ {
+ "featureType": "poi",
+ "elementType": "geometry",
+ "stylers": [{"color": "#383838"}]
+ },
+ {
+ "featureType": "poi",
+ "elementType": "labels.text.fill",
+ "stylers": [{"color": "#9e9e9e"}]
+ },
+ {
+ "featureType": "poi.park",
+ "elementType": "geometry",
+ "stylers": [{"color": "#181818"}]
+ },
+ {
+ "featureType": "poi.park",
+ "elementType": "labels.text.fill",
+ "stylers": [{"color": "#616161"}]
+ },
+ {
+ "featureType": "road",
+ "elementType": "geometry.fill",
+ "stylers": [{"color": "#2c2c2c"}]
+ },
+ {
+ "featureType": "road",
+ "elementType": "labels.text.fill",
+ "stylers": [{"color": "#8a8a8a"}]
+ },
+ {
+ "featureType": "road.arterial",
+ "elementType": "geometry",
+ "stylers": [{"color": "#373737"}]
+ },
+ {
+ "featureType": "road.highway",
+ "elementType": "geometry",
+ "stylers": [{"color": "#3c3c3c"}]
+ },
+ {
+ "featureType": "road.highway.controlled_access",
+ "elementType": "geometry",
+ "stylers": [{"color": "#4e4e4e"}]
+ },
+ {
+ "featureType": "road.local",
+ "elementType": "labels.text.fill",
+ "stylers": [{"color": "#616161"}]
+ },
+ {
+ "featureType": "transit",
+ "elementType": "labels.text.fill",
+ "stylers": [{"color": "#757575"}]
+ },
+ {
+ "featureType": "water",
+ "elementType": "geometry",
+ "stylers": [{"color": "#0c1221"}]
+ },
+ {
+ "featureType": "water",
+ "elementType": "labels.text.fill",
+ "stylers": [{"color": "#3d3d3d"}]
+ }
+ ]''';
+ }
+
+ Future _startNavigation() async {
+ if (_destinationLocation == null) return;
+ try {
+ final waypoint = NavigationWaypoint.withLatLngTarget(
+ title: widget.selectedDelivery?.name ?? 'Destination',
+ target: _destinationLocation!,
+ );
+
+ final destinations = Destinations(
+ waypoints: [waypoint],
+ displayOptions: NavigationDisplayOptions(showDestinationMarkers: true),
+ );
+
+ await GoogleMapsNavigator.setDestinations(destinations);
+ await GoogleMapsNavigator.startGuidance();
+
+ setState(() {
+ _isNavigating = true;
+ });
+ } catch (e) {
+ debugPrint('Navigation start error: $e');
+ if (mounted) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text('Navigation error: $e')),
+ );
+ }
+ }
+ }
+
+ Future _stopNavigation() async {
+ try {
+ await GoogleMapsNavigator.stopGuidance();
+ await GoogleMapsNavigator.clearDestinations();
+ setState(() {
+ _isNavigating = false;
+ });
+ } catch (e) {
+ debugPrint('Navigation stop error: $e');
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final initialPosition = widget.selectedDelivery?.deliveryAddress != null &&
+ widget.selectedDelivery!.deliveryAddress!.latitude != null &&
+ widget.selectedDelivery!.deliveryAddress!.longitude != null
+ ? LatLng(
+ latitude: widget.selectedDelivery!.deliveryAddress!.latitude!,
+ longitude: widget.selectedDelivery!.deliveryAddress!.longitude!,
+ )
+ : const LatLng(latitude: 45.5017, longitude: -73.5673);
+
+ return Stack(
+ children: [
+ GoogleMapsNavigationView(
+ onViewCreated: (controller) {
+ _navigationController = controller;
+ _applyDarkModeStyle();
+ controller.animateCamera(
+ CameraUpdate.newLatLngZoom(initialPosition, 12),
+ );
+ },
+ initialCameraPosition: CameraPosition(
+ target: initialPosition,
+ zoom: 12,
+ ),
+ ),
+ // Custom dark-themed controls overlay
+ Positioned(
+ bottom: 16,
+ right: 16,
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ // Start navigation button
+ if (widget.selectedDelivery != null && !_isNavigating)
+ _buildActionButton(
+ label: 'Navigate',
+ icon: Icons.directions,
+ onPressed: _startNavigation,
+ color: SvrntyColors.crimsonRed,
+ ),
+ if (_isNavigating)
+ _buildActionButton(
+ label: 'Stop',
+ icon: Icons.stop,
+ onPressed: _stopNavigation,
+ color: Colors.grey[600]!,
+ ),
+ ],
+ ),
+ ),
+ // Selected delivery info panel (dark themed)
+ if (widget.selectedDelivery != null)
+ Positioned(
+ top: 0,
+ left: 0,
+ right: 0,
+ child: Container(
+ decoration: BoxDecoration(
+ color: Theme.of(context).brightness == Brightness.dark
+ ? const Color(0xFF14161A)
+ : Colors.white,
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black.withOpacity(0.2),
+ blurRadius: 8,
+ offset: const Offset(0, 2),
+ ),
+ ],
+ ),
+ padding: const EdgeInsets.symmetric(
+ horizontal: 16,
+ vertical: 12,
+ ),
+ child: Row(
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ widget.selectedDelivery!.name,
+ style: Theme.of(context)
+ .textTheme
+ .titleMedium
+ ?.copyWith(fontWeight: FontWeight.w600),
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ ),
+ const SizedBox(height: 4),
+ Text(
+ widget.selectedDelivery!.deliveryAddress
+ ?.formattedAddress ??
+ 'No address',
+ style: Theme.of(context)
+ .textTheme
+ .bodySmall
+ ?.copyWith(
+ color: Theme.of(context).brightness ==
+ Brightness.dark
+ ? Colors.grey[400]
+ : Colors.grey[600],
+ ),
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(width: 8),
+ // Status indicator
+ Container(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 8,
+ vertical: 4,
+ ),
+ decoration: BoxDecoration(
+ color: widget.selectedDelivery!.delivered
+ ? SvrntyColors.statusCompleted
+ : SvrntyColors.statusPending,
+ borderRadius: BorderRadius.circular(4),
+ ),
+ child: Text(
+ widget.selectedDelivery!.delivered
+ ? 'Delivered'
+ : 'Pending',
+ style: Theme.of(context)
+ .textTheme
+ .labelSmall
+ ?.copyWith(
+ color: Colors.white,
+ fontWeight: FontWeight.w600,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+
+ Widget _buildActionButton({
+ required String label,
+ required IconData icon,
+ required VoidCallback onPressed,
+ required Color color,
+ }) {
+ return Container(
+ margin: const EdgeInsets.only(bottom: 8),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(8),
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black.withOpacity(0.3),
+ blurRadius: 4,
+ offset: const Offset(0, 2),
+ ),
+ ],
+ ),
+ child: Material(
+ color: color,
+ borderRadius: BorderRadius.circular(8),
+ child: InkWell(
+ onTap: onPressed,
+ borderRadius: BorderRadius.circular(8),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 12,
+ vertical: 8,
+ ),
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Icon(
+ icon,
+ color: Colors.white,
+ size: 18,
+ ),
+ const SizedBox(width: 6),
+ Text(
+ label,
+ style: const TextStyle(
+ color: Colors.white,
+ fontWeight: FontWeight.w500,
+ fontSize: 14,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/components/delivery_list_item.dart b/lib/components/delivery_list_item.dart
new file mode 100644
index 0000000..21da91f
--- /dev/null
+++ b/lib/components/delivery_list_item.dart
@@ -0,0 +1,293 @@
+import 'package:flutter/material.dart';
+import '../models/delivery.dart';
+import '../theme/animation_system.dart';
+import '../theme/color_system.dart';
+
+class DeliveryListItem extends StatefulWidget {
+ final Delivery delivery;
+ final bool isSelected;
+ final VoidCallback onTap;
+ final VoidCallback? onCall;
+ final int? animationIndex;
+
+ const DeliveryListItem({
+ super.key,
+ required this.delivery,
+ required this.isSelected,
+ required this.onTap,
+ this.onCall,
+ this.animationIndex,
+ });
+
+ @override
+ State createState() => _DeliveryListItemState();
+}
+
+class _DeliveryListItemState extends State
+ with SingleTickerProviderStateMixin {
+ late AnimationController _controller;
+ late Animation _slideAnimation;
+ late Animation _fadeAnimation;
+ late Animation _scaleAnimation;
+ bool _isHovered = false;
+
+ @override
+ void initState() {
+ super.initState();
+ _controller = AnimationController(
+ duration: const Duration(milliseconds: 400),
+ vsync: this,
+ );
+
+ final staggerDelay = Duration(
+ milliseconds:
+ (widget.animationIndex ?? 0) * AppAnimations.staggerDelayMs,
+ );
+
+ Future.delayed(staggerDelay, () {
+ if (mounted) {
+ _controller.forward();
+ }
+ });
+
+ _slideAnimation = Tween(begin: 20, end: 0).animate(
+ CurvedAnimation(parent: _controller, curve: Curves.easeOut),
+ );
+
+ _fadeAnimation = Tween(begin: 0, end: 1).animate(
+ CurvedAnimation(parent: _controller, curve: Curves.easeOut),
+ );
+
+ _scaleAnimation = Tween(begin: 0.95, end: 1).animate(
+ CurvedAnimation(parent: _controller, curve: Curves.easeOut),
+ );
+ }
+
+ @override
+ void dispose() {
+ _controller.dispose();
+ super.dispose();
+ }
+
+ Color _getStatusColor(Delivery delivery) {
+ if (delivery.isSkipped == true) return SvrntyColors.statusSkipped;
+ if (delivery.delivered == true) return SvrntyColors.statusCompleted;
+ return SvrntyColors.statusPending;
+ }
+
+ IconData _getStatusIcon(Delivery delivery) {
+ if (delivery.isSkipped) return Icons.skip_next;
+ if (delivery.delivered) return Icons.check_circle;
+ return Icons.schedule;
+ }
+
+ String _getStatusLabel(Delivery delivery) {
+ if (delivery.isSkipped) return 'Skipped';
+ if (delivery.delivered) return 'Delivered';
+ return 'Pending';
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final isDark = Theme.of(context).brightness == Brightness.dark;
+ final statusColor = _getStatusColor(widget.delivery);
+
+ return ScaleTransition(
+ scale: _scaleAnimation,
+ child: FadeTransition(
+ opacity: _fadeAnimation,
+ child: Transform.translate(
+ offset: Offset(_slideAnimation.value, 0),
+ child: MouseRegion(
+ onEnter: (_) => setState(() => _isHovered = true),
+ onExit: (_) => setState(() => _isHovered = false),
+ child: GestureDetector(
+ onTap: widget.onTap,
+ child: AnimatedContainer(
+ duration: AppAnimations.durationFast,
+ margin: const EdgeInsets.symmetric(
+ horizontal: 12,
+ vertical: 6,
+ ),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(10),
+ color: _isHovered || widget.isSelected
+ ? (isDark
+ ? Colors.grey[800]
+ : Colors.grey[100])
+ : Colors.transparent,
+ boxShadow: _isHovered || widget.isSelected
+ ? [
+ BoxShadow(
+ color: Colors.black.withOpacity(
+ isDark ? 0.3 : 0.08,
+ ),
+ blurRadius: 8,
+ offset: const Offset(0, 4),
+ ),
+ ]
+ : [],
+ ),
+ padding: const EdgeInsets.all(12),
+ child: Column(
+ children: [
+ // Main delivery info row
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Left accent bar
+ Container(
+ width: 4,
+ height: 60,
+ decoration: BoxDecoration(
+ color: statusColor,
+ borderRadius: BorderRadius.circular(2),
+ ),
+ ),
+ const SizedBox(width: 12),
+ // Delivery info
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Name
+ Text(
+ widget.delivery.name,
+ style: Theme.of(context)
+ .textTheme
+ .titleSmall
+ ?.copyWith(
+ fontWeight: FontWeight.w600,
+ ),
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ ),
+ const SizedBox(height: 4),
+ // Address
+ Text(
+ widget.delivery.deliveryAddress
+ ?.formattedAddress ??
+ 'No address',
+ style: Theme.of(context)
+ .textTheme
+ .bodySmall
+ ?.copyWith(
+ color: isDark
+ ? Colors.grey[400]
+ : Colors.grey[600],
+ ),
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ ),
+ const SizedBox(height: 4),
+ // Order count
+ Text(
+ '${widget.delivery.orders.length} order${widget.delivery.orders.length != 1 ? 's' : ''}',
+ style: Theme.of(context)
+ .textTheme
+ .labelSmall
+ ?.copyWith(
+ fontSize: 10,
+ color: isDark
+ ? Colors.grey[500]
+ : Colors.grey[500],
+ ),
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(width: 8),
+ // Status badge + Call button
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: [
+ // Status badge
+ Container(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 8,
+ vertical: 4,
+ ),
+ decoration: BoxDecoration(
+ color: statusColor,
+ borderRadius: BorderRadius.circular(6),
+ ),
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Icon(
+ _getStatusIcon(widget.delivery),
+ color: Colors.white,
+ size: 12,
+ ),
+ const SizedBox(width: 4),
+ Text(
+ _getStatusLabel(widget.delivery),
+ style: Theme.of(context)
+ .textTheme
+ .labelSmall
+ ?.copyWith(
+ color: Colors.white,
+ fontWeight: FontWeight.w600,
+ fontSize: 10,
+ ),
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: 6),
+ // Call button (if contact exists)
+ if (widget.delivery.orders.isNotEmpty &&
+ widget.delivery.orders.first.contact != null)
+ GestureDetector(
+ onTap: widget.onCall,
+ child: Container(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 8,
+ vertical: 4,
+ ),
+ decoration: BoxDecoration(
+ color: Colors.grey[300],
+ borderRadius: BorderRadius.circular(6),
+ ),
+ child: Icon(
+ Icons.phone,
+ color: Colors.grey[700],
+ size: 14,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ // Total amount (if present)
+ if (widget.delivery.orders.isNotEmpty &&
+ widget.delivery.orders.first.totalAmount != null)
+ Padding(
+ padding: const EdgeInsets.only(top: 8, left: 16),
+ child: Align(
+ alignment: Alignment.centerLeft,
+ child: Text(
+ 'Total: \$${widget.delivery.orders.first.totalAmount!.toStringAsFixed(2)}',
+ style: Theme.of(context)
+ .textTheme
+ .labelSmall
+ ?.copyWith(
+ color: isDark
+ ? Colors.amber[300]
+ : Colors.amber[700],
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/components/premium_route_card.dart b/lib/components/premium_route_card.dart
new file mode 100644
index 0000000..d9c82c3
--- /dev/null
+++ b/lib/components/premium_route_card.dart
@@ -0,0 +1,252 @@
+import 'package:flutter/material.dart';
+import '../models/delivery_route.dart';
+import '../theme/animation_system.dart';
+import '../theme/color_system.dart';
+
+class PremiumRouteCard extends StatefulWidget {
+ final DeliveryRoute route;
+ final VoidCallback onTap;
+ final EdgeInsets padding;
+
+ const PremiumRouteCard({
+ super.key,
+ required this.route,
+ required this.onTap,
+ this.padding = const EdgeInsets.all(16.0),
+ });
+
+ @override
+ State createState() => _PremiumRouteCardState();
+}
+
+class _PremiumRouteCardState extends State
+ with SingleTickerProviderStateMixin {
+ late AnimationController _controller;
+ late Animation _scaleAnimation;
+ late Animation _shadowAnimation;
+ bool _isHovered = false;
+
+ @override
+ void initState() {
+ super.initState();
+ _controller = AnimationController(
+ duration: AppAnimations.durationFast,
+ vsync: this,
+ );
+
+ _scaleAnimation = Tween(begin: 1.0, end: 1.02).animate(
+ CurvedAnimation(parent: _controller, curve: Curves.easeOut),
+ );
+
+ _shadowAnimation = Tween(begin: 2.0, end: 8.0).animate(
+ CurvedAnimation(parent: _controller, curve: Curves.easeOut),
+ );
+ }
+
+ @override
+ void dispose() {
+ _controller.dispose();
+ super.dispose();
+ }
+
+ void _onHoverEnter() {
+ setState(() => _isHovered = true);
+ _controller.forward();
+ }
+
+ void _onHoverExit() {
+ setState(() => _isHovered = false);
+ _controller.reverse();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final isDark = Theme.of(context).brightness == Brightness.dark;
+ final progressPercentage = (widget.route.progress * 100).toStringAsFixed(0);
+ final isCompleted = widget.route.progress >= 1.0;
+
+ return MouseRegion(
+ onEnter: (_) => _onHoverEnter(),
+ onExit: (_) => _onHoverExit(),
+ child: GestureDetector(
+ onTap: widget.onTap,
+ child: ScaleTransition(
+ scale: _scaleAnimation,
+ child: AnimatedBuilder(
+ animation: _shadowAnimation,
+ builder: (context, child) {
+ return Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(12),
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black.withOpacity(isDark ? 0.3 : 0.1),
+ blurRadius: _shadowAnimation.value,
+ offset: Offset(0, _shadowAnimation.value * 0.5),
+ ),
+ ],
+ ),
+ child: child,
+ );
+ },
+ child: Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(12),
+ color: isDark
+ ? const Color(0xFF14161A)
+ : const Color(0xFFFAFAFC),
+ ),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ // Left accent bar + Header
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Accent bar
+ Container(
+ width: 4,
+ height: double.infinity,
+ decoration: BoxDecoration(
+ color: isCompleted
+ ? SvrntyColors.statusCompleted
+ : SvrntyColors.crimsonRed,
+ borderRadius: const BorderRadius.only(
+ topLeft: Radius.circular(12),
+ bottomLeft: Radius.circular(12),
+ ),
+ ),
+ ),
+ // Content
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.fromLTRB(16, 16, 16, 12),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Route name
+ Text(
+ widget.route.name,
+ style:
+ Theme.of(context).textTheme.titleLarge?.copyWith(
+ fontWeight: FontWeight.w600,
+ letterSpacing: -0.3,
+ ),
+ maxLines: 2,
+ overflow: TextOverflow.ellipsis,
+ ),
+ const SizedBox(height: 4),
+ // Completion text
+ RichText(
+ text: TextSpan(
+ children: [
+ TextSpan(
+ text:
+ '${widget.route.deliveredCount}/${widget.route.deliveriesCount}',
+ style: Theme.of(context)
+ .textTheme
+ .bodySmall
+ ?.copyWith(
+ fontWeight: FontWeight.w600,
+ color: isCompleted
+ ? SvrntyColors.statusCompleted
+ : SvrntyColors.crimsonRed,
+ ),
+ ),
+ TextSpan(
+ text: ' completed',
+ style: Theme.of(context)
+ .textTheme
+ .bodySmall
+ ?.copyWith(
+ color: isDark
+ ? Colors.grey[400]
+ : Colors.grey[600],
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ // Delivery count badge
+ Padding(
+ padding: const EdgeInsets.fromLTRB(0, 16, 16, 0),
+ child: Container(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 8,
+ vertical: 4,
+ ),
+ decoration: BoxDecoration(
+ color: SvrntyColors.crimsonRed,
+ borderRadius: BorderRadius.circular(6),
+ ),
+ child: Text(
+ widget.route.deliveriesCount.toString(),
+ style: Theme.of(context)
+ .textTheme
+ .labelSmall
+ ?.copyWith(
+ color: Colors.white,
+ fontWeight: FontWeight.w600,
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ // Progress section
+ Padding(
+ padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Progress percentage text
+ Text(
+ '$progressPercentage% progress',
+ style: Theme.of(context).textTheme.labelSmall?.copyWith(
+ color: isDark
+ ? Colors.grey[400]
+ : Colors.grey[600],
+ fontSize: 11,
+ letterSpacing: 0.3,
+ ),
+ ),
+ const SizedBox(height: 8),
+ // Progress bar with gradient
+ ClipRRect(
+ borderRadius: BorderRadius.circular(4),
+ child: Container(
+ height: 6,
+ decoration: BoxDecoration(
+ color: isDark
+ ? Colors.grey[800]
+ : Colors.grey[200],
+ borderRadius: BorderRadius.circular(4),
+ ),
+ child: LinearProgressIndicator(
+ value: widget.route.progress,
+ backgroundColor: Colors.transparent,
+ valueColor: AlwaysStoppedAnimation(
+ isCompleted
+ ? SvrntyColors.statusCompleted
+ : SvrntyColors.crimsonRed,
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart
deleted file mode 100644
index 5493696..0000000
--- a/lib/l10n/app_localizations.dart
+++ /dev/null
@@ -1,368 +0,0 @@
-import 'dart:async';
-
-import 'package:flutter/foundation.dart';
-import 'package:flutter/widgets.dart';
-import 'package:flutter_localizations/flutter_localizations.dart';
-import 'package:intl/intl.dart' as intl;
-
-import 'app_localizations_en.dart';
-import 'app_localizations_fr.dart';
-
-// ignore_for_file: type=lint
-
-/// Callers can lookup localized strings with an instance of AppLocalizations
-/// returned by `AppLocalizations.of(context)`.
-///
-/// Applications need to include `AppLocalizations.delegate()` in their app's
-/// `localizationDelegates` list, and the locales they support in the app's
-/// `supportedLocales` list. For example:
-///
-/// ```dart
-/// import 'l10n/app_localizations.dart';
-///
-/// return MaterialApp(
-/// localizationsDelegates: AppLocalizations.localizationsDelegates,
-/// supportedLocales: AppLocalizations.supportedLocales,
-/// home: MyApplicationHome(),
-/// );
-/// ```
-///
-/// ## Update pubspec.yaml
-///
-/// Please make sure to update your pubspec.yaml to include the following
-/// packages:
-///
-/// ```yaml
-/// dependencies:
-/// # Internationalization support.
-/// flutter_localizations:
-/// sdk: flutter
-/// intl: any # Use the pinned version from flutter_localizations
-///
-/// # Rest of dependencies
-/// ```
-///
-/// ## iOS Applications
-///
-/// iOS applications define key application metadata, including supported
-/// locales, in an Info.plist file that is built into the application bundle.
-/// To configure the locales supported by your app, you’ll need to edit this
-/// file.
-///
-/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file.
-/// Then, in the Project Navigator, open the Info.plist file under the Runner
-/// project’s Runner folder.
-///
-/// Next, select the Information Property List item, select Add Item from the
-/// Editor menu, then select Localizations from the pop-up menu.
-///
-/// Select and expand the newly-created Localizations item then, for each
-/// locale your application supports, add a new item and select the locale
-/// you wish to add from the pop-up menu in the Value field. This list should
-/// be consistent with the languages listed in the AppLocalizations.supportedLocales
-/// property.
-abstract class AppLocalizations {
- AppLocalizations(String locale)
- : localeName = intl.Intl.canonicalizedLocale(locale.toString());
-
- final String localeName;
-
- static AppLocalizations of(BuildContext context) {
- return Localizations.of(context, AppLocalizations)!;
- }
-
- static const LocalizationsDelegate delegate =
- _AppLocalizationsDelegate();
-
- /// A list of this localizations delegate along with the default localizations
- /// delegates.
- ///
- /// Returns a list of localizations delegates containing this delegate along with
- /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
- /// and GlobalWidgetsLocalizations.delegate.
- ///
- /// Additional delegates can be added by appending to this list in
- /// MaterialApp. This list does not have to be used at all if a custom list
- /// of delegates is preferred or required.
- static const List> localizationsDelegates =
- >[
- delegate,
- GlobalMaterialLocalizations.delegate,
- GlobalCupertinoLocalizations.delegate,
- GlobalWidgetsLocalizations.delegate,
- ];
-
- /// A list of this localizations delegate's supported locales.
- static const List supportedLocales = [
- Locale('en'),
- Locale('fr'),
- ];
-
- /// No description provided for @appTitle.
- ///
- /// In en, this message translates to:
- /// **'Plan B Logistics'**
- String get appTitle;
-
- /// No description provided for @appDescription.
- ///
- /// In en, this message translates to:
- /// **'Delivery Management System'**
- String get appDescription;
-
- /// No description provided for @loginWithKeycloak.
- ///
- /// In en, this message translates to:
- /// **'Login with Keycloak'**
- String get loginWithKeycloak;
-
- /// No description provided for @deliveryRoutes.
- ///
- /// In en, this message translates to:
- /// **'Delivery Routes'**
- String get deliveryRoutes;
-
- /// No description provided for @routes.
- ///
- /// In en, this message translates to:
- /// **'Routes'**
- String get routes;
-
- /// No description provided for @deliveries.
- ///
- /// In en, this message translates to:
- /// **'Deliveries'**
- String get deliveries;
-
- /// No description provided for @settings.
- ///
- /// In en, this message translates to:
- /// **'Settings'**
- String get settings;
-
- /// No description provided for @profile.
- ///
- /// In en, this message translates to:
- /// **'Profile'**
- String get profile;
-
- /// No description provided for @logout.
- ///
- /// In en, this message translates to:
- /// **'Logout'**
- String get logout;
-
- /// No description provided for @completed.
- ///
- /// In en, this message translates to:
- /// **'Completed'**
- String get completed;
-
- /// No description provided for @pending.
- ///
- /// In en, this message translates to:
- /// **'Pending'**
- String get pending;
-
- /// No description provided for @todo.
- ///
- /// In en, this message translates to:
- /// **'To Do'**
- String get todo;
-
- /// No description provided for @delivered.
- ///
- /// In en, this message translates to:
- /// **'Delivered'**
- String get delivered;
-
- /// No description provided for @newCustomer.
- ///
- /// In en, this message translates to:
- /// **'New Customer'**
- String get newCustomer;
-
- /// No description provided for @items.
- ///
- /// In en, this message translates to:
- /// **'{count} items'**
- String items(int count);
-
- /// No description provided for @moneyCurrency.
- ///
- /// In en, this message translates to:
- /// **'{amount} MAD'**
- String moneyCurrency(double amount);
-
- /// No description provided for @call.
- ///
- /// In en, this message translates to:
- /// **'Call'**
- String get call;
-
- /// No description provided for @map.
- ///
- /// In en, this message translates to:
- /// **'Map'**
- String get map;
-
- /// No description provided for @more.
- ///
- /// In en, this message translates to:
- /// **'More'**
- String get more;
-
- /// No description provided for @markAsCompleted.
- ///
- /// In en, this message translates to:
- /// **'Mark as Completed'**
- String get markAsCompleted;
-
- /// No description provided for @markAsUncompleted.
- ///
- /// In en, this message translates to:
- /// **'Mark as Uncompleted'**
- String get markAsUncompleted;
-
- /// No description provided for @uploadPhoto.
- ///
- /// In en, this message translates to:
- /// **'Upload Photo'**
- String get uploadPhoto;
-
- /// No description provided for @viewDetails.
- ///
- /// In en, this message translates to:
- /// **'View Details'**
- String get viewDetails;
-
- /// No description provided for @deliverySuccessful.
- ///
- /// In en, this message translates to:
- /// **'Delivery marked as completed'**
- String get deliverySuccessful;
-
- /// No description provided for @deliveryFailed.
- ///
- /// In en, this message translates to:
- /// **'Failed to mark delivery'**
- String get deliveryFailed;
-
- /// No description provided for @noDeliveries.
- ///
- /// In en, this message translates to:
- /// **'No deliveries'**
- String get noDeliveries;
-
- /// No description provided for @noRoutes.
- ///
- /// In en, this message translates to:
- /// **'No routes available'**
- String get noRoutes;
-
- /// No description provided for @error.
- ///
- /// In en, this message translates to:
- /// **'Error: {message}'**
- String error(String message);
-
- /// No description provided for @retry.
- ///
- /// In en, this message translates to:
- /// **'Retry'**
- String get retry;
-
- /// No description provided for @authenticationRequired.
- ///
- /// In en, this message translates to:
- /// **'Authentication required'**
- String get authenticationRequired;
-
- /// No description provided for @phoneCall.
- ///
- /// In en, this message translates to:
- /// **'Call customer'**
- String get phoneCall;
-
- /// No description provided for @navigateToAddress.
- ///
- /// In en, this message translates to:
- /// **'Show on map'**
- String get navigateToAddress;
-
- /// No description provided for @language.
- ///
- /// In en, this message translates to:
- /// **'Language'**
- String get language;
-
- /// No description provided for @english.
- ///
- /// In en, this message translates to:
- /// **'English'**
- String get english;
-
- /// No description provided for @french.
- ///
- /// In en, this message translates to:
- /// **'French'**
- String get french;
-
- /// No description provided for @appVersion.
- ///
- /// In en, this message translates to:
- /// **'App Version'**
- String get appVersion;
-
- /// No description provided for @about.
- ///
- /// In en, this message translates to:
- /// **'About'**
- String get about;
-
- /// No description provided for @fullName.
- ///
- /// In en, this message translates to:
- /// **'{firstName} {lastName}'**
- String fullName(String firstName, String lastName);
-
- /// No description provided for @completedDeliveries.
- ///
- /// In en, this message translates to:
- /// **'{completed}/{total} completed'**
- String completedDeliveries(int completed, int total);
-}
-
-class _AppLocalizationsDelegate
- extends LocalizationsDelegate {
- const _AppLocalizationsDelegate();
-
- @override
- Future load(Locale locale) {
- return SynchronousFuture(lookupAppLocalizations(locale));
- }
-
- @override
- bool isSupported(Locale locale) =>
- ['en', 'fr'].contains(locale.languageCode);
-
- @override
- bool shouldReload(_AppLocalizationsDelegate old) => false;
-}
-
-AppLocalizations lookupAppLocalizations(Locale locale) {
- // Lookup logic when only language code is specified.
- switch (locale.languageCode) {
- case 'en':
- return AppLocalizationsEn();
- case 'fr':
- return AppLocalizationsFr();
- }
-
- throw FlutterError(
- 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely '
- 'an issue with the localizations generation tool. Please file an issue '
- 'on GitHub with a reproducible sample app and the gen-l10n configuration '
- 'that was used.',
- );
-}
diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart
deleted file mode 100644
index 5ec892f..0000000
--- a/lib/l10n/app_localizations_en.dart
+++ /dev/null
@@ -1,137 +0,0 @@
-// ignore: unused_import
-import 'package:intl/intl.dart' as intl;
-import 'app_localizations.dart';
-
-// ignore_for_file: type=lint
-
-/// The translations for English (`en`).
-class AppLocalizationsEn extends AppLocalizations {
- AppLocalizationsEn([String locale = 'en']) : super(locale);
-
- @override
- String get appTitle => 'Plan B Logistics';
-
- @override
- String get appDescription => 'Delivery Management System';
-
- @override
- String get loginWithKeycloak => 'Login with Keycloak';
-
- @override
- String get deliveryRoutes => 'Delivery Routes';
-
- @override
- String get routes => 'Routes';
-
- @override
- String get deliveries => 'Deliveries';
-
- @override
- String get settings => 'Settings';
-
- @override
- String get profile => 'Profile';
-
- @override
- String get logout => 'Logout';
-
- @override
- String get completed => 'Completed';
-
- @override
- String get pending => 'Pending';
-
- @override
- String get todo => 'To Do';
-
- @override
- String get delivered => 'Delivered';
-
- @override
- String get newCustomer => 'New Customer';
-
- @override
- String items(int count) {
- return '$count items';
- }
-
- @override
- String moneyCurrency(double amount) {
- return '$amount MAD';
- }
-
- @override
- String get call => 'Call';
-
- @override
- String get map => 'Map';
-
- @override
- String get more => 'More';
-
- @override
- String get markAsCompleted => 'Mark as Completed';
-
- @override
- String get markAsUncompleted => 'Mark as Uncompleted';
-
- @override
- String get uploadPhoto => 'Upload Photo';
-
- @override
- String get viewDetails => 'View Details';
-
- @override
- String get deliverySuccessful => 'Delivery marked as completed';
-
- @override
- String get deliveryFailed => 'Failed to mark delivery';
-
- @override
- String get noDeliveries => 'No deliveries';
-
- @override
- String get noRoutes => 'No routes available';
-
- @override
- String error(String message) {
- return 'Error: $message';
- }
-
- @override
- String get retry => 'Retry';
-
- @override
- String get authenticationRequired => 'Authentication required';
-
- @override
- String get phoneCall => 'Call customer';
-
- @override
- String get navigateToAddress => 'Show on map';
-
- @override
- String get language => 'Language';
-
- @override
- String get english => 'English';
-
- @override
- String get french => 'French';
-
- @override
- String get appVersion => 'App Version';
-
- @override
- String get about => 'About';
-
- @override
- String fullName(String firstName, String lastName) {
- return '$firstName $lastName';
- }
-
- @override
- String completedDeliveries(int completed, int total) {
- return '$completed/$total completed';
- }
-}
diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart
deleted file mode 100644
index 5d5b701..0000000
--- a/lib/l10n/app_localizations_fr.dart
+++ /dev/null
@@ -1,137 +0,0 @@
-// ignore: unused_import
-import 'package:intl/intl.dart' as intl;
-import 'app_localizations.dart';
-
-// ignore_for_file: type=lint
-
-/// The translations for French (`fr`).
-class AppLocalizationsFr extends AppLocalizations {
- AppLocalizationsFr([String locale = 'fr']) : super(locale);
-
- @override
- String get appTitle => 'Plan B Logistique';
-
- @override
- String get appDescription => 'Systme de Gestion des Livraisons';
-
- @override
- String get loginWithKeycloak => 'Connexion avec Keycloak';
-
- @override
- String get deliveryRoutes => 'Itinraires de Livraison';
-
- @override
- String get routes => 'Itinraires';
-
- @override
- String get deliveries => 'Livraisons';
-
- @override
- String get settings => 'Paramtres';
-
- @override
- String get profile => 'Profil';
-
- @override
- String get logout => 'Dconnexion';
-
- @override
- String get completed => 'Livr';
-
- @override
- String get pending => 'En attente';
-
- @override
- String get todo => 'livrer';
-
- @override
- String get delivered => 'Livr';
-
- @override
- String get newCustomer => 'Nouveau Client';
-
- @override
- String items(int count) {
- return '$count articles';
- }
-
- @override
- String moneyCurrency(double amount) {
- return '$amount MAD';
- }
-
- @override
- String get call => 'Appeler';
-
- @override
- String get map => 'Carte';
-
- @override
- String get more => 'Plus';
-
- @override
- String get markAsCompleted => 'Marquer comme livr';
-
- @override
- String get markAsUncompleted => 'Marquer comme livrer';
-
- @override
- String get uploadPhoto => 'Tlcharger une photo';
-
- @override
- String get viewDetails => 'Voir les dtails';
-
- @override
- String get deliverySuccessful => 'Livraison marque comme complte';
-
- @override
- String get deliveryFailed => 'chec du marquage de la livraison';
-
- @override
- String get noDeliveries => 'Aucune livraison';
-
- @override
- String get noRoutes => 'Aucun itinraire disponible';
-
- @override
- String error(String message) {
- return 'Erreur: $message';
- }
-
- @override
- String get retry => 'Ressayer';
-
- @override
- String get authenticationRequired => 'Authentification requise';
-
- @override
- String get phoneCall => 'Appeler le client';
-
- @override
- String get navigateToAddress => 'Afficher sur la carte';
-
- @override
- String get language => 'Langue';
-
- @override
- String get english => 'English';
-
- @override
- String get french => 'Franais';
-
- @override
- String get appVersion => 'Version de l\'application';
-
- @override
- String get about => ' propos';
-
- @override
- String fullName(String firstName, String lastName) {
- return '$firstName $lastName';
- }
-
- @override
- String completedDeliveries(int completed, int total) {
- return '$completed/$total livrs';
- }
-}
diff --git a/lib/pages/deliveries_page.dart b/lib/pages/deliveries_page.dart
index 0045de4..e36d3b3 100644
--- a/lib/pages/deliveries_page.dart
+++ b/lib/pages/deliveries_page.dart
@@ -9,7 +9,8 @@ import '../models/delivery_commands.dart';
import '../utils/breakpoints.dart';
import '../utils/responsive.dart';
import '../components/map_sidebar_layout.dart';
-import '../components/delivery_map.dart';
+import '../components/dark_mode_map.dart';
+import '../components/delivery_list_item.dart';
class DeliveriesPage extends ConsumerStatefulWidget {
final int routeFragmentId;
@@ -62,7 +63,7 @@ class _DeliveriesPageState extends ConsumerState {
.toList();
return MapSidebarLayout(
- mapWidget: DeliveryMap(
+ mapWidget: DarkModeMapComponent(
deliveries: deliveries,
selectedDelivery: _selectedDelivery,
onDeliverySelected: (delivery) {
@@ -260,14 +261,16 @@ class DeliveryListView extends StatelessWidget {
// Trigger refresh via provider
},
child: ListView.builder(
+ padding: const EdgeInsets.symmetric(vertical: 8),
itemCount: deliveries.length,
itemBuilder: (context, index) {
final delivery = deliveries[index];
- return DeliveryCard(
+ return DeliveryListItem(
delivery: delivery,
isSelected: selectedDelivery?.id == delivery.id,
onTap: () => onDeliverySelected(delivery),
- onAction: onAction,
+ onCall: () => onAction(delivery, 'call'),
+ animationIndex: index,
);
},
),
diff --git a/lib/pages/routes_page.dart b/lib/pages/routes_page.dart
index b3f45c6..7a1aa25 100644
--- a/lib/pages/routes_page.dart
+++ b/lib/pages/routes_page.dart
@@ -4,6 +4,7 @@ import '../models/delivery_route.dart';
import '../providers/providers.dart';
import '../utils/breakpoints.dart';
import '../utils/responsive.dart';
+import '../components/premium_route_card.dart';
import 'deliveries_page.dart';
import 'settings_page.dart';
@@ -137,47 +138,18 @@ class RoutesPage extends ConsumerWidget {
}
Widget _buildRouteCard(BuildContext context, DeliveryRoute route) {
- return Card(
- child: InkWell(
- onTap: () {
- Navigator.of(context).push(
- MaterialPageRoute(
- builder: (context) => DeliveriesPage(
- routeFragmentId: route.id,
- routeName: route.name,
- ),
+ return PremiumRouteCard(
+ route: route,
+ onTap: () {
+ Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (context) => DeliveriesPage(
+ routeFragmentId: route.id,
+ routeName: route.name,
),
- );
- },
- child: Padding(
- padding: EdgeInsets.all(ResponsiveSpacing.md(context)),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- route.name,
- style: Theme.of(context).textTheme.titleLarge,
- maxLines: 2,
- overflow: TextOverflow.ellipsis,
- ),
- SizedBox(height: ResponsiveSpacing.sm(context)),
- Text(
- '${route.deliveredCount}/${route.deliveriesCount} completed',
- style: Theme.of(context).textTheme.bodySmall,
- ),
- SizedBox(height: ResponsiveSpacing.md(context)),
- ClipRRect(
- borderRadius: BorderRadius.circular(4),
- child: LinearProgressIndicator(
- value: route.progress,
- minHeight: 8,
- ),
- ),
- ],
),
- ),
- ),
+ );
+ },
);
}
}
diff --git a/lib/theme.dart b/lib/theme.dart
index 2e711ec..4b97fca 100644
--- a/lib/theme.dart
+++ b/lib/theme.dart
@@ -1,4 +1,12 @@
-import "package:flutter/material.dart";
+import 'package:flutter/material.dart';
+import 'theme/color_system.dart';
+import 'theme/spacing_system.dart';
+import 'theme/border_system.dart';
+import 'theme/shadow_system.dart';
+import 'theme/size_system.dart';
+import 'theme/animation_system.dart';
+import 'theme/typography_system.dart';
+import 'theme/component_themes.dart';
class MaterialTheme {
final TextTheme textTheme;
@@ -9,51 +17,51 @@ class MaterialTheme {
static ColorScheme lightScheme() {
return const ColorScheme(
brightness: Brightness.light,
- primary: Color(0xffC44D58), // Svrnty Crimson Red
- surfaceTint: Color(0xffC44D58),
+ primary: Color(0xffDF2D45), // Svrnty Crimson Red (updated)
+ surfaceTint: Color(0xffDF2D45),
onPrimary: Color(0xffffffff),
- primaryContainer: Color(0xffffd8db),
- onPrimaryContainer: Color(0xff8b3238),
- secondary: Color(0xff475C6C), // Svrnty Slate Blue
+ primaryContainer: Color(0xffFFE0E5),
+ onPrimaryContainer: Color(0xff06080C),
+ secondary: Color(0xff3A4958), // Svrnty Dark Slate
onSecondary: Color(0xffffffff),
- secondaryContainer: Color(0xffd1dce7),
- onSecondaryContainer: Color(0xff2e3d4a),
- tertiary: Color(0xff5a4a6c),
+ secondaryContainer: Color(0xffD0DCE8),
+ onSecondaryContainer: Color(0xff06080C),
+ tertiary: Color(0xff1D2C39), // Svrnty Teal
onTertiary: Color(0xffffffff),
- tertiaryContainer: Color(0xffe0d3f2),
- onTertiaryContainer: Color(0xff3d2f4d),
- error: Color(0xffba1a1a),
+ tertiaryContainer: Color(0xffBFD5E3),
+ onTertiaryContainer: Color(0xff06080C),
+ error: Color(0xffEF4444),
onError: Color(0xffffffff),
- errorContainer: Color(0xffffdad6),
- onErrorContainer: Color(0xff93000a),
- surface: Color(0xfffafafa),
- onSurface: Color(0xff1a1c1e),
- onSurfaceVariant: Color(0xff43474e),
- outline: Color(0xff74777f),
- outlineVariant: Color(0xffc4c6cf),
- shadow: Color(0xff000000),
+ errorContainer: Color(0xffFEE2E2),
+ onErrorContainer: Color(0xff7F1D1D),
+ surface: Color(0xffFAFAFC),
+ onSurface: Color(0xff06080C),
+ onSurfaceVariant: Color(0xff506576),
+ outline: Color(0xffAEB8BE),
+ outlineVariant: Color(0xffD1D5DB),
+ shadow: Color(0xff1A000000),
scrim: Color(0xff000000),
- inverseSurface: Color(0xff2f3033),
- inversePrimary: Color(0xffffb3b9),
- primaryFixed: Color(0xffffd8db),
- onPrimaryFixed: Color(0xff410008),
- primaryFixedDim: Color(0xffffb3b9),
- onPrimaryFixedVariant: Color(0xff8b3238),
- secondaryFixed: Color(0xffd1dce7),
- onSecondaryFixed: Color(0xff0f1a24),
- secondaryFixedDim: Color(0xffb5c0cb),
- onSecondaryFixedVariant: Color(0xff2e3d4a),
- tertiaryFixed: Color(0xffe0d3f2),
- onTertiaryFixed: Color(0xff1f122f),
- tertiaryFixedDim: Color(0xffc4b7d6),
- onTertiaryFixedVariant: Color(0xff3d2f4d),
+ inverseSurface: Color(0xff06080C),
+ inversePrimary: Color(0xffFF6B7D),
+ primaryFixed: Color(0xffFFE0E5),
+ onPrimaryFixed: Color(0xff06080C),
+ primaryFixedDim: Color(0xffFFC0C9),
+ onPrimaryFixedVariant: Color(0xff8B1A2A),
+ secondaryFixed: Color(0xffD0DCE8),
+ onSecondaryFixed: Color(0xff06080C),
+ secondaryFixedDim: Color(0xffB0C4D8),
+ onSecondaryFixedVariant: Color(0xff3A4958),
+ tertiaryFixed: Color(0xffBFD5E3),
+ onTertiaryFixed: Color(0xff06080C),
+ tertiaryFixedDim: Color(0xff9FBDCF),
+ onTertiaryFixedVariant: Color(0xff1D2C39),
surfaceDim: Color(0xffdadcde),
surfaceBright: Color(0xfffafafa),
surfaceContainerLowest: Color(0xffffffff),
- surfaceContainerLow: Color(0xfff4f5f7),
- surfaceContainer: Color(0xffeef0f2),
- surfaceContainerHigh: Color(0xffe8eaec),
- surfaceContainerHighest: Color(0xffe2e4e7),
+ surfaceContainerLow: Color(0xfff6f6f8),
+ surfaceContainer: Color(0xfff1f1f4),
+ surfaceContainerHigh: Color(0xffebebee),
+ surfaceContainerHighest: Color(0xffe5e5e8),
);
}
@@ -171,55 +179,55 @@ class MaterialTheme {
return theme(lightHighContrastScheme());
}
- // Svrnty Brand Colors - Dark Theme (Bold & Saturated)
+ // Svrnty Brand Colors - Dark Theme
static ColorScheme darkScheme() {
return const ColorScheme(
brightness: Brightness.dark,
- primary: Color(0xffF3574E), // Bold Svrnty Crimson Red (slightly desaturated)
- surfaceTint: Color(0xffF3574E),
+ primary: Color(0xffDF2D45), // Svrnty Crimson Red
+ surfaceTint: Color(0xffFF6B7D),
onPrimary: Color(0xffffffff),
- primaryContainer: Color(0xffC44D58), // True brand crimson
- onPrimaryContainer: Color(0xffffffff),
- secondary: Color(0xff5A6F7D), // Rich Svrnty Slate Blue
+ primaryContainer: Color(0xff9C1A29),
+ onPrimaryContainer: Color(0xffFFE0E5),
+ secondary: Color(0xff506576), // Svrnty Slate Gray
onSecondary: Color(0xffffffff),
- secondaryContainer: Color(0xff475C6C), // True brand slate
- onSecondaryContainer: Color(0xffffffff),
- tertiary: Color(0xffA78BBF), // Richer purple
+ secondaryContainer: Color(0xff3A4958),
+ onSecondaryContainer: Color(0xffD0DCE8),
+ tertiary: Color(0xff5A8FA8), // Svrnty Teal variant
onTertiary: Color(0xffffffff),
- tertiaryContainer: Color(0xff8B6FA3),
- onTertiaryContainer: Color(0xffffffff),
- error: Color(0xffFF5449),
- onError: Color(0xffffffff),
- errorContainer: Color(0xffD32F2F),
- onErrorContainer: Color(0xffffffff),
- surface: Color(0xff1a1c1e), // Svrnty Dark Background
- onSurface: Color(0xfff0f0f0),
- onSurfaceVariant: Color(0xffc8cad0),
- outline: Color(0xff8d9199),
- outlineVariant: Color(0xff43474e),
+ tertiaryContainer: Color(0xff1D2C39),
+ onTertiaryContainer: Color(0xffBFD5E3),
+ error: Color(0xffFF6B6B),
+ onError: Color(0xff4C0707),
+ errorContainer: Color(0xff93000A),
+ onErrorContainer: Color(0xffFEE2E2),
+ surface: Color(0xff0A0C10), // Svrnty Dark Background
+ onSurface: Color(0xffF0F0F2),
+ onSurfaceVariant: Color(0xffBFC3C8),
+ outline: Color(0xff6B7280),
+ outlineVariant: Color(0xff374151),
shadow: Color(0xff000000),
scrim: Color(0xff000000),
- inverseSurface: Color(0xffe2e4e7),
- inversePrimary: Color(0xffC44D58),
- primaryFixed: Color(0xffFFD8DB),
- onPrimaryFixed: Color(0xff2d0008),
- primaryFixedDim: Color(0xffF3574E),
- onPrimaryFixedVariant: Color(0xffffffff),
- secondaryFixed: Color(0xffD1DCE7),
- onSecondaryFixed: Color(0xff0f1a24),
- secondaryFixedDim: Color(0xff5A6F7D),
- onSecondaryFixedVariant: Color(0xffffffff),
- tertiaryFixed: Color(0xffE0D3F2),
- onTertiaryFixed: Color(0xff1f122f),
- tertiaryFixedDim: Color(0xffA78BBF),
- onTertiaryFixedVariant: Color(0xffffffff),
- surfaceDim: Color(0xff1a1c1e),
+ inverseSurface: Color(0xffE2E2E6),
+ inversePrimary: Color(0xffDF2D45),
+ primaryFixed: Color(0xffFFE0E5),
+ onPrimaryFixed: Color(0xff3D0009),
+ primaryFixedDim: Color(0xffFF6B7D),
+ onPrimaryFixedVariant: Color(0xff9C1A29),
+ secondaryFixed: Color(0xffD0DCE8),
+ onSecondaryFixed: Color(0xff06080C),
+ secondaryFixedDim: Color(0xff506576),
+ onSecondaryFixedVariant: Color(0xff3A4958),
+ tertiaryFixed: Color(0xffBFD5E3),
+ onTertiaryFixed: Color(0xff001219),
+ tertiaryFixedDim: Color(0xff5A8FA8),
+ onTertiaryFixedVariant: Color(0xff1D2C39),
+ surfaceDim: Color(0xff0A0C10),
surfaceBright: Color(0xff404244),
- surfaceContainerLowest: Color(0xff0f1113),
- surfaceContainerLow: Color(0xff1f2123),
- surfaceContainer: Color(0xff23252a),
- surfaceContainerHigh: Color(0xff2d2f35),
- surfaceContainerHighest: Color(0xff383940),
+ surfaceContainerLowest: Color(0xff040507),
+ surfaceContainerLow: Color(0xff0F1114),
+ surfaceContainer: Color(0xff14161A),
+ surfaceContainerHigh: Color(0xff1F2228),
+ surfaceContainerHighest: Color(0xff2A2D34),
);
}
@@ -338,34 +346,128 @@ class MaterialTheme {
}
- ThemeData theme(ColorScheme colorScheme) => ThemeData(
- useMaterial3: true,
- brightness: colorScheme.brightness,
- colorScheme: colorScheme,
- textTheme: const TextTheme(
- displayLarge: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.bold),
- displayMedium: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.bold),
- displaySmall: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.bold),
- headlineLarge: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w600),
- headlineMedium: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w600),
- headlineSmall: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w600),
- titleLarge: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w600),
- titleMedium: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w500),
- titleSmall: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w500),
- bodyLarge: TextStyle(fontFamily: 'Montserrat'),
- bodyMedium: TextStyle(fontFamily: 'Montserrat'),
- bodySmall: TextStyle(fontFamily: 'Montserrat'),
- labelLarge: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w500),
- labelMedium: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w500),
- labelSmall: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w500),
- ).apply(
- bodyColor: colorScheme.onSurface,
- displayColor: colorScheme.onSurface,
- ),
- fontFamily: 'Montserrat',
- scaffoldBackgroundColor: colorScheme.surface,
- canvasColor: colorScheme.surface,
- );
+ ThemeData theme(ColorScheme colorScheme) {
+ return ThemeData(
+ useMaterial3: true,
+ brightness: colorScheme.brightness,
+ colorScheme: colorScheme,
+ fontFamily: 'Montserrat',
+ scaffoldBackgroundColor: colorScheme.surface,
+ canvasColor: colorScheme.surface,
+ textTheme: const TextTheme(
+ displayLarge: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w700,
+ fontSize: 57,
+ letterSpacing: -0.5,
+ ),
+ displayMedium: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w700,
+ fontSize: 45,
+ letterSpacing: -0.5,
+ ),
+ displaySmall: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w700,
+ fontSize: 36,
+ letterSpacing: -0.25,
+ ),
+ headlineLarge: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w600,
+ fontSize: 32,
+ letterSpacing: -0.25,
+ ),
+ headlineMedium: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w600,
+ fontSize: 28,
+ letterSpacing: 0,
+ ),
+ headlineSmall: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w600,
+ fontSize: 24,
+ letterSpacing: 0,
+ ),
+ titleLarge: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w600,
+ fontSize: 22,
+ letterSpacing: 0,
+ ),
+ titleMedium: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w500,
+ fontSize: 16,
+ letterSpacing: 0.15,
+ ),
+ titleSmall: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w500,
+ fontSize: 14,
+ letterSpacing: 0.1,
+ ),
+ bodyLarge: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w400,
+ fontSize: 16,
+ letterSpacing: 0.5,
+ ),
+ bodyMedium: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w400,
+ fontSize: 14,
+ letterSpacing: 0.25,
+ ),
+ bodySmall: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w400,
+ fontSize: 12,
+ letterSpacing: 0.4,
+ ),
+ labelLarge: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w500,
+ fontSize: 14,
+ letterSpacing: 0.1,
+ ),
+ labelMedium: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w500,
+ fontSize: 12,
+ letterSpacing: 0.5,
+ ),
+ labelSmall: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w500,
+ fontSize: 11,
+ letterSpacing: 0.5,
+ ),
+ ).apply(
+ bodyColor: colorScheme.onSurface,
+ displayColor: colorScheme.onSurface,
+ ),
+ // Component Themes
+ cardTheme: ComponentThemes.cardTheme(colorScheme),
+ appBarTheme: ComponentThemes.appBarTheme(colorScheme),
+ filledButtonTheme: ComponentThemes.filledButtonTheme(colorScheme),
+ elevatedButtonTheme: ComponentThemes.elevatedButtonTheme(colorScheme),
+ outlinedButtonTheme: ComponentThemes.outlinedButtonTheme(colorScheme),
+ inputDecorationTheme: ComponentThemes.inputDecorationTheme(colorScheme),
+ snackBarTheme: ComponentThemes.snackBarTheme(colorScheme),
+ dialogTheme: ComponentThemes.dialogTheme(colorScheme),
+ bottomNavigationBarTheme:
+ ComponentThemes.bottomNavigationBarTheme(colorScheme),
+ chipTheme: ComponentThemes.chipTheme(colorScheme),
+ progressIndicatorTheme:
+ ComponentThemes.progressIndicatorTheme(colorScheme),
+ floatingActionButtonTheme:
+ ComponentThemes.floatingActionButtonTheme(colorScheme),
+ sliderTheme: ComponentThemes.sliderTheme(colorScheme),
+ );
+ }
List get extendedColors => [
diff --git a/lib/theme/animation_system.dart b/lib/theme/animation_system.dart
new file mode 100644
index 0000000..3df2fb6
--- /dev/null
+++ b/lib/theme/animation_system.dart
@@ -0,0 +1,272 @@
+import 'package:flutter/material.dart';
+
+/// Svrnty Animation System
+/// Complete animation configuration with durations, curves, and stagger delays
+class AppAnimations {
+ // Prevent instantiation
+ AppAnimations._();
+
+ // ============================================
+ // ANIMATION DURATION CONSTANTS
+ // ============================================
+
+ /// Fast animation duration (200ms) - Button press, hover, quick interactions
+ static const Duration durationFast = Duration(milliseconds: 200);
+
+ /// Normal animation duration (300ms) - Default animations, fade, slide
+ static const Duration durationNormal = Duration(milliseconds: 300);
+
+ /// Slow animation duration (500ms) - Prominent animations, entrance, transitions
+ static const Duration durationSlow = Duration(milliseconds: 500);
+
+ /// Extra slow animation duration (800ms) - Slow, attention-grabbing animations
+ static const Duration durationXSlow = Duration(milliseconds: 800);
+
+ // ============================================
+ // ANIMATION DURATION IN MILLISECONDS
+ // ============================================
+
+ /// Fast animation milliseconds (200ms)
+ static const int durationFastMs = 200;
+
+ /// Normal animation milliseconds (300ms)
+ static const int durationNormalMs = 300;
+
+ /// Slow animation milliseconds (500ms)
+ static const int durationSlowMs = 500;
+
+ /// Extra slow animation milliseconds (800ms)
+ static const int durationXSlowMs = 800;
+
+ // ============================================
+ // ANIMATION CURVES
+ // ============================================
+
+ /// Ease In - Slow start, fast end (decelerate)
+ static const Curve curveEaseIn = Curves.easeIn;
+
+ /// Ease Out - Fast start, slow end (decelerate)
+ static const Curve curveEaseOut = Curves.easeOut;
+
+ /// Ease In Out - Smooth both ends (decelerate at start and end)
+ static const Curve curveEaseInOut = Curves.easeInOut;
+
+ /// Ease In Back - Back in, bounce effect
+ static const Curve curveEaseInBack = Curves.easeInBack;
+
+ /// Ease Out Back - Back out, bounce effect
+ static const Curve curveEaseOutBack = Curves.easeOutBack;
+
+ /// Elastic/Bouncy effect
+ static const Curve curveElastic = Curves.elasticOut;
+
+ /// Bouncing motion
+ static const Curve curveBounce = Curves.bounceOut;
+
+ /// Fast Out Slow In - Material standard curve
+ static const Curve curveFastOutSlowIn = Curves.fastOutSlowIn;
+
+ /// Deceleration curve (accelerate)
+ static const Curve curveAccelerate = Curves.decelerate;
+
+ /// Linear curve (no acceleration/deceleration)
+ static const Curve curveLinear = Curves.linear;
+
+ // ============================================
+ // STAGGER DELAYS (FOR LIST ANIMATIONS)
+ // ============================================
+
+ /// Default stagger delay for list items (50ms)
+ static const Duration staggerDelay = Duration(milliseconds: 50);
+
+ /// Quick stagger delay (30ms)
+ static const Duration staggerDelayFast = Duration(milliseconds: 30);
+
+ /// Slow stagger delay (100ms)
+ static const Duration staggerDelaySlow = Duration(milliseconds: 100);
+
+ /// Stagger delay in milliseconds
+ static const int staggerDelayMs = 50;
+
+ // ============================================
+ // SCALE ANIMATION CONSTANTS
+ // ============================================
+
+ /// Normal scale (1.0)
+ static const double scaleNormal = 1.0;
+
+ /// Hover/Light scale (1.02)
+ static const double scaleHover = 1.02;
+
+ /// Pressed/Active scale (0.98)
+ static const double scalePressed = 0.98;
+
+ /// Animation scale (0.9)
+ static const double scaleAnimation = 0.9;
+
+ // ============================================
+ // OFFSET ANIMATION CONSTANTS
+ // ============================================
+
+ /// Slide offset - Small (8px)
+ static const Offset offsetSm = Offset(0, 8);
+
+ /// Slide offset - Medium (16px)
+ static const Offset offsetMd = Offset(0, 16);
+
+ /// Slide offset - Large (20px)
+ static const Offset offsetLg = Offset(0, 20);
+
+ /// Slide offset - Extra large (32px)
+ static const Offset offsetXl = Offset(0, 32);
+
+ /// Floating offset - Up (10px)
+ static const Offset offsetFloating = Offset(0, -10);
+
+ // ============================================
+ // OPACITY ANIMATION CONSTANTS
+ // ============================================
+
+ /// Full opacity
+ static const double opacityFull = 1.0;
+
+ /// Half opacity
+ static const double opacityHalf = 0.5;
+
+ /// Subtle opacity (70%)
+ static const double opacitySubtle = 0.7;
+
+ /// Dim opacity (50%)
+ static const double opacityDim = 0.5;
+
+ /// Fade out opacity (30%)
+ static const double opacityFadeOut = 0.3;
+
+ /// Invisible opacity (0%)
+ static const double opacityInvisible = 0.0;
+
+ // ============================================
+ // ANIMATION PRESETS
+ // ============================================
+
+ /// Scale animation preset (button press) - note: CurvedAnimation cannot be const
+ // Use this pattern in your widgets instead:
+ // CurvedAnimation(parent: controller, curve: Curves.easeInOut)
+
+ // ============================================
+ // ROTATION ANIMATION CONSTANTS
+ // ============================================
+
+ /// Full rotation (360 degrees)
+ static const double rotationFull = 1.0;
+
+ /// Half rotation (180 degrees)
+ static const double rotationHalf = 0.5;
+
+ /// Quarter rotation (90 degrees)
+ static const double rotationQuarter = 0.25;
+
+ // ============================================
+ // UTILITY METHODS
+ // ============================================
+
+ /// Get duration based on animation intensity
+ static Duration getDuration(AnimationIntensity intensity) {
+ switch (intensity) {
+ case AnimationIntensity.fast:
+ return durationFast;
+ case AnimationIntensity.normal:
+ return durationNormal;
+ case AnimationIntensity.slow:
+ return durationSlow;
+ case AnimationIntensity.xSlow:
+ return durationXSlow;
+ }
+ }
+
+ /// Get curve based on animation type
+ static Curve getCurve(AnimationType type) {
+ switch (type) {
+ case AnimationType.easeIn:
+ return curveEaseIn;
+ case AnimationType.easeOut:
+ return curveEaseOut;
+ case AnimationType.easeInOut:
+ return curveEaseInOut;
+ case AnimationType.elastic:
+ return curveElastic;
+ case AnimationType.bounce:
+ return curveBounce;
+ case AnimationType.linear:
+ return curveLinear;
+ }
+ }
+
+ /// Get stagger delay for list index
+ static Duration getStaggerDelay(int index, {bool fast = false}) {
+ final baseDelay = fast ? staggerDelayFast : staggerDelay;
+ return Duration(
+ milliseconds: baseDelay.inMilliseconds * index,
+ );
+ }
+
+ /// Get scale value based on interaction state
+ static double getScale(InteractionState state) {
+ switch (state) {
+ case InteractionState.normal:
+ return scaleNormal;
+ case InteractionState.hover:
+ return scaleHover;
+ case InteractionState.pressed:
+ return scalePressed;
+ }
+ }
+}
+
+/// Animation intensity levels
+enum AnimationIntensity {
+ /// 200ms - Quick interactions
+ fast,
+
+ /// 300ms - Standard animations
+ normal,
+
+ /// 500ms - Prominent animations
+ slow,
+
+ /// 800ms - Slow, attention-grabbing animations
+ xSlow,
+}
+
+/// Animation types/curves
+enum AnimationType {
+ /// Ease In curve
+ easeIn,
+
+ /// Ease Out curve
+ easeOut,
+
+ /// Ease In Out curve
+ easeInOut,
+
+ /// Elastic/bouncy curve
+ elastic,
+
+ /// Bounce curve
+ bounce,
+
+ /// Linear curve
+ linear,
+}
+
+/// Interaction states for animation
+enum InteractionState {
+ /// Normal/default state
+ normal,
+
+ /// Hover state
+ hover,
+
+ /// Pressed/active state
+ pressed,
+}
diff --git a/lib/theme/border_system.dart b/lib/theme/border_system.dart
new file mode 100644
index 0000000..dde5b6b
--- /dev/null
+++ b/lib/theme/border_system.dart
@@ -0,0 +1,146 @@
+import 'package:flutter/material.dart';
+
+/// Svrnty Border System - Border Radius Constants
+/// All border radius values follow a strict 4px grid
+class AppBorders {
+ // Prevent instantiation
+ AppBorders._();
+
+ // ============================================
+ // BORDER RADIUS VALUES (4px Grid)
+ // ============================================
+
+ /// Extra small border radius (4px) - unit × 1
+ /// Used for: Subtle rounding on chips, tags, small elements
+ static const double radiusXs = 4.0;
+
+ /// Small border radius (8px) - unit × 2
+ /// Used for: Buttons, inputs, small cards
+ static const double radiusSm = 8.0;
+
+ /// Medium border radius (12px) - unit × 3
+ /// Used for: Cards, dropdowns, standard components
+ static const double radiusMd = 12.0;
+
+ /// Large border radius (16px) - unit × 4
+ /// Used for: Dialogs, large cards, prominent containers
+ static const double radiusLg = 16.0;
+
+ /// Extra large border radius (24px) - unit × 6
+ /// Used for: Containers, large surfaces
+ static const double radiusXl = 24.0;
+
+ /// Fully rounded border radius (999px)
+ /// Used for: Pills, badges, fully circular elements
+ static const double radiusRound = 999.0;
+
+ // ============================================
+ // BORDER RADIUS - BORDERRADIUS OBJECTS
+ // ============================================
+
+ /// BorderRadius.circular(4px)
+ static const BorderRadius circularXs = BorderRadius.all(Radius.circular(radiusXs));
+
+ /// BorderRadius.circular(8px)
+ static const BorderRadius circularSm = BorderRadius.all(Radius.circular(radiusSm));
+
+ /// BorderRadius.circular(12px)
+ static const BorderRadius circularMd = BorderRadius.all(Radius.circular(radiusMd));
+
+ /// BorderRadius.circular(16px)
+ static const BorderRadius circularLg = BorderRadius.all(Radius.circular(radiusLg));
+
+ /// BorderRadius.circular(24px)
+ static const BorderRadius circularXl = BorderRadius.all(Radius.circular(radiusXl));
+
+ /// BorderRadius.circular(999px) - Fully rounded
+ static const BorderRadius circularRound = BorderRadius.all(Radius.circular(radiusRound));
+
+ // ============================================
+ // BORDER RADIUS - TOP ONLY (for bottom sheets)
+ // ============================================
+
+ /// BorderRadius with top corners rounded (8px)
+ static const BorderRadius topSmallRadius = BorderRadius.only(
+ topLeft: Radius.circular(radiusSm),
+ topRight: Radius.circular(radiusSm),
+ );
+
+ /// BorderRadius with top corners rounded (12px)
+ static const BorderRadius topMediumRadius = BorderRadius.only(
+ topLeft: Radius.circular(radiusMd),
+ topRight: Radius.circular(radiusMd),
+ );
+
+ /// BorderRadius with top corners rounded (16px)
+ static const BorderRadius topLargeRadius = BorderRadius.only(
+ topLeft: Radius.circular(radiusLg),
+ topRight: Radius.circular(radiusLg),
+ );
+
+ // ============================================
+ // COMPONENT BORDER RADIUS MAPPING
+ // ============================================
+
+ /// Button border radius (8px - radiusSm)
+ static const double buttonRadius = radiusSm;
+
+ /// Input field border radius (8px - radiusSm)
+ static const double inputRadius = radiusSm;
+
+ /// Card border radius (12px - radiusMd)
+ static const double cardRadius = radiusMd;
+
+ /// Dialog border radius (16px - radiusLg)
+ static const double dialogRadius = radiusLg;
+
+ /// Chip border radius (999px - radiusRound, fully rounded)
+ static const double chipRadius = radiusRound;
+
+ /// Bottom sheet border radius (16px - radiusLg)
+ static const double bottomSheetRadius = radiusLg;
+
+ /// Dropdown border radius (8px - radiusSm)
+ static const double dropdownRadius = radiusSm;
+
+ /// Small component border radius (4px - radiusXs)
+ static const double smallComponentRadius = radiusXs;
+
+ // ============================================
+ // BORDER RADIUS SHAPE OBJECTS
+ // ============================================
+
+ /// RoundedRectangleBorder for buttons (8px radius)
+ static const ShapeBorder buttonShape = RoundedRectangleBorder(
+ borderRadius: circularSm,
+ );
+
+ /// RoundedRectangleBorder for cards (12px radius)
+ static const ShapeBorder cardShape = RoundedRectangleBorder(
+ borderRadius: circularMd,
+ );
+
+ /// RoundedRectangleBorder for dialogs (16px radius)
+ static const ShapeBorder dialogShape = RoundedRectangleBorder(
+ borderRadius: circularLg,
+ );
+
+ // ============================================
+ // COMPONENT BORDER RADIUS OBJECTS
+ // ============================================
+
+ /// Small component border radius (4px - Radius object)
+ static const Radius smallRadius = Radius.circular(radiusXs);
+
+ /// Button border radius (8px - Radius object)
+ static const Radius buttonBorderRadius = Radius.circular(radiusSm);
+
+ /// Card border radius (12px - Radius object)
+ static const Radius cardBorderRadius = Radius.circular(radiusMd);
+
+ /// Dialog border radius (16px - Radius object)
+ static const Radius dialogBorderRadius = Radius.circular(radiusLg);
+
+ /// Fully rounded (999px - Radius object)
+ static const Radius fullRoundRadius = Radius.circular(radiusRound);
+}
diff --git a/lib/theme/color_system.dart b/lib/theme/color_system.dart
new file mode 100644
index 0000000..f684f11
--- /dev/null
+++ b/lib/theme/color_system.dart
@@ -0,0 +1,159 @@
+import 'package:flutter/material.dart';
+
+/// Svrnty Brand Color System
+/// Complete color palette following Material Design 3 specifications
+class SvrntyColors {
+ // Prevent instantiation
+ SvrntyColors._();
+
+ // ============================================
+ // CORE BRAND COLORS
+ // ============================================
+
+ /// Crimson Red - Primary accent and brand signature
+ static const Color crimsonRed = Color(0xDF2D45);
+
+ /// Almost Black - Primary dark background
+ static const Color almostBlack = Color(0x06080C);
+
+ /// Dark Slate - Secondary dark tone
+ static const Color darkSlate = Color(0x3A4958);
+
+ /// Slate Gray - Mid-tone gray
+ static const Color slateGray = Color(0x506576);
+
+ /// Teal - Tertiary accent
+ static const Color teal = Color(0x1D2C39);
+
+ /// Light Gray - Neutral light
+ static const Color lightGray = Color(0xAEB8BE);
+
+ // ============================================
+ // SEMANTIC COLORS
+ // ============================================
+
+ /// Success - Green for positive actions and completed states
+ static const Color success = Color(0x22C55E);
+
+ /// Warning - Amber for warnings and attention-needed states
+ static const Color warning = Color(0xF59E0B);
+
+ /// Info - Blue for informational and in-progress states
+ static const Color info = Color(0x3B82F6);
+
+ /// Error - Red for errors, failures, and destructive actions
+ static const Color error = Color(0xEF4444);
+
+ // ============================================
+ // DELIVERY STATUS COLORS
+ // ============================================
+
+ /// Status Pending - Awaiting action (Slate Gray)
+ static const Color statusPending = slateGray;
+
+ /// Status In Progress - Currently being delivered (Blue)
+ static const Color statusInProgress = info;
+
+ /// Status Completed - Successfully delivered (Green)
+ static const Color statusCompleted = success;
+
+ /// Status Skipped - Skipped/passed delivery (Amber)
+ static const Color statusSkipped = warning;
+
+ /// Status Failed - Failed delivery (Red)
+ static const Color statusFailed = error;
+
+ // ============================================
+ // SURFACE VARIANTS
+ // ============================================
+
+ /// Surface Elevated - Light elevated surface
+ static const Color surfaceElevated = Color(0xF5F7FA);
+
+ /// Surface Subdued - Subdued light surface
+ static const Color surfaceSubdued = Color(0xE8EAEE);
+
+ // ============================================
+ // EXTENDED COLOR FAMILIES - SUCCESS (GREEN)
+ // ============================================
+
+ /// Success color light theme
+ static const Color successLight = Color(0x22C55E);
+
+ /// Success on color light theme
+ static const Color onSuccessLight = Color(0xFFFFFF);
+
+ /// Success container light theme
+ static const Color successContainerLight = Color(0xDCFCE7);
+
+ /// Success on container light theme
+ static const Color onSuccessContainerLight = Color(0x14532D);
+
+ /// Success color dark theme
+ static const Color successDark = Color(0x4ADE80);
+
+ /// Success on color dark theme
+ static const Color onSuccessDark = Color(0x14532D);
+
+ /// Success container dark theme
+ static const Color successContainerDark = Color(0x15803D);
+
+ /// Success on container dark theme
+ static const Color onSuccessContainerDark = Color(0xDCFCE7);
+
+ // ============================================
+ // EXTENDED COLOR FAMILIES - WARNING (AMBER)
+ // ============================================
+
+ /// Warning color light theme
+ static const Color warningLight = Color(0xF59E0B);
+
+ /// Warning on color light theme
+ static const Color onWarningLight = Color(0xFFFFFF);
+
+ /// Warning container light theme
+ static const Color warningContainerLight = Color(0xFEF3C7);
+
+ /// Warning on container light theme
+ static const Color onWarningContainerLight = Color(0x78350F);
+
+ /// Warning color dark theme
+ static const Color warningDark = Color(0xFBBF24);
+
+ /// Warning on color dark theme
+ static const Color onWarningDark = Color(0x78350F);
+
+ /// Warning container dark theme
+ static const Color warningContainerDark = Color(0xD97706);
+
+ /// Warning on container dark theme
+ static const Color onWarningContainerDark = Color(0xFEF3C7);
+
+ // ============================================
+ // EXTENDED COLOR FAMILIES - INFO (BLUE)
+ // ============================================
+
+ /// Info color light theme
+ static const Color infoLight = Color(0x3B82F6);
+
+ /// Info on color light theme
+ static const Color onInfoLight = Color(0xFFFFFF);
+
+ /// Info container light theme
+ static const Color infoContainerLight = Color(0xDEEEFF);
+
+ /// Info on container light theme
+ static const Color onInfoContainerLight = Color(0x003DA1);
+
+ /// Info color dark theme
+ static const Color infoDark = Color(0x90CAF9);
+
+ /// Info on color dark theme
+ static const Color onInfoDark = Color(0x003DA1);
+
+ /// Info container dark theme
+ static const Color infoContainerDark = Color(0x0D47A1);
+
+ /// Info on container dark theme
+ static const Color onInfoContainerDark = Color(0xDEEEFF);
+}
diff --git a/lib/theme/component_themes.dart b/lib/theme/component_themes.dart
new file mode 100644
index 0000000..1faba24
--- /dev/null
+++ b/lib/theme/component_themes.dart
@@ -0,0 +1,262 @@
+import 'package:flutter/material.dart';
+import 'spacing_system.dart';
+import 'border_system.dart';
+import 'shadow_system.dart';
+import 'size_system.dart';
+
+/// Component Theme Data Builders
+/// Centralized component theme definitions for consistent styling
+class ComponentThemes {
+ static CardThemeData cardTheme(ColorScheme colorScheme) {
+ return CardThemeData(
+ elevation: AppShadow.cardElevation,
+ shadowColor: AppShadow.getShadowColor(colorScheme.brightness),
+ shape: RoundedRectangleBorder(
+ borderRadius: AppBorders.circularMd,
+ ),
+ clipBehavior: Clip.antiAlias,
+ color: colorScheme.surface,
+ );
+ }
+
+ static AppBarTheme appBarTheme(ColorScheme colorScheme) {
+ return AppBarTheme(
+ backgroundColor: colorScheme.surface,
+ foregroundColor: colorScheme.onSurface,
+ elevation: AppShadow.appBarElevation,
+ centerTitle: false,
+ iconTheme: IconThemeData(
+ color: colorScheme.onSurface,
+ size: AppSizes.iconMd,
+ ),
+ titleTextStyle: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w600,
+ fontSize: 20,
+ color: colorScheme.onSurface,
+ ),
+ );
+ }
+
+ static FilledButtonThemeData filledButtonTheme(ColorScheme colorScheme) {
+ return FilledButtonThemeData(
+ style: FilledButton.styleFrom(
+ backgroundColor: colorScheme.primary,
+ foregroundColor: colorScheme.onPrimary,
+ elevation: AppShadow.elevationSm,
+ padding: AppSpacing.paddingButton,
+ shape: RoundedRectangleBorder(
+ borderRadius: AppBorders.circularSm,
+ ),
+ textStyle: const TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w500,
+ fontSize: 14,
+ ),
+ ),
+ );
+ }
+
+ static ElevatedButtonThemeData elevatedButtonTheme(ColorScheme colorScheme) {
+ return ElevatedButtonThemeData(
+ style: ElevatedButton.styleFrom(
+ backgroundColor: colorScheme.primary,
+ foregroundColor: colorScheme.onPrimary,
+ elevation: AppShadow.elevationSm,
+ padding: AppSpacing.paddingButton,
+ shape: RoundedRectangleBorder(
+ borderRadius: AppBorders.circularSm,
+ ),
+ textStyle: const TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w500,
+ fontSize: 14,
+ ),
+ ),
+ );
+ }
+
+ static OutlinedButtonThemeData outlinedButtonTheme(ColorScheme colorScheme) {
+ return OutlinedButtonThemeData(
+ style: OutlinedButton.styleFrom(
+ foregroundColor: colorScheme.primary,
+ side: BorderSide(color: colorScheme.outline),
+ padding: AppSpacing.paddingButton,
+ shape: RoundedRectangleBorder(
+ borderRadius: AppBorders.circularSm,
+ ),
+ textStyle: const TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w500,
+ fontSize: 14,
+ ),
+ ),
+ );
+ }
+
+ static InputDecorationTheme inputDecorationTheme(ColorScheme colorScheme) {
+ return InputDecorationTheme(
+ filled: true,
+ fillColor: colorScheme.surfaceContainerHighest.withOpacity(0.5),
+ contentPadding: EdgeInsets.symmetric(
+ horizontal: AppSpacing.inputPadding,
+ vertical: AppSpacing.md,
+ ),
+ border: OutlineInputBorder(
+ borderRadius: AppBorders.circularSm,
+ borderSide: BorderSide(
+ color: colorScheme.outline.withOpacity(0.3),
+ ),
+ ),
+ enabledBorder: OutlineInputBorder(
+ borderRadius: AppBorders.circularSm,
+ borderSide: BorderSide(
+ color: colorScheme.outline.withOpacity(0.3),
+ width: 1,
+ ),
+ ),
+ focusedBorder: OutlineInputBorder(
+ borderRadius: AppBorders.circularSm,
+ borderSide: BorderSide(
+ color: colorScheme.primary,
+ width: 2,
+ ),
+ ),
+ errorBorder: OutlineInputBorder(
+ borderRadius: AppBorders.circularSm,
+ borderSide: BorderSide(
+ color: colorScheme.error,
+ width: 1,
+ ),
+ ),
+ focusedErrorBorder: OutlineInputBorder(
+ borderRadius: AppBorders.circularSm,
+ borderSide: BorderSide(
+ color: colorScheme.error,
+ width: 2,
+ ),
+ ),
+ labelStyle: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w500,
+ color: colorScheme.onSurfaceVariant,
+ ),
+ hintStyle: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w400,
+ color: colorScheme.onSurfaceVariant.withOpacity(0.6),
+ ),
+ );
+ }
+
+ static SnackBarThemeData snackBarTheme(ColorScheme colorScheme) {
+ return SnackBarThemeData(
+ backgroundColor: colorScheme.inverseSurface,
+ contentTextStyle: TextStyle(
+ fontFamily: 'Montserrat',
+ color: colorScheme.onInverseSurface,
+ ),
+ behavior: SnackBarBehavior.floating,
+ shape: RoundedRectangleBorder(
+ borderRadius: AppBorders.circularSm,
+ ),
+ );
+ }
+
+ static DialogThemeData dialogTheme(ColorScheme colorScheme) {
+ return DialogThemeData(
+ backgroundColor: colorScheme.surface,
+ elevation: AppShadow.dialogElevation,
+ shape: RoundedRectangleBorder(
+ borderRadius: AppBorders.circularLg,
+ ),
+ contentTextStyle: TextStyle(
+ fontFamily: 'Montserrat',
+ color: colorScheme.onSurface,
+ ),
+ );
+ }
+
+ static BottomNavigationBarThemeData bottomNavigationBarTheme(
+ ColorScheme colorScheme,
+ ) {
+ return BottomNavigationBarThemeData(
+ backgroundColor: colorScheme.surface,
+ selectedItemColor: colorScheme.primary,
+ unselectedItemColor: colorScheme.onSurfaceVariant,
+ showSelectedLabels: true,
+ showUnselectedLabels: true,
+ type: BottomNavigationBarType.fixed,
+ selectedLabelStyle: const TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w500,
+ fontSize: 12,
+ ),
+ unselectedLabelStyle: const TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w400,
+ fontSize: 12,
+ ),
+ );
+ }
+
+ static ChipThemeData chipTheme(ColorScheme colorScheme) {
+ return ChipThemeData(
+ backgroundColor: colorScheme.surfaceContainerHighest,
+ deleteIconColor: colorScheme.onSurfaceVariant,
+ disabledColor: colorScheme.surfaceContainerHighest.withOpacity(0.38),
+ padding: EdgeInsets.symmetric(
+ horizontal: AppSpacing.sm,
+ vertical: AppSpacing.xs,
+ ),
+ shape: RoundedRectangleBorder(
+ borderRadius: AppBorders.circularSm,
+ ),
+ labelStyle: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w500,
+ color: colorScheme.onSurfaceVariant,
+ ),
+ secondaryLabelStyle: TextStyle(
+ fontFamily: 'Montserrat',
+ fontWeight: FontWeight.w500,
+ color: colorScheme.onSurfaceVariant,
+ ),
+ brightness: colorScheme.brightness,
+ );
+ }
+
+ static ProgressIndicatorThemeData progressIndicatorTheme(
+ ColorScheme colorScheme,
+ ) {
+ return ProgressIndicatorThemeData(
+ color: colorScheme.primary,
+ linearTrackColor: colorScheme.surfaceContainerHighest,
+ circularTrackColor: colorScheme.surfaceContainerHighest,
+ );
+ }
+
+ static FloatingActionButtonThemeData floatingActionButtonTheme(
+ ColorScheme colorScheme,
+ ) {
+ return FloatingActionButtonThemeData(
+ backgroundColor: colorScheme.primary,
+ foregroundColor: colorScheme.onPrimary,
+ elevation: AppShadow.fabElevation,
+ shape: RoundedRectangleBorder(
+ borderRadius: AppBorders.circularMd,
+ ),
+ );
+ }
+
+ static SliderThemeData sliderTheme(ColorScheme colorScheme) {
+ return SliderThemeData(
+ trackHeight: 4.0,
+ activeTrackColor: colorScheme.primary,
+ inactiveTrackColor: colorScheme.surfaceContainerHighest,
+ thumbColor: colorScheme.primary,
+ overlayColor: colorScheme.primary.withOpacity(0.12),
+ valueIndicatorColor: colorScheme.primary,
+ );
+ }
+}
diff --git a/lib/theme/gradient_system.dart b/lib/theme/gradient_system.dart
new file mode 100644
index 0000000..658c0ea
--- /dev/null
+++ b/lib/theme/gradient_system.dart
@@ -0,0 +1,332 @@
+import 'package:flutter/material.dart';
+import 'color_system.dart';
+
+/// Svrnty Gradient System
+/// Predefined gradients for status bars, progress indicators, and backgrounds
+class AppGradients {
+ // Prevent instantiation
+ AppGradients._();
+
+ // ============================================
+ // DELIVERY STATUS GRADIENTS
+ // ============================================
+
+ /// Pending status gradient (Slate Gray)
+ static const LinearGradient gradientStatusPending = LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ colors: [
+ SvrntyColors.statusPending,
+ Color(0x506576), // Slightly different shade for gradient effect
+ ],
+ );
+
+ /// In Progress status gradient (Blue/Info)
+ static const LinearGradient gradientStatusInProgress = LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ colors: [
+ SvrntyColors.statusInProgress,
+ Color(0x5B9BFF), // Lighter blue for gradient
+ ],
+ );
+
+ /// Completed status gradient (Green/Success)
+ static const LinearGradient gradientStatusCompleted = LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ colors: [
+ SvrntyColors.statusCompleted,
+ Color(0x4ADE80), // Lighter green for gradient
+ ],
+ );
+
+ /// Skipped status gradient (Amber/Warning)
+ static const LinearGradient gradientStatusSkipped = LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ colors: [
+ SvrntyColors.statusSkipped,
+ Color(0xFBBF24), // Lighter amber for gradient
+ ],
+ );
+
+ /// Failed status gradient (Red/Error)
+ static const LinearGradient gradientStatusFailed = LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ colors: [
+ SvrntyColors.statusFailed,
+ Color(0xFF7D7D), // Lighter red for gradient
+ ],
+ );
+
+ // ============================================
+ // PROGRESS INDICATOR GRADIENTS
+ // ============================================
+
+ /// Progress bar gradient (Blue to transparent)
+ static LinearGradient gradientProgress({
+ required Color color,
+ bool horizontal = true,
+ }) {
+ return LinearGradient(
+ begin: horizontal ? Alignment.centerLeft : Alignment.topCenter,
+ end: horizontal ? Alignment.centerRight : Alignment.bottomCenter,
+ colors: [
+ color,
+ color.withOpacity(0.8),
+ ],
+ );
+ }
+
+ /// Success progress gradient
+ static const LinearGradient gradientProgressSuccess = LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ colors: [
+ SvrntyColors.success,
+ Color(0x4ADE80), // Lighter green
+ ],
+ );
+
+ /// Warning progress gradient
+ static const LinearGradient gradientProgressWarning = LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ colors: [
+ SvrntyColors.warning,
+ Color(0xFBBF24), // Lighter amber
+ ],
+ );
+
+ /// Error progress gradient
+ static const LinearGradient gradientProgressError = LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ colors: [
+ SvrntyColors.error,
+ Color(0xFF7D7D), // Lighter red
+ ],
+ );
+
+ /// Info progress gradient
+ static const LinearGradient gradientProgressInfo = LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ colors: [
+ SvrntyColors.info,
+ Color(0x5B9BFF), // Lighter blue
+ ],
+ );
+
+ // ============================================
+ // BRAND GRADIENTS
+ // ============================================
+
+ /// Primary brand gradient (Crimson Red)
+ static const LinearGradient gradientPrimary = LinearGradient(
+ begin: Alignment.topLeft,
+ end: Alignment.bottomRight,
+ colors: [
+ SvrntyColors.crimsonRed,
+ Color(0xC44D58), // Slightly darker shade
+ ],
+ );
+
+ /// Secondary brand gradient (Slate Blue)
+ static const LinearGradient gradientSecondary = LinearGradient(
+ begin: Alignment.topLeft,
+ end: Alignment.bottomRight,
+ colors: [
+ SvrntyColors.darkSlate,
+ SvrntyColors.slateGray,
+ ],
+ );
+
+ /// Accent gradient (Crimson to Slate)
+ static const LinearGradient gradientAccent = LinearGradient(
+ begin: Alignment.topLeft,
+ end: Alignment.bottomRight,
+ colors: [
+ SvrntyColors.crimsonRed,
+ SvrntyColors.darkSlate,
+ ],
+ );
+
+ // ============================================
+ // BACKGROUND GRADIENTS
+ // ============================================
+
+ /// Light background gradient
+ static const LinearGradient gradientBackgroundLight = LinearGradient(
+ begin: Alignment.topLeft,
+ end: Alignment.bottomRight,
+ colors: [
+ Color(0xFAFAFC),
+ Color(0xF5F7FA),
+ ],
+ );
+
+ /// Dark background gradient
+ static const LinearGradient gradientBackgroundDark = LinearGradient(
+ begin: Alignment.topLeft,
+ end: Alignment.bottomRight,
+ colors: [
+ Color(0x1A1C1E),
+ Color(0x2A2D34),
+ ],
+ );
+
+ /// Elevated surface gradient (light)
+ static const LinearGradient gradientElevatedLight = LinearGradient(
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
+ colors: [
+ Color(0xFFFFFF),
+ Color(0xF5F7FA),
+ ],
+ );
+
+ /// Elevated surface gradient (dark)
+ static const LinearGradient gradientElevatedDark = LinearGradient(
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
+ colors: [
+ Color(0x2A2D34),
+ Color(0x1F2123),
+ ],
+ );
+
+ // ============================================
+ // OVERLAY GRADIENTS
+ // ============================================
+
+ /// Dark overlay gradient (for images)
+ static const LinearGradient gradientOverlayDark = LinearGradient(
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
+ colors: [
+ Color(0x00000000), // Transparent at top
+ Color(0x4D000000), // Dark at bottom
+ ],
+ );
+
+ /// Light overlay gradient
+ static const LinearGradient gradientOverlayLight = LinearGradient(
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
+ colors: [
+ Color(0x00FFFFFF), // Transparent at top
+ Color(0x4DFFFFFF), // Light at bottom
+ ],
+ );
+
+ /// Vignette gradient (darkened edges)
+ static const RadialGradient gradientVignette = RadialGradient(
+ center: Alignment.center,
+ radius: 1.2,
+ colors: [
+ Color(0x00000000),
+ Color(0x80000000),
+ ],
+ );
+
+ // ============================================
+ // SHIMMER GRADIENTS (for loading states)
+ // ============================================
+
+ /// Shimmer gradient light theme
+ static const LinearGradient gradientShimmerLight = LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ colors: [
+ Color(0xFFFFFFFF),
+ Color(0x80F0F0F0),
+ Color(0xFFFFFFFF),
+ ],
+ stops: [0.1, 0.5, 0.9],
+ );
+
+ /// Shimmer gradient dark theme
+ static const LinearGradient gradientShimmerDark = LinearGradient(
+ begin: Alignment.centerLeft,
+ end: Alignment.centerRight,
+ colors: [
+ Color(0xFF2A2D34),
+ Color(0x80383940),
+ Color(0xFF2A2D34),
+ ],
+ stops: [0.1, 0.5, 0.9],
+ );
+
+ // ============================================
+ // UTILITY METHODS
+ // ============================================
+
+ /// Get status gradient based on delivery status
+ static LinearGradient getStatusGradient(String status) {
+ switch (status.toLowerCase()) {
+ case 'pending':
+ return gradientStatusPending;
+ case 'in_progress':
+ case 'inprogress':
+ return gradientStatusInProgress;
+ case 'completed':
+ case 'done':
+ return gradientStatusCompleted;
+ case 'skipped':
+ return gradientStatusSkipped;
+ case 'failed':
+ return gradientStatusFailed;
+ default:
+ return gradientStatusPending;
+ }
+ }
+
+ /// Get progress gradient based on status
+ static LinearGradient getProgressGradient(String status) {
+ switch (status.toLowerCase()) {
+ case 'success':
+ return gradientProgressSuccess;
+ case 'warning':
+ return gradientProgressWarning;
+ case 'error':
+ return gradientProgressError;
+ case 'info':
+ return gradientProgressInfo;
+ default:
+ return gradientProgressSuccess;
+ }
+ }
+
+ /// Create a custom gradient with opacity
+ static LinearGradient withOpacity(
+ LinearGradient gradient,
+ double opacity,
+ ) {
+ return LinearGradient(
+ begin: gradient.begin,
+ end: gradient.end,
+ colors: gradient.colors
+ .map((color) => color.withOpacity(opacity))
+ .toList(),
+ stops: gradient.stops,
+ );
+ }
+
+ /// Create a directional gradient
+ static LinearGradient directional({
+ required List colors,
+ required Alignment begin,
+ required Alignment end,
+ List? stops,
+ }) {
+ return LinearGradient(
+ begin: begin,
+ end: end,
+ colors: colors,
+ stops: stops,
+ );
+ }
+}
diff --git a/lib/theme/shadow_system.dart b/lib/theme/shadow_system.dart
new file mode 100644
index 0000000..5934bcd
--- /dev/null
+++ b/lib/theme/shadow_system.dart
@@ -0,0 +1,208 @@
+import 'package:flutter/material.dart';
+
+/// Svrnty Shadow & Elevation System
+/// Comprehensive shadow and elevation definitions for light and dark themes
+class AppShadow {
+ // Prevent instantiation
+ AppShadow._();
+
+ // ============================================
+ // ELEVATION CONSTANTS
+ // ============================================
+
+ /// No elevation/shadow
+ static const double elevationNone = 0.0;
+
+ /// Minimal depth elevation
+ static const double elevationXs = 1.0;
+
+ /// Small shadow elevation (default for cards)
+ static const double elevationSm = 2.0;
+
+ /// Medium shadow elevation (hover states)
+ static const double elevationMd = 4.0;
+
+ /// Large shadow elevation
+ static const double elevationLg = 8.0;
+
+ /// Extra large shadow elevation (dialogs, prominent surfaces)
+ static const double elevationXl = 16.0;
+
+ // ============================================
+ // SHADOW DEFINITIONS - LIGHT THEME
+ // ============================================
+
+ /// Light theme shadow color (10% opacity black)
+ static const Color _shadowColorLight = Color(0x1A000000);
+
+ /// No shadow for light theme
+ static const List shadowNoneLight = [];
+
+ /// Minimal shadow for light theme
+ static const List shadowXsLight = [
+ BoxShadow(
+ color: _shadowColorLight,
+ blurRadius: 1,
+ offset: Offset(0, 1),
+ ),
+ ];
+
+ /// Small shadow for light theme (default for cards)
+ static const List shadowSmLight = [
+ BoxShadow(
+ color: _shadowColorLight,
+ blurRadius: 2,
+ offset: Offset(0, 1),
+ ),
+ ];
+
+ /// Medium shadow for light theme (hover states)
+ static const List shadowMdLight = [
+ BoxShadow(
+ color: _shadowColorLight,
+ blurRadius: 4,
+ offset: Offset(0, 2),
+ ),
+ ];
+
+ /// Large shadow for light theme
+ static const List shadowLgLight = [
+ BoxShadow(
+ color: _shadowColorLight,
+ blurRadius: 8,
+ offset: Offset(0, 4),
+ ),
+ ];
+
+ /// Extra large shadow for light theme (dialogs)
+ static const List shadowXlLight = [
+ BoxShadow(
+ color: _shadowColorLight,
+ blurRadius: 16,
+ offset: Offset(0, 8),
+ ),
+ ];
+
+ // ============================================
+ // SHADOW DEFINITIONS - DARK THEME
+ // ============================================
+
+ /// Dark theme shadow color (pure black)
+ static const Color _shadowColorDark = Color(0x4D000000);
+
+ /// No shadow for dark theme
+ static const List shadowNoneDark = [];
+
+ /// Minimal shadow for dark theme
+ static const List shadowXsDark = [
+ BoxShadow(
+ color: _shadowColorDark,
+ blurRadius: 1,
+ offset: Offset(0, 1),
+ ),
+ ];
+
+ /// Small shadow for dark theme (default for cards)
+ static const List shadowSmDark = [
+ BoxShadow(
+ color: _shadowColorDark,
+ blurRadius: 2,
+ offset: Offset(0, 1),
+ ),
+ ];
+
+ /// Medium shadow for dark theme (hover states)
+ static const List shadowMdDark = [
+ BoxShadow(
+ color: _shadowColorDark,
+ blurRadius: 4,
+ offset: Offset(0, 2),
+ ),
+ ];
+
+ /// Large shadow for dark theme
+ static const List shadowLgDark = [
+ BoxShadow(
+ color: _shadowColorDark,
+ blurRadius: 8,
+ offset: Offset(0, 4),
+ ),
+ ];
+
+ /// Extra large shadow for dark theme (dialogs)
+ static const List shadowXlDark = [
+ BoxShadow(
+ color: _shadowColorDark,
+ blurRadius: 16,
+ offset: Offset(0, 8),
+ ),
+ ];
+
+ // ============================================
+ // SHADOW UTILITY METHODS
+ // ============================================
+
+ /// Get shadow list based on brightness and elevation level
+ static List getShadow(
+ Brightness brightness,
+ double elevation,
+ ) {
+ final isDark = brightness == Brightness.dark;
+
+ switch (elevation) {
+ case elevationNone:
+ return isDark ? shadowNoneDark : shadowNoneLight;
+ case elevationXs:
+ return isDark ? shadowXsDark : shadowXsLight;
+ case elevationSm:
+ return isDark ? shadowSmDark : shadowSmLight;
+ case elevationMd:
+ return isDark ? shadowMdDark : shadowMdLight;
+ case elevationLg:
+ return isDark ? shadowLgDark : shadowLgLight;
+ case elevationXl:
+ return isDark ? shadowXlDark : shadowXlLight;
+ default:
+ return isDark ? shadowSmDark : shadowSmLight;
+ }
+ }
+
+ /// Get shadow color based on brightness
+ static Color getShadowColor(Brightness brightness) {
+ return brightness == Brightness.dark ? _shadowColorDark : _shadowColorLight;
+ }
+
+ // ============================================
+ // COMPONENT ELEVATION MAPPING
+ // ============================================
+
+ /// Card elevation (2)
+ static const double cardElevation = elevationSm;
+
+ /// Card hover elevation (4)
+ static const double cardHoverElevation = elevationMd;
+
+ /// AppBar elevation (0 - flat design)
+ static const double appBarElevation = elevationNone;
+
+ /// Dialog elevation (8)
+ static const double dialogElevation = elevationLg;
+
+ /// FAB elevation (8)
+ static const double fabElevation = elevationLg;
+
+ /// FAB hover elevation (16)
+ static const double fabHoverElevation = elevationXl;
+
+ /// Bottom sheet elevation (8)
+ static const double bottomSheetElevation = elevationLg;
+
+ /// Floating action button pressed elevation (12)
+ static const double fabPressedElevation = elevationXl;
+
+ /// Menu/Dropdown elevation (8)
+ static const double menuElevation = elevationLg;
+
+ /// Tooltip elevation (16)
+ static const double tooltipElevation = elevationXl;
+}
diff --git a/lib/theme/size_system.dart b/lib/theme/size_system.dart
new file mode 100644
index 0000000..22e462a
--- /dev/null
+++ b/lib/theme/size_system.dart
@@ -0,0 +1,236 @@
+import 'package:flutter/material.dart';
+
+/// Svrnty Size System
+/// Standard sizing constants for icons, buttons, containers, and other components
+class AppSizes {
+ // Prevent instantiation
+ AppSizes._();
+
+ // ============================================
+ // ICON SIZES
+ // ============================================
+
+ /// Extra small icon (16px)
+ static const double iconXs = 16.0;
+
+ /// Small icon (20px)
+ static const double iconSm = 20.0;
+
+ /// Standard icon size (24px)
+ static const double iconMd = 24.0;
+
+ /// Large icon size (32px)
+ static const double iconLg = 32.0;
+
+ /// Extra large icon (40px)
+ static const double iconXl = 40.0;
+
+ /// Huge icon (48px)
+ static const double iconXxl = 48.0;
+
+ /// Extra huge icon (56px)
+ static const double iconXxxl = 56.0;
+
+ // ============================================
+ // BUTTON SIZES
+ // ============================================
+
+ /// Small button height (32px)
+ static const double buttonHeightSm = 32.0;
+
+ /// Medium button height (40px) - Default
+ static const double buttonHeightMd = 40.0;
+
+ /// Large button height (48px)
+ static const double buttonHeightLg = 48.0;
+
+ /// Extra large button height (56px)
+ static const double buttonHeightXl = 56.0;
+
+ // ============================================
+ // INPUT FIELD SIZES
+ // ============================================
+
+ /// Input field height
+ static const double inputHeight = 56.0;
+
+ /// Compact input field height (no vertical padding)
+ static const double inputHeightCompact = 40.0;
+
+ /// Input field min width
+ static const double inputMinWidth = 48.0;
+
+ // ============================================
+ // CONTAINER & LAYOUT SIZES
+ // ============================================
+
+ /// Standard card minimum height
+ static const double cardMinHeight = 80.0;
+
+ /// Standard dialog max width (mobile)
+ static const double dialogMaxWidthMobile = 280.0;
+
+ /// Standard dialog max width (tablet/desktop)
+ static const double dialogMaxWidthDesktop = 560.0;
+
+ /// Maximum content width for centered layouts
+ static const double maxContentWidth = 1200.0;
+
+ /// Compact content width (forms, focused layouts)
+ static const double compactContentWidth = 600.0;
+
+ /// Standard container max width
+ static const double containerMaxWidth = 900.0;
+
+ // ============================================
+ // APPBAR & HEADER SIZES
+ // ============================================
+
+ /// Standard app bar height
+ static const double appBarHeight = 56.0;
+
+ /// Large app bar height
+ static const double appBarHeightLarge = 72.0;
+
+ /// Compact app bar height
+ static const double appBarHeightCompact = 48.0;
+
+ // ============================================
+ // BOTTOM SHEET SIZES
+ // ============================================
+
+ /// Bottom sheet max width
+ static const double bottomSheetMaxWidth = 540.0;
+
+ /// Bottom sheet default height (auto)
+ static const double bottomSheetHeightAuto = 0.0;
+
+ /// Bottom sheet half screen height
+ static const double bottomSheetHeightHalf = 0.5;
+
+ /// Bottom sheet 3/4 screen height
+ static const double bottomSheetHeight3Quarter = 0.75;
+
+ // ============================================
+ // ELEVATION & Z-INDEX
+ // ============================================
+
+ /// Standard z-index for floating elements
+ static const int zIndexFloating = 100;
+
+ /// Z-index for modals/dialogs
+ static const int zIndexModal = 50;
+
+ /// Z-index for tooltips
+ static const int zIndexTooltip = 150;
+
+ // ============================================
+ // DIVIDER & LINE SIZES
+ // ============================================
+
+ /// Divider thickness
+ static const double dividerThickness = 1.0;
+
+ /// Thin divider thickness (0.5px)
+ static const double dividerThicknessThin = 0.5;
+
+ /// Thick divider thickness (2px)
+ static const double dividerThicknessThick = 2.0;
+
+ /// Horizontal divider height
+ static const double dividerHeightHorizontal = 1.0;
+
+ /// Vertical divider width
+ static const double dividerWidthVertical = 1.0;
+
+ // ============================================
+ // PROGRESS INDICATOR SIZES
+ // ============================================
+
+ /// Progress indicator thickness
+ static const double progressIndicatorThickness = 4.0;
+
+ /// Circular progress indicator size
+ static const double circularProgressSize = 48.0;
+
+ /// Linear progress indicator height (thin)
+ static const double linearProgressHeightThin = 2.0;
+
+ /// Linear progress indicator height (standard)
+ static const double linearProgressHeightStandard = 4.0;
+
+ /// Linear progress indicator height (thick)
+ static const double linearProgressHeightThick = 8.0;
+
+ // ============================================
+ // CHIP & BADGE SIZES
+ // ============================================
+
+ /// Chip height
+ static const double chipHeight = 32.0;
+
+ /// Small chip height
+ static const double chipHeightSm = 24.0;
+
+ /// Badge size (for counter badges)
+ static const double badgeSize = 24.0;
+
+ /// Badge size (small)
+ static const double badgeSizeSm = 16.0;
+
+ // ============================================
+ // AVATAR SIZES
+ // ============================================
+
+ /// Small avatar size
+ static const double avatarSizeSm = 32.0;
+
+ /// Medium avatar size
+ static const double avatarSizeMd = 48.0;
+
+ /// Large avatar size
+ static const double avatarSizeLg = 64.0;
+
+ /// Extra large avatar size
+ static const double avatarSizeXl = 80.0;
+
+ // ============================================
+ // RESPONSIVE SIZING
+ // ============================================
+
+ /// Minimum tap target size (Material guidelines - 48px)
+ static const double minTapTarget = 48.0;
+
+ /// Minimum tap target size for desktop (36px)
+ static const double minTapTargetDesktop = 36.0;
+
+ /// Standard element spacing
+ static const double elementSpacing = 8.0;
+
+ /// Large element spacing
+ static const double elementSpacingLarge = 16.0;
+
+ // ============================================
+ // UTILITY METHODS
+ // ============================================
+
+ /// Get responsive button height based on device type
+ static double getButtonHeight(bool isCompact) {
+ return isCompact ? buttonHeightMd : buttonHeightLg;
+ }
+
+ /// Get responsive dialog max width based on screen width
+ static double getDialogMaxWidth(double screenWidth) {
+ return screenWidth < 600 ? dialogMaxWidthMobile : dialogMaxWidthDesktop;
+ }
+
+ /// Get responsive icon size
+ static double getIconSize({
+ required bool isCompact,
+ required bool isLarge,
+ }) {
+ if (isLarge) return iconLg;
+ if (isCompact) return iconSm;
+ return iconMd;
+ }
+}
diff --git a/lib/theme/spacing_system.dart b/lib/theme/spacing_system.dart
new file mode 100644
index 0000000..7ee1e21
--- /dev/null
+++ b/lib/theme/spacing_system.dart
@@ -0,0 +1,296 @@
+import 'package:flutter/material.dart';
+
+/// Svrnty Spacing System - 4px Grid
+/// All spacing, sizing, and border radius values follow a strict 4px grid
+class AppSpacing {
+ // Prevent instantiation
+ AppSpacing._();
+
+ // ============================================
+ // BASE SPACING SCALE (4px grid)
+ // ============================================
+
+ /// Extra small spacing (4px) - unit × 1
+ static const double xs = 4.0;
+
+ /// Small spacing (8px) - unit × 2
+ static const double sm = 8.0;
+
+ /// Medium spacing (16px) - unit × 4 - DEFAULT
+ static const double md = 16.0;
+
+ /// Large spacing (24px) - unit × 6
+ static const double lg = 24.0;
+
+ /// Extra large spacing (32px) - unit × 8
+ static const double xl = 32.0;
+
+ /// Double extra large (48px) - unit × 12
+ static const double xxl = 48.0;
+
+ /// Triple extra large (64px) - unit × 16
+ static const double xxxl = 64.0;
+
+ // ============================================
+ // COMPONENT-SPECIFIC SPACING
+ // ============================================
+
+ /// Padding inside cards
+ static const double cardPadding = md; // 16px
+
+ /// Horizontal button padding
+ static const double buttonPaddingX = lg; // 24px
+
+ /// Vertical button padding
+ static const double buttonPaddingY = 12.0; // sm × 1.5
+
+ /// Padding in input fields
+ static const double inputPadding = md; // 16px
+
+ /// Standard icon size
+ static const double iconSize = lg; // 24px
+
+ /// Large icon size
+ static const double iconSizeLarge = xl; // 32px
+
+ /// Dialog content padding
+ static const double dialogPadding = lg; // 24px
+
+ /// Standard app bar height
+ static const double appBarHeight = 56.0;
+
+ /// List item padding
+ static const double listItemPadding = md; // 16px
+
+ // ============================================
+ // PRE-BUILT EDGEINSETS - ALL SIDES
+ // ============================================
+
+ /// EdgeInsets.all(4px)
+ static const EdgeInsets paddingAllXs = EdgeInsets.all(xs);
+
+ /// EdgeInsets.all(8px)
+ static const EdgeInsets paddingAllSm = EdgeInsets.all(sm);
+
+ /// EdgeInsets.all(16px)
+ static const EdgeInsets paddingAllMd = EdgeInsets.all(md);
+
+ /// EdgeInsets.all(24px)
+ static const EdgeInsets paddingAllLg = EdgeInsets.all(lg);
+
+ /// EdgeInsets.all(32px)
+ static const EdgeInsets paddingAllXl = EdgeInsets.all(xl);
+
+ /// EdgeInsets.all(48px)
+ static const EdgeInsets paddingAllXxl = EdgeInsets.all(xxl);
+
+ // ============================================
+ // PRE-BUILT EDGEINSETS - HORIZONTAL
+ // ============================================
+
+ /// EdgeInsets.symmetric(horizontal: 4px)
+ static const EdgeInsets paddingHorizontalXs =
+ EdgeInsets.symmetric(horizontal: xs);
+
+ /// EdgeInsets.symmetric(horizontal: 8px)
+ static const EdgeInsets paddingHorizontalSm =
+ EdgeInsets.symmetric(horizontal: sm);
+
+ /// EdgeInsets.symmetric(horizontal: 16px)
+ static const EdgeInsets paddingHorizontalMd =
+ EdgeInsets.symmetric(horizontal: md);
+
+ /// EdgeInsets.symmetric(horizontal: 24px)
+ static const EdgeInsets paddingHorizontalLg =
+ EdgeInsets.symmetric(horizontal: lg);
+
+ /// EdgeInsets.symmetric(horizontal: 32px)
+ static const EdgeInsets paddingHorizontalXl =
+ EdgeInsets.symmetric(horizontal: xl);
+
+ // ============================================
+ // PRE-BUILT EDGEINSETS - VERTICAL
+ // ============================================
+
+ /// EdgeInsets.symmetric(vertical: 4px)
+ static const EdgeInsets paddingVerticalXs =
+ EdgeInsets.symmetric(vertical: xs);
+
+ /// EdgeInsets.symmetric(vertical: 8px)
+ static const EdgeInsets paddingVerticalSm =
+ EdgeInsets.symmetric(vertical: sm);
+
+ /// EdgeInsets.symmetric(vertical: 16px)
+ static const EdgeInsets paddingVerticalMd =
+ EdgeInsets.symmetric(vertical: md);
+
+ /// EdgeInsets.symmetric(vertical: 24px)
+ static const EdgeInsets paddingVerticalLg =
+ EdgeInsets.symmetric(vertical: lg);
+
+ /// EdgeInsets.symmetric(vertical: 32px)
+ static const EdgeInsets paddingVerticalXl =
+ EdgeInsets.symmetric(vertical: xl);
+
+ // ============================================
+ // PRE-BUILT EDGEINSETS - COMPONENT SPECIFIC
+ // ============================================
+
+ /// Card padding (16px all sides)
+ static const EdgeInsets paddingCard = paddingAllMd;
+
+ /// Button padding (horizontal: 24px, vertical: 12px)
+ static const EdgeInsets paddingButton =
+ EdgeInsets.symmetric(horizontal: lg, vertical: buttonPaddingY);
+
+ /// List item padding (horizontal: 16px, vertical: 8px)
+ static const EdgeInsets paddingListItem =
+ EdgeInsets.symmetric(horizontal: md, vertical: sm);
+
+ /// Dialog padding (24px all sides)
+ static const EdgeInsets paddingDialog = paddingAllLg;
+
+ // ============================================
+ // SPACER WIDGETS - UNIVERSAL (SQUARE)
+ // ============================================
+
+ /// SizedBox(width: 4, height: 4)
+ static const Widget spacerXs = SizedBox(width: xs, height: xs);
+
+ /// SizedBox(width: 8, height: 8)
+ static const Widget spacerSm = SizedBox(width: sm, height: sm);
+
+ /// SizedBox(width: 16, height: 16)
+ static const Widget spacerMd = SizedBox(width: md, height: md);
+
+ /// SizedBox(width: 24, height: 24)
+ static const Widget spacerLg = SizedBox(width: lg, height: lg);
+
+ /// SizedBox(width: 32, height: 32)
+ static const Widget spacerXl = SizedBox(width: xl, height: xl);
+
+ // ============================================
+ // SPACER WIDGETS - VERTICAL
+ // ============================================
+
+ /// SizedBox(height: 4)
+ static const Widget vSpacerXs = SizedBox(height: xs);
+
+ /// SizedBox(height: 8)
+ static const Widget vSpacerSm = SizedBox(height: sm);
+
+ /// SizedBox(height: 16)
+ static const Widget vSpacerMd = SizedBox(height: md);
+
+ /// SizedBox(height: 24)
+ static const Widget vSpacerLg = SizedBox(height: lg);
+
+ /// SizedBox(height: 32)
+ static const Widget vSpacerXl = SizedBox(height: xl);
+
+ /// SizedBox(height: 48)
+ static const Widget vSpacerXxl = SizedBox(height: xxl);
+
+ /// SizedBox(height: 64)
+ static const Widget vSpacerXxxl = SizedBox(height: xxxl);
+
+ // ============================================
+ // SPACER WIDGETS - HORIZONTAL
+ // ============================================
+
+ /// SizedBox(width: 4)
+ static const Widget hSpacerXs = SizedBox(width: xs);
+
+ /// SizedBox(width: 8)
+ static const Widget hSpacerSm = SizedBox(width: sm);
+
+ /// SizedBox(width: 16)
+ static const Widget hSpacerMd = SizedBox(width: md);
+
+ /// SizedBox(width: 24)
+ static const Widget hSpacerLg = SizedBox(width: lg);
+
+ /// SizedBox(width: 32)
+ static const Widget hSpacerXl = SizedBox(width: xl);
+
+ /// SizedBox(width: 48)
+ static const Widget hSpacerXxl = SizedBox(width: xxl);
+
+ /// SizedBox(width: 64)
+ static const Widget hSpacerXxxl = SizedBox(width: xxxl);
+
+ // ============================================
+ // GAPS FOR ROW/COLUMN SPACING
+ // ============================================
+
+ /// Gap for Row/Column (4px)
+ static const double gapXs = xs;
+
+ /// Gap for Row/Column (8px)
+ static const double gapSm = sm;
+
+ /// Gap for Row/Column (16px)
+ static const double gapMd = md;
+
+ /// Gap for Row/Column (24px)
+ static const double gapLg = lg;
+
+ /// Gap for Row/Column (32px)
+ static const double gapXl = xl;
+
+ // ============================================
+ // RESPONSIVE SCREEN MARGINS
+ // ============================================
+
+ /// Screen margin for mobile devices (< 600px)
+ static const double screenMarginMobile = md; // 16px
+
+ /// Screen margin for tablets (600-1024px)
+ static const double screenMarginTablet = lg; // 24px
+
+ /// Screen margin for desktop (> 1024px)
+ static const double screenMarginDesktop = xl; // 32px
+
+ /// Max content width constraint
+ static const double maxContentWidth = 1200.0;
+
+ /// Compact content width for forms/compact layouts
+ static const double compactContentWidth = 600.0;
+
+ // ============================================
+ // RESPONSIVE PADDING FOR SCREENS
+ // ============================================
+
+ /// Screen padding for mobile
+ static const EdgeInsets screenPaddingMobile =
+ EdgeInsets.symmetric(horizontal: screenMarginMobile);
+
+ /// Screen padding for tablet
+ static const EdgeInsets screenPaddingTablet =
+ EdgeInsets.symmetric(horizontal: screenMarginTablet);
+
+ /// Screen padding for desktop
+ static const EdgeInsets screenPaddingDesktop =
+ EdgeInsets.symmetric(horizontal: screenMarginDesktop);
+
+ // ============================================
+ // UTILITY METHODS
+ // ============================================
+
+ /// Get responsive screen margin based on screen width
+ static double getScreenMargin(double screenWidth) {
+ if (screenWidth < 600) {
+ return screenMarginMobile;
+ } else if (screenWidth < 1024) {
+ return screenMarginTablet;
+ } else {
+ return screenMarginDesktop;
+ }
+ }
+
+ /// Get responsive screen padding based on screen width
+ static EdgeInsets getScreenPadding(double screenWidth) {
+ final margin = getScreenMargin(screenWidth);
+ return EdgeInsets.symmetric(horizontal: margin);
+ }
+}
diff --git a/lib/theme/typography_system.dart b/lib/theme/typography_system.dart
new file mode 100644
index 0000000..2da630c
--- /dev/null
+++ b/lib/theme/typography_system.dart
@@ -0,0 +1,331 @@
+import 'package:flutter/material.dart';
+
+/// Svrnty Typography System
+/// Extended text styles and typography utilities beyond Material Design 3
+class AppTypography {
+ // Prevent instantiation
+ AppTypography._();
+
+ // ============================================
+ // FONT FAMILIES
+ // ============================================
+
+ /// Primary font family (Montserrat)
+ static const String fontFamilyPrimary = 'Montserrat';
+
+ /// Monospace font family (IBM Plex Mono)
+ static const String fontFamilyMono = 'IBMPlexMono';
+
+ // ============================================
+ // FONT WEIGHTS
+ // ============================================
+
+ /// Light font weight (300)
+ static const FontWeight weightLight = FontWeight.w300;
+
+ /// Regular font weight (400)
+ static const FontWeight weightRegular = FontWeight.w400;
+
+ /// Medium font weight (500)
+ static const FontWeight weightMedium = FontWeight.w500;
+
+ /// Semi-bold font weight (600)
+ static const FontWeight weightSemiBold = FontWeight.w600;
+
+ /// Bold font weight (700)
+ static const FontWeight weightBold = FontWeight.w700;
+
+ // ============================================
+ // FONT SIZES
+ // ============================================
+
+ /// Display Large font size (57px)
+ static const double sizeDisplayLarge = 57.0;
+
+ /// Display Medium font size (45px)
+ static const double sizeDisplayMedium = 45.0;
+
+ /// Display Small font size (36px)
+ static const double sizeDisplaySmall = 36.0;
+
+ /// Headline Large font size (32px)
+ static const double sizeHeadlineLarge = 32.0;
+
+ /// Headline Medium font size (28px)
+ static const double sizeHeadlineMedium = 28.0;
+
+ /// Headline Small font size (24px)
+ static const double sizeHeadlineSmall = 24.0;
+
+ /// Title Large font size (22px)
+ static const double sizeTitleLarge = 22.0;
+
+ /// Title Medium font size (16px)
+ static const double sizeTitleMedium = 16.0;
+
+ /// Title Small font size (14px)
+ static const double sizeTitleSmall = 14.0;
+
+ /// Body Large font size (16px)
+ static const double sizeBodyLarge = 16.0;
+
+ /// Body Medium font size (14px)
+ static const double sizeBodyMedium = 14.0;
+
+ /// Body Small font size (12px)
+ static const double sizeBodySmall = 12.0;
+
+ /// Label Large font size (14px)
+ static const double sizeLabelLarge = 14.0;
+
+ /// Label Medium font size (12px)
+ static const double sizeLabelMedium = 12.0;
+
+ /// Label Small font size (11px)
+ static const double sizeLabelSmall = 11.0;
+
+ // ============================================
+ // LINE HEIGHTS
+ // ============================================
+
+ /// Display Large line height (1.12 = 64px)
+ static const double lineHeightDisplayLarge = 1.12;
+
+ /// Display Medium line height (1.16 = 52px)
+ static const double lineHeightDisplayMedium = 1.16;
+
+ /// Display Small line height (1.22 = 44px)
+ static const double lineHeightDisplaySmall = 1.22;
+
+ /// Headline Large line height (1.25 = 40px)
+ static const double lineHeightHeadlineLarge = 1.25;
+
+ /// Headline Medium line height (1.29 = 36px)
+ static const double lineHeightHeadlineMedium = 1.29;
+
+ /// Headline Small line height (1.33 = 32px)
+ static const double lineHeightHeadlineSmall = 1.33;
+
+ /// Title Large line height (1.27 = 28px)
+ static const double lineHeightTitleLarge = 1.27;
+
+ /// Title Medium line height (1.5 = 24px)
+ static const double lineHeightTitleMedium = 1.5;
+
+ /// Title Small line height (1.43 = 20px)
+ static const double lineHeightTitleSmall = 1.43;
+
+ /// Body Large line height (1.5 = 24px)
+ static const double lineHeightBodyLarge = 1.5;
+
+ /// Body Medium line height (1.43 = 20px)
+ static const double lineHeightBodyMedium = 1.43;
+
+ /// Body Small line height (1.33 = 16px)
+ static const double lineHeightBodySmall = 1.33;
+
+ /// Label Large line height (1.43 = 20px)
+ static const double lineHeightLabelLarge = 1.43;
+
+ /// Label Medium line height (1.33 = 16px)
+ static const double lineHeightLabelMedium = 1.33;
+
+ /// Label Small line height (1.45 = 16px)
+ static const double lineHeightLabelSmall = 1.45;
+
+ // ============================================
+ // LETTER SPACING
+ // ============================================
+
+ /// Display Large letter spacing (-0.5px)
+ static const double letterSpacingDisplayLarge = -0.5;
+
+ /// Display Medium letter spacing (-0.5px)
+ static const double letterSpacingDisplayMedium = -0.5;
+
+ /// Display Small letter spacing (-0.25px)
+ static const double letterSpacingDisplaySmall = -0.25;
+
+ /// Headline Large letter spacing (-0.25px)
+ static const double letterSpacingHeadlineLarge = -0.25;
+
+ /// Headline Medium letter spacing (0px)
+ static const double letterSpacingHeadlineMedium = 0.0;
+
+ /// Headline Small letter spacing (0px)
+ static const double letterSpacingHeadlineSmall = 0.0;
+
+ /// Title Large letter spacing (0px)
+ static const double letterSpacingTitleLarge = 0.0;
+
+ /// Title Medium letter spacing (0.15px)
+ static const double letterSpacingTitleMedium = 0.15;
+
+ /// Title Small letter spacing (0.1px)
+ static const double letterSpacingTitleSmall = 0.1;
+
+ /// Body Large letter spacing (0.5px)
+ static const double letterSpacingBodyLarge = 0.5;
+
+ /// Body Medium letter spacing (0.25px)
+ static const double letterSpacingBodyMedium = 0.25;
+
+ /// Body Small letter spacing (0.4px)
+ static const double letterSpacingBodySmall = 0.4;
+
+ /// Label Large letter spacing (0.1px)
+ static const double letterSpacingLabelLarge = 0.1;
+
+ /// Label Medium letter spacing (0.5px)
+ static const double letterSpacingLabelMedium = 0.5;
+
+ /// Label Small letter spacing (0.5px)
+ static const double letterSpacingLabelSmall = 0.5;
+
+ // ============================================
+ // CUSTOM TEXT STYLES
+ // ============================================
+
+ /// Monospace small text style
+ static const TextStyle monoSmall = TextStyle(
+ fontFamily: fontFamilyMono,
+ fontSize: sizeBodySmall,
+ fontWeight: weightRegular,
+ );
+
+ /// Monospace medium text style
+ static const TextStyle monoMedium = TextStyle(
+ fontFamily: fontFamilyMono,
+ fontSize: sizeBodyMedium,
+ fontWeight: weightRegular,
+ );
+
+ /// Monospace large text style
+ static const TextStyle monoLarge = TextStyle(
+ fontFamily: fontFamilyMono,
+ fontSize: sizeBodyLarge,
+ fontWeight: weightRegular,
+ );
+
+ /// Monospace bold text style
+ static const TextStyle monoBold = TextStyle(
+ fontFamily: fontFamilyMono,
+ fontSize: sizeBodyMedium,
+ fontWeight: weightBold,
+ );
+
+ /// Code snippet text style
+ static const TextStyle codeStyle = TextStyle(
+ fontFamily: fontFamilyMono,
+ fontSize: sizeBodySmall,
+ fontWeight: weightRegular,
+ backgroundColor: Color(0xFFF5F5F5),
+ );
+
+ // ============================================
+ // TEXT STYLE EXTENSIONS
+ // ============================================
+
+ /// Create a text style with color override
+ static TextStyle withColor(
+ TextStyle baseStyle,
+ Color color,
+ ) {
+ return baseStyle.copyWith(color: color);
+ }
+
+ /// Create a text style with size override
+ static TextStyle withSize(
+ TextStyle baseStyle,
+ double fontSize,
+ ) {
+ return baseStyle.copyWith(fontSize: fontSize);
+ }
+
+ /// Create a text style with weight override
+ static TextStyle withWeight(
+ TextStyle baseStyle,
+ FontWeight fontWeight,
+ ) {
+ return baseStyle.copyWith(fontWeight: fontWeight);
+ }
+
+ /// Create a text style with opacity
+ static TextStyle withOpacity(
+ TextStyle baseStyle,
+ double opacity,
+ ) {
+ final color = baseStyle.color ?? Colors.black;
+ return baseStyle.copyWith(
+ color: color.withOpacity(opacity),
+ );
+ }
+
+ /// Create a text style with letter spacing override
+ static TextStyle withLetterSpacing(
+ TextStyle baseStyle,
+ double letterSpacing,
+ ) {
+ return baseStyle.copyWith(letterSpacing: letterSpacing);
+ }
+
+ /// Create a text style with line height override
+ static TextStyle withLineHeight(
+ TextStyle baseStyle,
+ double height,
+ ) {
+ return baseStyle.copyWith(height: height);
+ }
+
+ /// Create a monospace version of a text style
+ static TextStyle toMonospace(TextStyle baseStyle) {
+ return baseStyle.copyWith(
+ fontFamily: fontFamilyMono,
+ );
+ }
+
+ /// Create an italic version of a text style
+ static TextStyle toItalic(TextStyle baseStyle) {
+ return baseStyle.copyWith(
+ fontStyle: FontStyle.italic,
+ );
+ }
+
+ /// Create a strikethrough version of a text style
+ static TextStyle withStrikethrough(TextStyle baseStyle) {
+ return baseStyle.copyWith(
+ decoration: TextDecoration.lineThrough,
+ );
+ }
+
+ /// Create an underlined version of a text style
+ static TextStyle withUnderline(TextStyle baseStyle) {
+ return baseStyle.copyWith(
+ decoration: TextDecoration.underline,
+ );
+ }
+
+ /// Create a text style with multiple modifications
+ static TextStyle merge(
+ TextStyle baseStyle, {
+ Color? color,
+ double? fontSize,
+ FontWeight? fontWeight,
+ double? letterSpacing,
+ double? height,
+ String? fontFamily,
+ FontStyle? fontStyle,
+ TextDecoration? decoration,
+ }) {
+ return baseStyle.copyWith(
+ color: color,
+ fontSize: fontSize,
+ fontWeight: fontWeight,
+ letterSpacing: letterSpacing,
+ height: height,
+ fontFamily: fontFamily,
+ fontStyle: fontStyle,
+ decoration: decoration,
+ );
+ }
+}