skills/flutterflow-designer/screen-blueprints.md

687 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ZIVVO Screen Blueprints
Screen-by-screen widget trees and build guides for all 4 roles. Each blueprint shows the exact widget hierarchy and key properties to configure in FlutterFlow.
## How to Use These Blueprints
1. Create a new page in FlutterFlow
2. Follow the widget tree top-down — add outermost widget first
3. Indented items are children of the item above
4. Properties in parentheses are the key values to set
5. After building, preview on target device size (390x844 for mobile)
---
## Shared Screens (All Roles)
### Splash Screen
**Page settings:** No AppBar, no bottom nav, full screen, no scroll.
```
Scaffold (backgroundColor: Background #FAFAF7)
└── Stack
├── Container (width: Expand, height: Expand)
│ └── Gradient background: Sunrise (Primary Light 8% → transparent)
├── Center
│ └── Column (mainAxisAlignment: Center, crossAxisSize: Min)
│ ├── Image (src: ZIVVO logo asset, width: 120, height: 120)
│ ├── SizedBox (height: 16)
│ └── Text ("ZIVVO", style: displayLarge, color: Primary #D4A012)
└── Positioned (bottom: 48, left: 0, right: 0)
└── Center
└── CircularProgressIndicator (color: Secondary #0D8B7D, strokeWidth: 3)
```
**Build steps:**
```
1. Create page "SplashScreen" → set as home page
2. Disable AppBar and bottom nav in page settings
3. Add Stack as root widget
4. Inside Stack: add Container → set Expand width/height → add gradient
5. Inside Stack: add Center → Column → Image + SizedBox + Text
6. Inside Stack: add Align (bottom center) → CircularProgressIndicator
7. Actions → On Load: wait 2s then navigate to OnboardingPage
```
### Onboarding (3 Pages)
**Page settings:** No AppBar, no bottom nav, full screen.
```
Scaffold
└── Column (mainAxisAlignment: SpaceBetween)
├── Expanded
│ └── PageView (controller: pageController, pageSnapping: true)
│ ├── Page 1: "Move Freely"
│ │ └── Column (mainAxisAlignment: Center, padding: 32)
│ │ ├── Image (src: city illustration, width: 280, height: 280, fit: Contain)
│ │ ├── SizedBox (height: 32)
│ │ ├── Text ("Move Freely", style: headlineLarge, textAlign: Center)
│ │ ├── SizedBox (height: 12)
│ │ └── Text ("Rides, rentals, and deliveries — all in one app.",
│ │ style: bodyLarge, color: TextSecondary, textAlign: Center)
│ ├── Page 2: "Works Everywhere"
│ │ └── Column (same structure)
│ │ ├── Image (signal bars illustration)
│ │ ├── Text ("Works Everywhere")
│ │ └── Text ("Offline maps, low-bandwidth tracking, 2G-ready.")
│ └── Page 3: "You're in Control"
│ └── Column (same structure)
│ ├── Image (tracking screen illustration)
│ ├── Text ("You're in Control")
│ └── Text ("Real-time tracking, transparent pricing, SOS safety.")
├── Container (padding: horizontal 24, vertical 16)
│ └── Column
│ ├── Row (mainAxisAlignment: Center, spacing: 8)
│ │ ├── Container (w: 8, h: 8, radius: 4, color: conditional on page index)
│ │ ├── Container (same — dot indicator)
│ │ └── Container (same — dot indicator)
│ ├── SizedBox (height: 24)
│ └── Row (mainAxisAlignment: SpaceBetween)
│ ├── ZivvoButtonGhost (text: "Skip", onTap: navigate to PhoneEntry)
│ └── ZivvoButtonPrimary (text: conditional "Next"/"Get Started",
│ width: 160, onTap: next page or navigate to PhoneEntry)
```
### Phone Entry Screen
```
Scaffold (AppBar: back button only, transparent, no title)
└── Column (padding: horizontal 24)
├── SizedBox (height: 32)
├── Text ("Enter your phone number", style: headlineLarge)
├── SizedBox (height: 8)
├── Text ("We'll send you a verification code",
│ style: bodyMedium, color: TextSecondary)
├── SizedBox (height: 32)
├── Row (spacing: 12)
│ ├── Container (width: 88, height: 52, radius: 12, border: 1.5px #E8E4DC)
│ │ └── Center
│ │ └── Row (mainAxisAlignment: Center, spacing: 4)
│ │ ├── Text ("🇨🇲", style: bodyLarge)
│ │ └── Text ("+237", style: bodyLarge, weight: 600)
│ └── Expanded
│ └── ZivvoInput (hintText: "6XX XXX XXX", keyboardType: Phone,
│ maxLength: 9, autofocus: true)
├── Spacer
└── Container (padding: bottom 32)
└── ZivvoButtonPrimary (text: "Continue", width: Expand,
enabled: phoneField.length == 9,
onTap: send OTP → navigate to OTPVerification)
```
### OTP Verification Screen
```
Scaffold (AppBar: back button, transparent)
└── Column (padding: horizontal 24)
├── SizedBox (height: 32)
├── Text ("Verify your number", style: headlineLarge)
├── SizedBox (height: 8)
├── RichText
│ ├── TextSpan ("We sent a code to ", style: bodyMedium, color: TextSecondary)
│ └── TextSpan ("+237 6XX XXX XXX", style: bodyMedium, weight: 600)
├── SizedBox (height: 40)
├── PinCode (length: 6, autoFocus: true, keyboardType: Number,
│ fieldWidth: 48, fieldHeight: 56, borderRadius: 12,
│ activeBorderColor: Primary, selectedBorderColor: Secondary)
├── SizedBox (height: 24)
├── Row (mainAxisAlignment: Center)
│ ├── Text ("Didn't receive code? ", style: bodySmall, color: TextSecondary)
│ └── TextButton ("Resend", style: labelLarge, color: Secondary,
│ enabled: resendTimer == 0, onTap: resend OTP)
├── Spacer
└── Container (padding: bottom 32)
└── ZivvoButtonPrimary (text: "Verify", width: Expand,
enabled: pinCode.length == 6,
onTap: verify OTP → navigate based on registration state)
```
### Settings Screen
```
Scaffold (AppBar: title "Settings", back button)
└── ListView (padding: vertical 8)
├── ListTile (leading: CircleImage(user photo), title: user name,
│ subtitle: phone number, trailing: chevron, onTap: Profile)
├── Divider (indent: 16, endIndent: 16)
├── SectionHeader ("Preferences")
├── ListTile (leading: Icon(language), title: "Language",
│ trailing: Text(current lang), onTap: LanguagePicker)
├── ListTile (leading: Icon(dark_mode), title: "Dark Mode",
│ trailing: Switch(value: isDarkMode))
├── ListTile (leading: Icon(notifications), title: "Notifications",
│ trailing: chevron, onTap: NotificationSettings)
├── Divider
├── SectionHeader ("Account")
├── ListTile (leading: Icon(payment), title: "Payment Methods",
│ trailing: chevron, onTap: PaymentMethods)
├── ListTile (leading: Icon(history), title: "Ride History",
│ trailing: chevron, onTap: RideHistory)
├── ListTile (leading: Icon(help), title: "Help & Support",
│ trailing: chevron, onTap: HelpSupport)
├── ListTile (leading: Icon(info), title: "About ZIVVO",
│ trailing: chevron, onTap: About)
├── Divider
└── ListTile (leading: Icon(logout, color: Error), title: "Sign Out",
titleColor: Error, onTap: confirm → sign out)
```
---
## Customer Role Screens
### Customer Home (Map + Where To)
```
Scaffold (extendBody: true, no AppBar)
└── Stack
├── FlutterMap (width: Expand, height: Expand)
│ ├── TileLayer (urlTemplate: vector tile URL)
│ ├── MarkerLayer — user location
│ │ └── Marker (point: userLatLng)
│ │ └── Container (w: 20, h: 20, radius: 10, color: Secondary,
│ │ border: 3px white, shadow: teal glow)
│ └── MarkerLayer — nearby drivers
│ └── [Dynamic markers from driver positions]
│ └── Image (src: car_icon, width: 32, height: 32, rotation: heading)
├── Positioned (top: safeAreaTop + 8, left: 16, right: 16)
│ └── Row (mainAxisAlignment: SpaceBetween)
│ ├── Container (w: 44, h: 44, radius: 22, color: white, shadow: sm)
│ │ └── IconButton (icon: menu, onTap: open drawer)
│ └── Container (w: 44, h: 44, radius: 22, color: white, shadow: sm)
│ └── Stack
│ ├── IconButton (icon: notifications_outlined, onTap: Notifications)
│ └── Positioned (top: 0, right: 0)
│ └── Badge (count: unreadCount, color: Error)
├── DraggableScrollableSheet (initialChildSize: 0.25, minChildSize: 0.15,
│ maxChildSize: 0.85)
│ └── Container (radius: topLeft 20, topRight 20, color: Surface, shadow: xl)
│ └── Column
│ ├── Center
│ │ └── Container (w: 40, h: 4, radius: 2, color: Divider, margin: top 8)
│ ├── SizedBox (height: 16)
│ ├── Container (margin: horizontal 16, padding: 0)
│ │ └── Row
│ │ ├── Container (w: 3, h: 48, radius: 1.5, color: Primary)
│ │ └── Expanded
│ │ └── ZivvoInput (hintText: "Where to?",
│ │ prefixIcon: search, readOnly: true,
│ │ onTap: navigate to DestinationSearch)
│ ├── SizedBox (height: 16)
│ ├── Container (height: 40, padding: horizontal 16)
│ │ └── ListView (direction: Horizontal, spacing: 8)
│ │ ├── ZivvoChip (icon: home, text: "Home", onTap: set destination)
│ │ ├── ZivvoChip (icon: work, text: "Work", onTap: set destination)
│ │ └── ZivvoChip (icon: bookmark, text: "Saved", onTap: SavedPlaces)
│ └── SizedBox (height: 16)
```
### Destination Search Screen
```
Scaffold (AppBar: none)
└── Column
├── Container (padding: 16, color: Surface)
│ └── Column (spacing: 12)
│ ├── Row
│ │ ├── IconButton (icon: arrow_back, onTap: pop)
│ │ └── Expanded
│ │ └── Text ("Plan your trip", style: headingMedium)
│ ├── Row (spacing: 12)
│ │ ├── Column (width: 24, spacing: 4)
│ │ │ ├── Container (w: 10, h: 10, radius: 5, color: Primary)
│ │ │ ├── Container (w: 2, h: 24, color: Divider)
│ │ │ └── Container (w: 10, h: 10, radius: 2, color: Error)
│ │ └── Expanded
│ │ └── Column (spacing: 8)
│ │ ├── ZivvoInput (hintText: "Pickup location", value: currentAddress)
│ │ └── ZivvoInput (hintText: "Where to?", autofocus: true)
├── Expanded
│ └── ListView
│ ├── SectionHeader ("Recent")
│ ├── [RecentPlaceRow] × N
│ │ └── ListTile (leading: Icon(history), title: place name,
│ │ subtitle: address, onTap: select destination)
│ ├── SectionHeader ("Saved Places")
│ └── [SavedPlaceRow] × N
│ └── ListTile (leading: Icon(star), title: place name,
│ subtitle: address, onTap: select destination)
```
### Vehicle Selection & Fare Estimate
```
Scaffold (AppBar: back button, "Choose a ride")
└── Column
├── Container (height: 200)
│ └── FlutterMap (showing route polyline, pickup/dropoff markers)
├── Expanded
│ └── ListView (padding: 16, spacing: 8)
│ ├── VehicleOption (selected state)
│ │ └── ZivvoCard (border: 2px Primary when selected)
│ │ └── Row (spacing: 12)
│ │ ├── Image (src: economy_car, width: 64, height: 40, fit: Contain)
│ │ ├── Expanded
│ │ │ └── Column (crossAxisAlignment: Start)
│ │ │ ├── Row
│ │ │ │ ├── Text ("Economy", style: titleLarge)
│ │ │ │ └── Badge ("Popular", color: Primary Wash)
│ │ │ └── Text ("4 min away • 4 seats", style: bodySmall, color: TextSecondary)
│ │ └── Column (crossAxisAlignment: End)
│ │ ├── Text ("2,500 XAF", style: titleLarge, weight: 700)
│ │ └── Text ("~15 min", style: bodySmall, color: TextSecondary)
│ ├── VehicleOption (Comfort)
│ │ └── ... (same structure, different image/price/time)
│ ├── VehicleOption (Premium)
│ │ └── ... (same structure)
│ └── VehicleOption (Moto)
│ └── ... (same structure, motorcycle image, 1 seat)
├── Container (padding: 16, color: Surface, shadow: xl)
│ └── Column (spacing: 12)
│ ├── Row (mainAxisAlignment: SpaceBetween)
│ │ ├── Row (spacing: 8)
│ │ │ ├── Icon (payment, color: Primary)
│ │ │ └── Text ("MTN MoMo", style: bodyMedium)
│ │ └── TextButton ("Change", color: Secondary)
│ └── ZivvoButtonPrimary (text: "Book Economy • 2,500 XAF",
│ width: Expand, onTap: request ride)
```
### Active Ride Tracking
```
Scaffold (extendBody: true, no AppBar)
└── Stack
├── FlutterMap (width: Expand, height: Expand)
│ ├── TileLayer
│ ├── PolylineLayer
│ │ └── Polyline (points: routeCoords, color: Secondary, strokeWidth: 5)
│ ├── MarkerLayer
│ │ ├── Marker (pickup: gold pin icon)
│ │ └── Marker (dropoff: red pin icon)
│ └── MarkerLayer (animated)
│ └── Marker (driver: car icon, rotated to heading, animated position)
├── Positioned (top: safeAreaTop + 8, left: 16, right: 16)
│ └── ZivvoCard
│ └── Row (mainAxisAlignment: SpaceBetween)
│ ├── Column (crossAxisAlignment: Start)
│ │ ├── Text ("Arriving in", style: bodySmall, color: TextSecondary)
│ │ └── Text ("4 min", style: headingLarge, color: Secondary)
│ └── Column (crossAxisAlignment: End)
│ ├── Text ("Distance", style: bodySmall, color: TextSecondary)
│ └── Text ("1.2 km", style: headingMedium)
├── DraggableScrollableSheet (initialChildSize: 0.3, minChildSize: 0.15)
│ └── Container (radius: top 20, color: Surface, shadow: xl)
│ └── Column
│ ├── Handle bar (same as home screen)
│ ├── Container (padding: 16)
│ │ └── Row (spacing: 12)
│ │ ├── ZivvoAvatar (src: driverPhoto, size: 48)
│ │ ├── Expanded
│ │ │ └── Column (crossAxisAlignment: Start)
│ │ │ ├── Text (driverName, style: titleLarge)
│ │ │ └── Row (spacing: 4)
│ │ │ ├── Icon (star, color: Primary, size: 16)
│ │ │ └── Text ("4.8", style: bodySmall)
│ │ └── Column
│ │ ├── Text (vehicleMake, style: bodySmall, color: TextSecondary)
│ │ └── Text (licensePlate, style: labelLarge)
│ ├── Divider
│ ├── Container (padding: horizontal 16, vertical 8)
│ │ └── Row (mainAxisAlignment: SpaceEvenly)
│ │ ├── IconButton (icon: phone, label: "Call", onTap: call driver)
│ │ ├── IconButton (icon: message, label: "Chat", onTap: open chat)
│ │ └── IconButton (icon: share, label: "Share", onTap: share tracking link)
│ ├── Divider
│ ├── Container (padding: 16)
│ │ └── Text ("Driver is on the way to pick you up",
│ │ style: bodyMedium, color: TextSecondary, textAlign: Center)
│ └── Container (padding: 16)
│ └── ZivvoButtonDanger (text: "SOS Emergency",
│ width: Expand, icon: warning,
│ onTap: hold 3s → trigger SOS)
```
### Ride Complete & Rating
```
Scaffold (AppBar: none)
└── Column (padding: 24)
├── SizedBox (height: 48)
├── Icon (check_circle, color: Success, size: 64)
├── SizedBox (height: 16)
├── Text ("Trip Complete!", style: headlineLarge, textAlign: Center)
├── SizedBox (height: 32)
├── ZivvoCard
│ └── Column (spacing: 12)
│ ├── Row (mainAxisAlignment: SpaceBetween)
│ │ ├── Text ("Total Fare", style: bodyMedium, color: TextSecondary)
│ │ └── Text ("2,500 XAF", style: headingMedium, weight: 700)
│ ├── Divider
│ ├── Row (mainAxisAlignment: SpaceBetween)
│ │ ├── Text ("Distance", style: bodySmall, color: TextSecondary)
│ │ └── Text ("4.2 km", style: bodyMedium)
│ ├── Row (mainAxisAlignment: SpaceBetween)
│ │ ├── Text ("Duration", style: bodySmall, color: TextSecondary)
│ │ └── Text ("15 min", style: bodyMedium)
│ └── Row (mainAxisAlignment: SpaceBetween)
│ ├── Text ("Payment", style: bodySmall, color: TextSecondary)
│ └── Text ("MTN MoMo", style: bodyMedium)
├── SizedBox (height: 32)
├── Text ("Rate your driver", style: headingMedium, textAlign: Center)
├── SizedBox (height: 16)
├── Row (mainAxisAlignment: Center, spacing: 8)
│ ├── ZivvoAvatar (src: driverPhoto, size: 56)
│ └── Text (driverName, style: titleLarge)
├── SizedBox (height: 12)
├── RatingBar (initialRating: 5, itemCount: 5, itemSize: 40,
│ activeColor: Primary, inactiveColor: Divider)
├── SizedBox (height: 16)
├── ZivvoInput (hintText: "Add a comment (optional)", maxLines: 3)
├── Spacer
└── ZivvoButtonPrimary (text: "Done", width: Expand, onTap: submit → navigate home)
```
---
## Driver Role Screens
### Driver Home
```
Scaffold (extendBody: true, no AppBar)
└── Stack
├── FlutterMap (width: Expand, height: Expand)
│ └── TileLayer + optional demand heatmap overlay
├── Positioned (top: safeAreaTop + 8, left: 16, right: 16)
│ └── ZivvoCard
│ └── Row (mainAxisAlignment: SpaceBetween)
│ ├── Row (spacing: 12)
│ │ ├── ZivvoAvatar (src: driverSelfie, size: 40)
│ │ └── Column (crossAxisAlignment: Start)
│ │ ├── Text (driverName, style: titleLarge)
│ │ └── Row (spacing: 4)
│ │ ├── Icon (star, color: Primary, size: 14)
│ │ ├── Text ("4.8", style: bodySmall)
│ │ └── Badge ("Gold", color: Primary Wash, textColor: Primary)
│ └── Switch (value: isOnline, activeColor: Primary,
│ onChanged: toggle availability)
├── Positioned (bottom: 16, left: 16, right: 16)
│ └── ZivvoCard
│ └── Column (spacing: 8)
│ ├── Row (mainAxisAlignment: SpaceEvenly)
│ │ ├── Column (crossAxisAlignment: Center)
│ │ │ ├── Text ("12,500 XAF", style: headingMedium, color: Primary)
│ │ │ └── Text ("Today's earnings", style: bodySmall, color: TextSecondary)
│ │ ├── Container (w: 1, h: 40, color: Divider)
│ │ └── Column (crossAxisAlignment: Center)
│ │ ├── Text ("7", style: headingMedium)
│ │ └── Text ("Trips", style: bodySmall, color: TextSecondary)
│ └── Text (isOnline ? "You're online" : "You're offline",
│ style: bodyMedium, color: isOnline ? Secondary : TextSecondary,
│ textAlign: Center)
```
### Ride Request Card (Incoming)
```
AnimatedContainer (slide up from bottom, duration: 500ms)
└── ZivvoCardElevated (margin: 16)
└── Column (spacing: 12)
├── Row (spacing: 12)
│ ├── ZivvoAvatar (src: customerPhoto, size: 48)
│ ├── Expanded
│ │ └── Column (crossAxisAlignment: Start)
│ │ ├── Text (customerName, style: titleLarge)
│ │ └── Row (spacing: 4)
│ │ ├── Icon (star, color: Primary, size: 14)
│ │ └── Text ("4.6 • 23 rides", style: bodySmall, color: TextSecondary)
│ └── Text ("2,500 XAF", style: headingMedium, weight: 700, color: Primary)
├── Divider
├── Row (spacing: 8)
│ ├── Icon (circle, color: Primary, size: 12)
│ ├── Expanded
│ │ └── Text (pickupAddress, style: bodyMedium, maxLines: 1, overflow: Ellipsis)
│ └── Text ("1.2 km", style: bodySmall, color: TextSecondary)
├── Row (spacing: 8)
│ ├── Icon (location_on, color: Error, size: 12)
│ └── Expanded
│ └── Text (dropoffAddress, style: bodyMedium, maxLines: 1, overflow: Ellipsis)
├── SizedBox (height: 4)
├── Center
│ └── Stack
│ ├── CircularProgressIndicator (value: countdown/30, color: Secondary,
│ │ strokeWidth: 4, size: 56)
│ └── Text (countdownSeconds, style: headingMedium)
├── SizedBox (height: 4)
└── Row (spacing: 12)
├── Expanded
│ └── ZivvoButtonGhost (text: "Decline", onTap: decline request)
└── Expanded
└── ZivvoButtonPrimary (text: "Accept", onTap: accept request)
```
### Driver Navigation (Active Trip)
```
Scaffold (extendBody: true, no AppBar)
└── Stack
├── FlutterMap (full screen)
│ ├── PolylineLayer (route to pickup/dropoff, color: Secondary, width: 5)
│ ├── MarkerLayer (pickup pin, dropoff pin)
│ └── MarkerLayer (driver position, animated)
├── Positioned (top: safeAreaTop, left: 0, right: 0)
│ └── Container (color: Secondary, padding: 16)
│ └── Column
│ ├── Text ("Head to pickup", style: bodyMedium, color: white)
│ ├── Text ("Turn right on Rue de la Joie", style: headingMedium, color: white)
│ └── Row (spacing: 16)
│ ├── Text ("1.2 km", style: headingLarge, color: white)
│ └── Text ("4 min", style: headingLarge, color: white)
├── Positioned (bottom: 0, left: 0, right: 0)
│ └── ZivvoCard (radiusTopOnly: 20)
│ └── Column (padding: 16, spacing: 12)
│ ├── Row (spacing: 12)
│ │ ├── ZivvoAvatar (src: customerPhoto, size: 40)
│ │ ├── Expanded → Column
│ │ │ ├── Text (customerName, style: titleLarge)
│ │ │ └── Text (pickupAddress, style: bodySmall, color: TextSecondary)
│ │ ├── IconButton (icon: phone, onTap: call customer)
│ │ └── IconButton (icon: message, onTap: open chat)
│ └── ZivvoButtonPrimary (text: "Arrived at Pickup",
│ width: Expand, onTap: update trip status)
```
---
## Dealer Role Screens
### Dealer Dashboard
```
Scaffold (AppBar: title "Dashboard", actions: [notifications, settings])
└── ListView (padding: 16, spacing: 16)
├── ZivvoCard (gradient: Sunrise 5%)
│ └── Column (spacing: 8)
│ ├── Text ("Welcome back, Jean", style: headingMedium)
│ ├── Text ("Here's your fleet overview", style: bodyMedium, color: TextSecondary)
│ └── Row (mainAxisAlignment: SpaceEvenly, spacing: 12)
│ ├── StatColumn (value: "24", label: "Vehicles", icon: directions_car)
│ ├── StatColumn (value: "18", label: "Active", icon: check_circle, color: Success)
│ ├── StatColumn (value: "3", label: "Rented", icon: key, color: Secondary)
│ └── StatColumn (value: "3", label: "Idle", icon: pause_circle, color: Warning)
├── ZivvoCard
│ └── Column (spacing: 12)
│ ├── Row (mainAxisAlignment: SpaceBetween)
│ │ ├── Text ("Revenue", style: titleLarge)
│ │ └── DropDown (options: ["This Week", "This Month", "This Year"])
│ ├── Text ("485,000 XAF", style: displayMedium, color: Primary)
│ ├── Row (spacing: 4)
│ │ ├── Icon (trending_up, color: Success, size: 16)
│ │ └── Text ("+12% vs last week", style: bodySmall, color: Success)
│ └── Chart (type: Line, height: 150, color: Primary)
├── Row (mainAxisAlignment: SpaceBetween)
│ ├── Text ("Pending Requests", style: titleLarge)
│ └── TextButton ("View All", color: Secondary)
├── [RentalRequestCard] × 2-3
│ └── ZivvoCard
│ └── Row (spacing: 12)
│ ├── CircleImage (customer photo)
│ ├── Expanded → Column
│ │ ├── Text (customerName, style: titleLarge)
│ │ └── Text ("Toyota Corolla • 3 days", style: bodySmall)
│ └── Column
│ ├── ZivvoButtonPrimary (text: "Accept", height: 36)
│ └── ZivvoButtonGhost (text: "Decline", height: 36)
└── SizedBox (height: 80) // bottom nav clearance
```
### Vehicle Management (Fleet List)
```
Scaffold (AppBar: title "My Fleet", actions: [filter, sort])
├── FloatingActionButton (icon: add, onTap: AddVehicle)
└── ListView (padding: 16, spacing: 12)
└── [VehicleCard] × N
└── ZivvoCard
└── Row (spacing: 12)
├── Container (width: 100, height: 80, radius: 12)
│ └── Image (src: vehiclePhoto, fit: Cover)
├── Expanded
│ └── Column (crossAxisAlignment: Start, spacing: 4)
│ ├── Text ("Toyota Corolla 2021", style: titleLarge)
│ ├── Text ("LT 1234 AB", style: bodySmall, color: TextSecondary)
│ ├── Row (spacing: 4)
│ │ └── Badge (status: "Active", color: conditional)
│ └── Text ("15,000 XAF/day", style: labelLarge, color: Primary)
└── IconButton (icon: more_vert, onTap: vehicle options menu)
```
---
## Admin Role Screens
### Admin Dashboard
```
Scaffold (AppBar: title "Admin", actions: [notifications])
└── ListView (padding: 16, spacing: 16)
├── Row (spacing: 12)
│ ├── Expanded → ZivvoCard
│ │ └── Column
│ │ ├── Icon (people, color: Secondary, size: 32)
│ │ ├── Text ("12,450", style: headingLarge)
│ │ └── Text ("Total Users", style: bodySmall, color: TextSecondary)
│ └── Expanded → ZivvoCard
│ └── Column
│ ├── Icon (directions_car, color: Primary, size: 32)
│ ├── Text ("342", style: headingLarge)
│ └── Text ("Active Rides", style: bodySmall, color: TextSecondary)
├── Row (spacing: 12)
│ ├── Expanded → ZivvoCard
│ │ └── Column
│ │ ├── Icon (verified, color: Success, size: 32)
│ │ ├── Text ("89", style: headingLarge)
│ │ └── Text ("KYC Pending", style: bodySmall, color: Warning)
│ └── Expanded → ZivvoCard
│ └── Column
│ ├── Icon (warning, color: Error, size: 32)
│ ├── Text ("3", style: headingLarge)
│ └── Text ("Active SOS", style: bodySmall, color: Error)
├── ZivvoCard
│ └── Column (spacing: 12)
│ ├── Text ("Revenue Today", style: titleLarge)
│ ├── Text ("1,250,000 XAF", style: displayMedium, color: Primary)
│ └── Chart (type: Bar, height: 150)
└── ZivvoCard
└── Column (spacing: 8)
├── Text ("Recent Activity", style: titleLarge)
└── [ActivityRow] × 5
└── ListTile (leading: avatar, title: action, subtitle: timestamp)
```
### KYC Review Screen
```
Scaffold (AppBar: title "KYC Review", back button)
└── ListView (padding: 16, spacing: 16)
├── ZivvoCard
│ └── Column (spacing: 12)
│ ├── Row (spacing: 12)
│ │ ├── ZivvoAvatar (src: applicantPhoto, size: 64)
│ │ └── Column (crossAxisAlignment: Start)
│ │ ├── Text (applicantName, style: headingMedium)
│ │ ├── Text ("Driver Application", style: bodyMedium, color: TextSecondary)
│ │ └── Text ("Submitted 2h ago", style: bodySmall, color: TextSecondary)
│ ├── Divider
│ ├── Text ("Identity Document", style: titleLarge)
│ ├── Image (src: idFrontPhoto, width: Expand, height: 200, fit: Contain, radius: 12)
│ ├── Image (src: idBackPhoto, width: Expand, height: 200, fit: Contain, radius: 12)
│ ├── Divider
│ ├── Text ("Driver's License", style: titleLarge)
│ ├── Image (src: licensePhoto, width: Expand, height: 200, fit: Contain, radius: 12)
│ ├── Divider
│ ├── Text ("Vehicle Registration", style: titleLarge)
│ ├── Image (src: registrationPhoto, width: Expand, height: 200, fit: Contain, radius: 12)
│ ├── Divider
│ ├── Text ("Selfie Verification", style: titleLarge)
│ ├── Row (spacing: 12)
│ │ ├── Image (src: selfiePhoto, width: 150, height: 150, fit: Cover, radius: 12)
│ │ └── Column
│ │ ├── Row → Icon(check) + Text("Face match: 94%")
│ │ └── Row → Icon(check) + Text("Liveness: Passed")
├── ZivvoInput (hintText: "Rejection reason (if applicable)", maxLines: 3)
├── Row (spacing: 12)
│ ├── Expanded
│ │ └── ZivvoButtonDanger (text: "Reject", onTap: reject with reason)
│ └── Expanded
│ └── ZivvoButtonPrimary (text: "Approve", onTap: approve KYC)
```
---
## Bottom Navigation Bars
### Customer Bottom Nav
```
BottomNavigationBar (type: Fixed, backgroundColor: Surface, elevation: 8)
├── Item (icon: home, label: "Home", activeColor: Primary)
├── Item (icon: history, label: "Rides", activeColor: Primary)
├── Item (icon: directions_car, label: "Rentals", activeColor: Primary)
└── Item (icon: person, label: "Account", activeColor: Primary)
```
### Driver Bottom Nav
```
BottomNavigationBar
├── Item (icon: map, label: "Home")
├── Item (icon: list, label: "Trips")
├── Item (icon: account_balance_wallet, label: "Earnings")
└── Item (icon: person, label: "Account")
```
### Dealer Bottom Nav
```
BottomNavigationBar
├── Item (icon: dashboard, label: "Dashboard")
├── Item (icon: directions_car, label: "Fleet")
├── Item (icon: assignment, label: "Requests")
└── Item (icon: person, label: "Account")
```
### Admin Bottom Nav
```
BottomNavigationBar
├── Item (icon: dashboard, label: "Dashboard")
├── Item (icon: people, label: "Users")
├── Item (icon: verified_user, label: "KYC")
├── Item (icon: report, label: "Disputes")
└── Item (icon: settings, label: "Settings")
```