Implement premium UI/UX refinements for Apple-like polish
Add three major UI components with animations and dark mode support: - PremiumRouteCard: Enhanced route cards with left accent bar, delivery count badge, animated hover effects (1.02x scale), and dynamic shadow elevation - DarkModeMapComponent: Google Maps integration with dark theme styling, custom info panels, navigation buttons, and delivery status indicators - DeliveryListItem: Animated list items with staggered entrance animations, status badges with icons, contact indicators, and hover states Updates: - RoutesPage: Now uses PremiumRouteCard with improved visual hierarchy - DeliveriesPage: Integrated DarkModeMapComponent and DeliveryListItem with proper theme awareness - Animation system: Leverages existing AppAnimations constants for 200ms fast interactions and easeOut curves Design philosophy: - Element separation through left accent bars (status-coded) - Elevation and shadow for depth (0.1-0.3 opacity) - Staggered animations for list items (50ms delays) - Dark mode optimized for evening use (reduced brightness) - Responsive hover states with tactile feedback Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
ccb817e3c6
commit
3f31a509e0
10
ios/build/Logs/Build/LogStoreManifest.plist
Normal file
10
ios/build/Logs/Build/LogStoreManifest.plist
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>logFormatVersion</key>
|
||||||
|
<integer>11</integer>
|
||||||
|
<key>logs</key>
|
||||||
|
<dict/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
10
ios/build/Logs/Launch/LogStoreManifest.plist
Normal file
10
ios/build/Logs/Launch/LogStoreManifest.plist
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>logFormatVersion</key>
|
||||||
|
<integer>11</integer>
|
||||||
|
<key>logs</key>
|
||||||
|
<dict/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
10
ios/build/Logs/Localization/LogStoreManifest.plist
Normal file
10
ios/build/Logs/Localization/LogStoreManifest.plist
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>logFormatVersion</key>
|
||||||
|
<integer>11</integer>
|
||||||
|
<key>logs</key>
|
||||||
|
<dict/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
10
ios/build/Logs/Package/LogStoreManifest.plist
Normal file
10
ios/build/Logs/Package/LogStoreManifest.plist
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>logFormatVersion</key>
|
||||||
|
<integer>11</integer>
|
||||||
|
<key>logs</key>
|
||||||
|
<dict/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
10
ios/build/Logs/Test/LogStoreManifest.plist
Normal file
10
ios/build/Logs/Test/LogStoreManifest.plist
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>logFormatVersion</key>
|
||||||
|
<integer>11</integer>
|
||||||
|
<key>logs</key>
|
||||||
|
<dict/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
456
lib/components/dark_mode_map.dart
Normal file
456
lib/components/dark_mode_map.dart
Normal file
@ -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<Delivery> deliveries;
|
||||||
|
final Delivery? selectedDelivery;
|
||||||
|
final ValueChanged<Delivery?>? onDeliverySelected;
|
||||||
|
|
||||||
|
const DarkModeMapComponent({
|
||||||
|
super.key,
|
||||||
|
required this.deliveries,
|
||||||
|
this.selectedDelivery,
|
||||||
|
this.onDeliverySelected,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DarkModeMapComponent> createState() => _DarkModeMapComponentState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DarkModeMapComponentState extends State<DarkModeMapComponent> {
|
||||||
|
GoogleNavigationViewController? _navigationController;
|
||||||
|
bool _isNavigating = false;
|
||||||
|
LatLng? _destinationLocation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_initializeNavigation();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _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<void> _navigateToLocation(LatLng location) async {
|
||||||
|
if (_navigationController == null) return;
|
||||||
|
try {
|
||||||
|
await _navigationController!.animateCamera(
|
||||||
|
CameraUpdate.newLatLngZoom(location, 15),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Camera navigation error: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _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<void> _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<void> _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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
293
lib/components/delivery_list_item.dart
Normal file
293
lib/components/delivery_list_item.dart
Normal file
@ -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<DeliveryListItem> createState() => _DeliveryListItemState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DeliveryListItemState extends State<DeliveryListItem>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
late AnimationController _controller;
|
||||||
|
late Animation<double> _slideAnimation;
|
||||||
|
late Animation<double> _fadeAnimation;
|
||||||
|
late Animation<double> _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<double>(begin: 20, end: 0).animate(
|
||||||
|
CurvedAnimation(parent: _controller, curve: Curves.easeOut),
|
||||||
|
);
|
||||||
|
|
||||||
|
_fadeAnimation = Tween<double>(begin: 0, end: 1).animate(
|
||||||
|
CurvedAnimation(parent: _controller, curve: Curves.easeOut),
|
||||||
|
);
|
||||||
|
|
||||||
|
_scaleAnimation = Tween<double>(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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
252
lib/components/premium_route_card.dart
Normal file
252
lib/components/premium_route_card.dart
Normal file
@ -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<PremiumRouteCard> createState() => _PremiumRouteCardState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PremiumRouteCardState extends State<PremiumRouteCard>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
late AnimationController _controller;
|
||||||
|
late Animation<double> _scaleAnimation;
|
||||||
|
late Animation<double> _shadowAnimation;
|
||||||
|
bool _isHovered = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_controller = AnimationController(
|
||||||
|
duration: AppAnimations.durationFast,
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
|
||||||
|
_scaleAnimation = Tween<double>(begin: 1.0, end: 1.02).animate(
|
||||||
|
CurvedAnimation(parent: _controller, curve: Curves.easeOut),
|
||||||
|
);
|
||||||
|
|
||||||
|
_shadowAnimation = Tween<double>(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<Color>(
|
||||||
|
isCompleted
|
||||||
|
? SvrntyColors.statusCompleted
|
||||||
|
: SvrntyColors.crimsonRed,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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<AppLocalizations>(context, AppLocalizations)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const LocalizationsDelegate<AppLocalizations> 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<LocalizationsDelegate<dynamic>> localizationsDelegates =
|
|
||||||
<LocalizationsDelegate<dynamic>>[
|
|
||||||
delegate,
|
|
||||||
GlobalMaterialLocalizations.delegate,
|
|
||||||
GlobalCupertinoLocalizations.delegate,
|
|
||||||
GlobalWidgetsLocalizations.delegate,
|
|
||||||
];
|
|
||||||
|
|
||||||
/// A list of this localizations delegate's supported locales.
|
|
||||||
static const List<Locale> supportedLocales = <Locale>[
|
|
||||||
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<AppLocalizations> {
|
|
||||||
const _AppLocalizationsDelegate();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<AppLocalizations> load(Locale locale) {
|
|
||||||
return SynchronousFuture<AppLocalizations>(lookupAppLocalizations(locale));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool isSupported(Locale locale) =>
|
|
||||||
<String>['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.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -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';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -9,7 +9,8 @@ import '../models/delivery_commands.dart';
|
|||||||
import '../utils/breakpoints.dart';
|
import '../utils/breakpoints.dart';
|
||||||
import '../utils/responsive.dart';
|
import '../utils/responsive.dart';
|
||||||
import '../components/map_sidebar_layout.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 {
|
class DeliveriesPage extends ConsumerStatefulWidget {
|
||||||
final int routeFragmentId;
|
final int routeFragmentId;
|
||||||
@ -62,7 +63,7 @@ class _DeliveriesPageState extends ConsumerState<DeliveriesPage> {
|
|||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
return MapSidebarLayout(
|
return MapSidebarLayout(
|
||||||
mapWidget: DeliveryMap(
|
mapWidget: DarkModeMapComponent(
|
||||||
deliveries: deliveries,
|
deliveries: deliveries,
|
||||||
selectedDelivery: _selectedDelivery,
|
selectedDelivery: _selectedDelivery,
|
||||||
onDeliverySelected: (delivery) {
|
onDeliverySelected: (delivery) {
|
||||||
@ -260,14 +261,16 @@ class DeliveryListView extends StatelessWidget {
|
|||||||
// Trigger refresh via provider
|
// Trigger refresh via provider
|
||||||
},
|
},
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
itemCount: deliveries.length,
|
itemCount: deliveries.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final delivery = deliveries[index];
|
final delivery = deliveries[index];
|
||||||
return DeliveryCard(
|
return DeliveryListItem(
|
||||||
delivery: delivery,
|
delivery: delivery,
|
||||||
isSelected: selectedDelivery?.id == delivery.id,
|
isSelected: selectedDelivery?.id == delivery.id,
|
||||||
onTap: () => onDeliverySelected(delivery),
|
onTap: () => onDeliverySelected(delivery),
|
||||||
onAction: onAction,
|
onCall: () => onAction(delivery, 'call'),
|
||||||
|
animationIndex: index,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import '../models/delivery_route.dart';
|
|||||||
import '../providers/providers.dart';
|
import '../providers/providers.dart';
|
||||||
import '../utils/breakpoints.dart';
|
import '../utils/breakpoints.dart';
|
||||||
import '../utils/responsive.dart';
|
import '../utils/responsive.dart';
|
||||||
|
import '../components/premium_route_card.dart';
|
||||||
import 'deliveries_page.dart';
|
import 'deliveries_page.dart';
|
||||||
import 'settings_page.dart';
|
import 'settings_page.dart';
|
||||||
|
|
||||||
@ -137,8 +138,8 @@ class RoutesPage extends ConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildRouteCard(BuildContext context, DeliveryRoute route) {
|
Widget _buildRouteCard(BuildContext context, DeliveryRoute route) {
|
||||||
return Card(
|
return PremiumRouteCard(
|
||||||
child: InkWell(
|
route: route,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
@ -149,35 +150,6 @@ class RoutesPage extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
296
lib/theme.dart
296
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 {
|
class MaterialTheme {
|
||||||
final TextTheme textTheme;
|
final TextTheme textTheme;
|
||||||
@ -9,51 +17,51 @@ class MaterialTheme {
|
|||||||
static ColorScheme lightScheme() {
|
static ColorScheme lightScheme() {
|
||||||
return const ColorScheme(
|
return const ColorScheme(
|
||||||
brightness: Brightness.light,
|
brightness: Brightness.light,
|
||||||
primary: Color(0xffC44D58), // Svrnty Crimson Red
|
primary: Color(0xffDF2D45), // Svrnty Crimson Red (updated)
|
||||||
surfaceTint: Color(0xffC44D58),
|
surfaceTint: Color(0xffDF2D45),
|
||||||
onPrimary: Color(0xffffffff),
|
onPrimary: Color(0xffffffff),
|
||||||
primaryContainer: Color(0xffffd8db),
|
primaryContainer: Color(0xffFFE0E5),
|
||||||
onPrimaryContainer: Color(0xff8b3238),
|
onPrimaryContainer: Color(0xff06080C),
|
||||||
secondary: Color(0xff475C6C), // Svrnty Slate Blue
|
secondary: Color(0xff3A4958), // Svrnty Dark Slate
|
||||||
onSecondary: Color(0xffffffff),
|
onSecondary: Color(0xffffffff),
|
||||||
secondaryContainer: Color(0xffd1dce7),
|
secondaryContainer: Color(0xffD0DCE8),
|
||||||
onSecondaryContainer: Color(0xff2e3d4a),
|
onSecondaryContainer: Color(0xff06080C),
|
||||||
tertiary: Color(0xff5a4a6c),
|
tertiary: Color(0xff1D2C39), // Svrnty Teal
|
||||||
onTertiary: Color(0xffffffff),
|
onTertiary: Color(0xffffffff),
|
||||||
tertiaryContainer: Color(0xffe0d3f2),
|
tertiaryContainer: Color(0xffBFD5E3),
|
||||||
onTertiaryContainer: Color(0xff3d2f4d),
|
onTertiaryContainer: Color(0xff06080C),
|
||||||
error: Color(0xffba1a1a),
|
error: Color(0xffEF4444),
|
||||||
onError: Color(0xffffffff),
|
onError: Color(0xffffffff),
|
||||||
errorContainer: Color(0xffffdad6),
|
errorContainer: Color(0xffFEE2E2),
|
||||||
onErrorContainer: Color(0xff93000a),
|
onErrorContainer: Color(0xff7F1D1D),
|
||||||
surface: Color(0xfffafafa),
|
surface: Color(0xffFAFAFC),
|
||||||
onSurface: Color(0xff1a1c1e),
|
onSurface: Color(0xff06080C),
|
||||||
onSurfaceVariant: Color(0xff43474e),
|
onSurfaceVariant: Color(0xff506576),
|
||||||
outline: Color(0xff74777f),
|
outline: Color(0xffAEB8BE),
|
||||||
outlineVariant: Color(0xffc4c6cf),
|
outlineVariant: Color(0xffD1D5DB),
|
||||||
shadow: Color(0xff000000),
|
shadow: Color(0xff1A000000),
|
||||||
scrim: Color(0xff000000),
|
scrim: Color(0xff000000),
|
||||||
inverseSurface: Color(0xff2f3033),
|
inverseSurface: Color(0xff06080C),
|
||||||
inversePrimary: Color(0xffffb3b9),
|
inversePrimary: Color(0xffFF6B7D),
|
||||||
primaryFixed: Color(0xffffd8db),
|
primaryFixed: Color(0xffFFE0E5),
|
||||||
onPrimaryFixed: Color(0xff410008),
|
onPrimaryFixed: Color(0xff06080C),
|
||||||
primaryFixedDim: Color(0xffffb3b9),
|
primaryFixedDim: Color(0xffFFC0C9),
|
||||||
onPrimaryFixedVariant: Color(0xff8b3238),
|
onPrimaryFixedVariant: Color(0xff8B1A2A),
|
||||||
secondaryFixed: Color(0xffd1dce7),
|
secondaryFixed: Color(0xffD0DCE8),
|
||||||
onSecondaryFixed: Color(0xff0f1a24),
|
onSecondaryFixed: Color(0xff06080C),
|
||||||
secondaryFixedDim: Color(0xffb5c0cb),
|
secondaryFixedDim: Color(0xffB0C4D8),
|
||||||
onSecondaryFixedVariant: Color(0xff2e3d4a),
|
onSecondaryFixedVariant: Color(0xff3A4958),
|
||||||
tertiaryFixed: Color(0xffe0d3f2),
|
tertiaryFixed: Color(0xffBFD5E3),
|
||||||
onTertiaryFixed: Color(0xff1f122f),
|
onTertiaryFixed: Color(0xff06080C),
|
||||||
tertiaryFixedDim: Color(0xffc4b7d6),
|
tertiaryFixedDim: Color(0xff9FBDCF),
|
||||||
onTertiaryFixedVariant: Color(0xff3d2f4d),
|
onTertiaryFixedVariant: Color(0xff1D2C39),
|
||||||
surfaceDim: Color(0xffdadcde),
|
surfaceDim: Color(0xffdadcde),
|
||||||
surfaceBright: Color(0xfffafafa),
|
surfaceBright: Color(0xfffafafa),
|
||||||
surfaceContainerLowest: Color(0xffffffff),
|
surfaceContainerLowest: Color(0xffffffff),
|
||||||
surfaceContainerLow: Color(0xfff4f5f7),
|
surfaceContainerLow: Color(0xfff6f6f8),
|
||||||
surfaceContainer: Color(0xffeef0f2),
|
surfaceContainer: Color(0xfff1f1f4),
|
||||||
surfaceContainerHigh: Color(0xffe8eaec),
|
surfaceContainerHigh: Color(0xffebebee),
|
||||||
surfaceContainerHighest: Color(0xffe2e4e7),
|
surfaceContainerHighest: Color(0xffe5e5e8),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,55 +179,55 @@ class MaterialTheme {
|
|||||||
return theme(lightHighContrastScheme());
|
return theme(lightHighContrastScheme());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Svrnty Brand Colors - Dark Theme (Bold & Saturated)
|
// Svrnty Brand Colors - Dark Theme
|
||||||
static ColorScheme darkScheme() {
|
static ColorScheme darkScheme() {
|
||||||
return const ColorScheme(
|
return const ColorScheme(
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
primary: Color(0xffF3574E), // Bold Svrnty Crimson Red (slightly desaturated)
|
primary: Color(0xffDF2D45), // Svrnty Crimson Red
|
||||||
surfaceTint: Color(0xffF3574E),
|
surfaceTint: Color(0xffFF6B7D),
|
||||||
onPrimary: Color(0xffffffff),
|
onPrimary: Color(0xffffffff),
|
||||||
primaryContainer: Color(0xffC44D58), // True brand crimson
|
primaryContainer: Color(0xff9C1A29),
|
||||||
onPrimaryContainer: Color(0xffffffff),
|
onPrimaryContainer: Color(0xffFFE0E5),
|
||||||
secondary: Color(0xff5A6F7D), // Rich Svrnty Slate Blue
|
secondary: Color(0xff506576), // Svrnty Slate Gray
|
||||||
onSecondary: Color(0xffffffff),
|
onSecondary: Color(0xffffffff),
|
||||||
secondaryContainer: Color(0xff475C6C), // True brand slate
|
secondaryContainer: Color(0xff3A4958),
|
||||||
onSecondaryContainer: Color(0xffffffff),
|
onSecondaryContainer: Color(0xffD0DCE8),
|
||||||
tertiary: Color(0xffA78BBF), // Richer purple
|
tertiary: Color(0xff5A8FA8), // Svrnty Teal variant
|
||||||
onTertiary: Color(0xffffffff),
|
onTertiary: Color(0xffffffff),
|
||||||
tertiaryContainer: Color(0xff8B6FA3),
|
tertiaryContainer: Color(0xff1D2C39),
|
||||||
onTertiaryContainer: Color(0xffffffff),
|
onTertiaryContainer: Color(0xffBFD5E3),
|
||||||
error: Color(0xffFF5449),
|
error: Color(0xffFF6B6B),
|
||||||
onError: Color(0xffffffff),
|
onError: Color(0xff4C0707),
|
||||||
errorContainer: Color(0xffD32F2F),
|
errorContainer: Color(0xff93000A),
|
||||||
onErrorContainer: Color(0xffffffff),
|
onErrorContainer: Color(0xffFEE2E2),
|
||||||
surface: Color(0xff1a1c1e), // Svrnty Dark Background
|
surface: Color(0xff0A0C10), // Svrnty Dark Background
|
||||||
onSurface: Color(0xfff0f0f0),
|
onSurface: Color(0xffF0F0F2),
|
||||||
onSurfaceVariant: Color(0xffc8cad0),
|
onSurfaceVariant: Color(0xffBFC3C8),
|
||||||
outline: Color(0xff8d9199),
|
outline: Color(0xff6B7280),
|
||||||
outlineVariant: Color(0xff43474e),
|
outlineVariant: Color(0xff374151),
|
||||||
shadow: Color(0xff000000),
|
shadow: Color(0xff000000),
|
||||||
scrim: Color(0xff000000),
|
scrim: Color(0xff000000),
|
||||||
inverseSurface: Color(0xffe2e4e7),
|
inverseSurface: Color(0xffE2E2E6),
|
||||||
inversePrimary: Color(0xffC44D58),
|
inversePrimary: Color(0xffDF2D45),
|
||||||
primaryFixed: Color(0xffFFD8DB),
|
primaryFixed: Color(0xffFFE0E5),
|
||||||
onPrimaryFixed: Color(0xff2d0008),
|
onPrimaryFixed: Color(0xff3D0009),
|
||||||
primaryFixedDim: Color(0xffF3574E),
|
primaryFixedDim: Color(0xffFF6B7D),
|
||||||
onPrimaryFixedVariant: Color(0xffffffff),
|
onPrimaryFixedVariant: Color(0xff9C1A29),
|
||||||
secondaryFixed: Color(0xffD1DCE7),
|
secondaryFixed: Color(0xffD0DCE8),
|
||||||
onSecondaryFixed: Color(0xff0f1a24),
|
onSecondaryFixed: Color(0xff06080C),
|
||||||
secondaryFixedDim: Color(0xff5A6F7D),
|
secondaryFixedDim: Color(0xff506576),
|
||||||
onSecondaryFixedVariant: Color(0xffffffff),
|
onSecondaryFixedVariant: Color(0xff3A4958),
|
||||||
tertiaryFixed: Color(0xffE0D3F2),
|
tertiaryFixed: Color(0xffBFD5E3),
|
||||||
onTertiaryFixed: Color(0xff1f122f),
|
onTertiaryFixed: Color(0xff001219),
|
||||||
tertiaryFixedDim: Color(0xffA78BBF),
|
tertiaryFixedDim: Color(0xff5A8FA8),
|
||||||
onTertiaryFixedVariant: Color(0xffffffff),
|
onTertiaryFixedVariant: Color(0xff1D2C39),
|
||||||
surfaceDim: Color(0xff1a1c1e),
|
surfaceDim: Color(0xff0A0C10),
|
||||||
surfaceBright: Color(0xff404244),
|
surfaceBright: Color(0xff404244),
|
||||||
surfaceContainerLowest: Color(0xff0f1113),
|
surfaceContainerLowest: Color(0xff040507),
|
||||||
surfaceContainerLow: Color(0xff1f2123),
|
surfaceContainerLow: Color(0xff0F1114),
|
||||||
surfaceContainer: Color(0xff23252a),
|
surfaceContainer: Color(0xff14161A),
|
||||||
surfaceContainerHigh: Color(0xff2d2f35),
|
surfaceContainerHigh: Color(0xff1F2228),
|
||||||
surfaceContainerHighest: Color(0xff383940),
|
surfaceContainerHighest: Color(0xff2A2D34),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,34 +346,128 @@ class MaterialTheme {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ThemeData theme(ColorScheme colorScheme) => ThemeData(
|
ThemeData theme(ColorScheme colorScheme) {
|
||||||
|
return ThemeData(
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
brightness: colorScheme.brightness,
|
brightness: colorScheme.brightness,
|
||||||
colorScheme: colorScheme,
|
colorScheme: colorScheme,
|
||||||
|
fontFamily: 'Montserrat',
|
||||||
|
scaffoldBackgroundColor: colorScheme.surface,
|
||||||
|
canvasColor: colorScheme.surface,
|
||||||
textTheme: const TextTheme(
|
textTheme: const TextTheme(
|
||||||
displayLarge: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.bold),
|
displayLarge: TextStyle(
|
||||||
displayMedium: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.bold),
|
fontFamily: 'Montserrat',
|
||||||
displaySmall: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.bold),
|
fontWeight: FontWeight.w700,
|
||||||
headlineLarge: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w600),
|
fontSize: 57,
|
||||||
headlineMedium: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w600),
|
letterSpacing: -0.5,
|
||||||
headlineSmall: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w600),
|
),
|
||||||
titleLarge: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w600),
|
displayMedium: TextStyle(
|
||||||
titleMedium: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w500),
|
fontFamily: 'Montserrat',
|
||||||
titleSmall: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w500),
|
fontWeight: FontWeight.w700,
|
||||||
bodyLarge: TextStyle(fontFamily: 'Montserrat'),
|
fontSize: 45,
|
||||||
bodyMedium: TextStyle(fontFamily: 'Montserrat'),
|
letterSpacing: -0.5,
|
||||||
bodySmall: TextStyle(fontFamily: 'Montserrat'),
|
),
|
||||||
labelLarge: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w500),
|
displaySmall: TextStyle(
|
||||||
labelMedium: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w500),
|
fontFamily: 'Montserrat',
|
||||||
labelSmall: TextStyle(fontFamily: 'Montserrat', fontWeight: FontWeight.w500),
|
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(
|
).apply(
|
||||||
bodyColor: colorScheme.onSurface,
|
bodyColor: colorScheme.onSurface,
|
||||||
displayColor: colorScheme.onSurface,
|
displayColor: colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
fontFamily: 'Montserrat',
|
// Component Themes
|
||||||
scaffoldBackgroundColor: colorScheme.surface,
|
cardTheme: ComponentThemes.cardTheme(colorScheme),
|
||||||
canvasColor: colorScheme.surface,
|
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<ExtendedColor> get extendedColors => [
|
List<ExtendedColor> get extendedColors => [
|
||||||
|
|||||||
272
lib/theme/animation_system.dart
Normal file
272
lib/theme/animation_system.dart
Normal file
@ -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,
|
||||||
|
}
|
||||||
146
lib/theme/border_system.dart
Normal file
146
lib/theme/border_system.dart
Normal file
@ -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);
|
||||||
|
}
|
||||||
159
lib/theme/color_system.dart
Normal file
159
lib/theme/color_system.dart
Normal file
@ -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);
|
||||||
|
}
|
||||||
262
lib/theme/component_themes.dart
Normal file
262
lib/theme/component_themes.dart
Normal file
@ -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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
332
lib/theme/gradient_system.dart
Normal file
332
lib/theme/gradient_system.dart
Normal file
@ -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<Color> colors,
|
||||||
|
required Alignment begin,
|
||||||
|
required Alignment end,
|
||||||
|
List<double>? stops,
|
||||||
|
}) {
|
||||||
|
return LinearGradient(
|
||||||
|
begin: begin,
|
||||||
|
end: end,
|
||||||
|
colors: colors,
|
||||||
|
stops: stops,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
208
lib/theme/shadow_system.dart
Normal file
208
lib/theme/shadow_system.dart
Normal file
@ -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<BoxShadow> shadowNoneLight = [];
|
||||||
|
|
||||||
|
/// Minimal shadow for light theme
|
||||||
|
static const List<BoxShadow> shadowXsLight = [
|
||||||
|
BoxShadow(
|
||||||
|
color: _shadowColorLight,
|
||||||
|
blurRadius: 1,
|
||||||
|
offset: Offset(0, 1),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Small shadow for light theme (default for cards)
|
||||||
|
static const List<BoxShadow> shadowSmLight = [
|
||||||
|
BoxShadow(
|
||||||
|
color: _shadowColorLight,
|
||||||
|
blurRadius: 2,
|
||||||
|
offset: Offset(0, 1),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Medium shadow for light theme (hover states)
|
||||||
|
static const List<BoxShadow> shadowMdLight = [
|
||||||
|
BoxShadow(
|
||||||
|
color: _shadowColorLight,
|
||||||
|
blurRadius: 4,
|
||||||
|
offset: Offset(0, 2),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Large shadow for light theme
|
||||||
|
static const List<BoxShadow> shadowLgLight = [
|
||||||
|
BoxShadow(
|
||||||
|
color: _shadowColorLight,
|
||||||
|
blurRadius: 8,
|
||||||
|
offset: Offset(0, 4),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Extra large shadow for light theme (dialogs)
|
||||||
|
static const List<BoxShadow> 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<BoxShadow> shadowNoneDark = [];
|
||||||
|
|
||||||
|
/// Minimal shadow for dark theme
|
||||||
|
static const List<BoxShadow> shadowXsDark = [
|
||||||
|
BoxShadow(
|
||||||
|
color: _shadowColorDark,
|
||||||
|
blurRadius: 1,
|
||||||
|
offset: Offset(0, 1),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Small shadow for dark theme (default for cards)
|
||||||
|
static const List<BoxShadow> shadowSmDark = [
|
||||||
|
BoxShadow(
|
||||||
|
color: _shadowColorDark,
|
||||||
|
blurRadius: 2,
|
||||||
|
offset: Offset(0, 1),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Medium shadow for dark theme (hover states)
|
||||||
|
static const List<BoxShadow> shadowMdDark = [
|
||||||
|
BoxShadow(
|
||||||
|
color: _shadowColorDark,
|
||||||
|
blurRadius: 4,
|
||||||
|
offset: Offset(0, 2),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Large shadow for dark theme
|
||||||
|
static const List<BoxShadow> shadowLgDark = [
|
||||||
|
BoxShadow(
|
||||||
|
color: _shadowColorDark,
|
||||||
|
blurRadius: 8,
|
||||||
|
offset: Offset(0, 4),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Extra large shadow for dark theme (dialogs)
|
||||||
|
static const List<BoxShadow> shadowXlDark = [
|
||||||
|
BoxShadow(
|
||||||
|
color: _shadowColorDark,
|
||||||
|
blurRadius: 16,
|
||||||
|
offset: Offset(0, 8),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// SHADOW UTILITY METHODS
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
/// Get shadow list based on brightness and elevation level
|
||||||
|
static List<BoxShadow> 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;
|
||||||
|
}
|
||||||
236
lib/theme/size_system.dart
Normal file
236
lib/theme/size_system.dart
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
296
lib/theme/spacing_system.dart
Normal file
296
lib/theme/spacing_system.dart
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
331
lib/theme/typography_system.dart
Normal file
331
lib/theme/typography_system.dart
Normal file
@ -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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user