Implement collapsible sidebar with badge-only view

Add collapsible sidebar functionality for both deliveries and routes pages:

- DeliveryListItem: Add isCollapsed parameter to show badge-only view when sidebar is collapsed
- RouteListItem: Add isCollapsed parameter with same badge-only behavior
- MapSidebarLayout: Add sidebarBuilder function to pass collapsed state to child widgets
- CollapsibleRoutesSidebar: Pass collapsed state to RouteListItem components
- UnifiedDeliveryListView: Add isCollapsed parameter and pass to DeliveryListItem

Collapsed sidebar:
- Width: 80px (accommodates 60px badge with 10px margins)
- Shows only status-colored order number badges
- Badges remain centered and aligned during animations
- Removed horizontal slide animation in collapsed view to prevent misalignment
- Maintains scale and fade animations for smooth entrance

Expanded sidebar:
- Width: 420px (original full layout)
- Shows badge, vertical accent bar, and delivery/route details
- Full animations including horizontal slide

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Jean-Philippe Brule
2025-11-17 11:00:48 -05:00
parent 98ce195bbb
commit 65f0f4451b
33 changed files with 373 additions and 29 deletions
+57
View File
@@ -10,6 +10,7 @@ class DeliveryListItem extends StatefulWidget {
final VoidCallback? onCall;
final Function(String)? onAction;
final int? animationIndex;
final bool isCollapsed;
const DeliveryListItem({
super.key,
@@ -19,6 +20,7 @@ class DeliveryListItem extends StatefulWidget {
this.onCall,
this.onAction,
this.animationIndex,
this.isCollapsed = false,
});
@override
@@ -83,6 +85,61 @@ class _DeliveryListItemState extends State<DeliveryListItem>
final isDark = Theme.of(context).brightness == Brightness.dark;
final statusColor = _getStatusColor(widget.delivery);
// Collapsed view: Show only the badge
if (widget.isCollapsed) {
return ScaleTransition(
scale: _scaleAnimation,
child: FadeTransition(
opacity: _fadeAnimation,
child: MouseRegion(
onEnter: (_) => setState(() => _isHovered = true),
onExit: (_) => setState(() => _isHovered = false),
child: GestureDetector(
onTap: widget.onTap,
child: Container(
margin: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 10,
),
child: Center(
child: Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: statusColor,
borderRadius: BorderRadius.circular(10),
boxShadow: (_isHovered || widget.isSelected)
? [
BoxShadow(
color: Colors.black.withValues(
alpha: isDark ? 0.3 : 0.15,
),
blurRadius: 8,
offset: const Offset(0, 4),
),
]
: [],
),
child: Center(
child: Text(
'${widget.delivery.deliveryIndex + 1}',
style: const TextStyle(
color: Colors.white,
fontSize: 26,
fontWeight: FontWeight.w700,
),
),
),
),
),
),
),
),
),
);
}
// Expanded view: Show full layout
return ScaleTransition(
scale: _scaleAnimation,
child: FadeTransition(