import 'package:flutter/material.dart'; /// Miler Professional Design System Constants /// Centralized design tokens for spacing, radius, shadows, and layout /// ───────────────────────────────────────────────────────────────────────── /// SHARED RADII AND ELEVATION /// /// ── Why this used to be much bigger ── /// /// This class shipped 43 members: a nine-step spacing scale, eleven `EdgeInsets` /// presets, six radii, five shadows, a `cardDecoration` helper and three named /// border radii. An audit found **six of them used, 17 times in total.** The /// spacing scale had never been referenced once; every screen writes `16.w` or /// `12.h` inline, which is both more readable at the call site and correct for /// this app, because those need ScreenUtil's per-device scaling and a `const /// double` cannot have it. /// /// The 37 dead members were not harmless. A token nobody uses still has to be /// read, still looks authoritative, and is the reason someone reaches for /// `Colors.grey.shade100` instead — a design system that is mostly aspirational /// teaches people to ignore it. So the unused members are gone and what is left /// is only what the app actually reaches for. /// /// The audit metric that flagged this file was itself misleading, which is worth /// recording: 13 files import `design_constants.dart` and only 5 mention /// `DesignConstants`. The other 8 import it for [ButtonSizes], which lives in /// this same file and is used 72 times. Colocating a thriving class with a dead /// one hid both facts. /// /// **Adding to this class:** don't, until a second call site needs the value. /// Two uses make a token; one use is a local constant. /// ───────────────────────────────────────────────────────────────────────── class DesignConstants { DesignConstants._(); // ── Motion ──────────────────────────────────────────────────────────── // // The app was using 35 distinct durations. No shared vocabulary means every // transition is a fresh guess, and some of the guesses sat directly in the // rider's path between two stops — an 800ms entrance and several 500ms // cross-fades on screens he passes through 40 times a shift. // // Motion on a driver app is a tax paid all day. It earns its place only when // it explains a state change; anything else is time spent standing still. /// Press and release. Fast enough to feel like the surface responded rather /// than animated. static const Duration motionPress = Duration(milliseconds: 120); /// A state change in place: a chip selecting, a tick landing, a row opening. static const Duration motionState = Duration(milliseconds: 200); /// A page or a sheet arriving. The ceiling for anything on the critical path. static const Duration motionPage = Duration(milliseconds: 300); /// Celebration, and only celebration — once per completed route, never on a /// screen the rider is trying to get through. static const Duration motionCelebrate = Duration(milliseconds: 600); // ── Border radius ───────────────────────────────────────────────────── // // ── Three, because 26 communicated nothing ── // // An audit counted 26 distinct radii in use against the four declared here, // and found the most common value in the app was 14 — `ButtonSizes.radius`, // a *button* token, being applied to cards and sheets. Radius is one of the // strongest cues that two things belong to the same family; at 26 values it // says nothing, and two elements side by side at 12 and 14 read as a // rendering bug rather than a decision. // // `radius2xl` (20) is retired: it was the third-most-used value and had no // job the container radius could not do. /// **Inner** — inputs, chips, small tiles, panels *inside* a container. static const double radiusLg = 12.0; /// **Container** — cards, sheets, and anything that is a surface in its own /// right. Buttons use this too, so a button and the card it sits on agree. static const double radiusXl = 16.0; /// **Pill** — fully rounded: pills, circular tracks, avatars. static const double radiusFull = 999.0; // ── Elevation ───────────────────────────────────────────────────────── // // Only two survive, and both are used sparingly. Most cards in the app are // now borderless fills with no shadow at all — see // `ColorConstants.cardSurface` — so elevation is the exception, not the // default it once was. /// A card lifted just off the page. static const List shadowSm = [ BoxShadow(color: Color(0x1A000000), blurRadius: 4, offset: Offset(0, 2)), ]; /// Something floating over content: a menu, a dragged element. static const List shadowLg = [ BoxShadow(color: Color(0x2A000000), blurRadius: 12, offset: Offset(0, 8)), ]; } /// ───────────────────────────────────────────────────────────────────────── /// BUTTON SIZING — one scale for the whole app. /// /// Buttons had drifted to a dozen different heights (44, 48, 52, 54, 56, 58) /// across the pickup flow, Home, auth and the sheets. On a screen the rider /// uses 30–50 times a day that reads as sloppiness, and worse, it breaks the /// muscle memory that lets him hit a control without looking: if "the green /// one at the bottom" is a different size on every screen, he has to aim. /// /// Three sizes, and only three. Every button in the app must pick one. /// ───────────────────────────────────────────────────────────────────────── class ButtonSizes { ButtonSizes._(); /// **Primary** — the one action a screen exists for. Full-width CTAs: /// Accept Trip, Start Pickup, Confirm, Navigate. static const double primary = 56.0; /// **Secondary** — supporting actions that sit beside or below a primary: /// Reject, Skip, Details, filter chips with a tap target. static const double secondary = 48.0; /// **Compact** — dense rows where several actions share a line, e.g. the /// per-stop Accept/Reject pair inside a trip card. Still above the 44dp /// accessibility floor. static const double compact = 44.0; /// Square icon-only buttons (call, overflow) match [primary] so they line up /// with the CTA they sit next to. static const double icon = 56.0; /// Corner radius, shared by every button size so they read as one family. /// /// 14 → 16 so it matches [DesignConstants.radiusXl]. At 14 a button sitting /// on a 16pt card had a visibly tighter corner than the card holding it, /// which is the kind of two-point difference nobody can name and everybody /// sees. static const double radius = DesignConstants.radiusXl; /// Absolute floor for any tappable target, per accessibility guidance. static const double minTapTarget = 44.0; } /// ───────────────────────────────────────────────────────────────────────── /// DESIGN TRIAL — full addresses on stop cards /// /// Turn on to see every stop card carrying its **complete** address rather than /// the trimmed one, over [kFullAddressMaxLines] lines instead of two. /// /// Two separate things are suppressed normally, and both are lifted together /// here or the trial would not show what it claims to: /// /// • **Home shortens the text.** `Trip.shortAddress` strips the tail every stop /// on the route shares — the city, the state, the pincode — because repeating /// "Coimbatore, Tamil Nadu 641006" on all six stops tells the rider nothing /// about which one he is looking at. With the trial on, the raw address is /// used instead. Bookings already shows the raw value. /// /// • **Both screens cap the lines.** Two, so a card's height does not depend on /// how verbose a customer was — a list of cards that step unevenly down the /// screen is harder to scan and fits fewer stops. /// /// Both caps exist for measured reasons, so this is a switch rather than a /// deletion. **Set to `false` to restore the shipped behaviour.** const bool kFullAddressTrial = true; /// Line cap while [kFullAddressTrial] is on. Four fits a full Indian address at /// 13sp in a stop card's column without letting one pathological record run the /// card off the screen. const int kFullAddressMaxLines = 4;