device infos
This commit is contained in:
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
import 'package:miler/data/bag_manifest.dart';
|
||||
import 'package:miler/data/order_manifest.dart';
|
||||
import 'package:miler/views/helpers/constants/miler_surface.dart';
|
||||
import 'package:miler/data/service_profile.dart';
|
||||
import 'package:miler/data/stop_area.dart';
|
||||
@@ -87,7 +87,7 @@ import 'package:miler/views/helpers/constants/miler_type.dart';
|
||||
/// One pickup place and every order the rider collects there.
|
||||
///
|
||||
/// Built by the page from the trip's own stops — this holds no state of its
|
||||
/// own and derives no counts. The bag lines come from [BagManifest.forGroup],
|
||||
/// own and derives no counts. The bag lines come from [OrderManifest.forGroup],
|
||||
/// which is the single source for "how many bags" and "which bag": the count is
|
||||
/// `orders.length` because a bag is what an order arrives in, and the identity
|
||||
/// is the printed label or the order's position **in this group**. The timeline
|
||||
@@ -101,6 +101,26 @@ class RouteGroup {
|
||||
/// which case the group is drawn flat (see [RouteTimeline]).
|
||||
final String name;
|
||||
|
||||
/// The street the counter is on — where [name] actually is.
|
||||
///
|
||||
/// ── Why a name was not enough ──
|
||||
///
|
||||
/// The card gave the place a name, a load and a distance, and then expected
|
||||
/// the rider to know where "Vidhya Kitchen" is. He often does; on his first
|
||||
/// week, or on a kitchen the hub added yesterday, he does not — and the only
|
||||
/// way to the address was to tap through to the preview sheet, which is a
|
||||
/// sheet and a dismiss to answer *where am I going*, the plainest question
|
||||
/// the card exists for.
|
||||
///
|
||||
/// It sits directly above the load, so the header reads as one descending
|
||||
/// statement: **what this place is, where it is, what is waiting there**.
|
||||
/// Put below the load it would have separated the counts from the journey
|
||||
/// figures they are read against.
|
||||
///
|
||||
/// Empty is normal and prints nothing — a payload without an address, or a
|
||||
/// flat stop whose own name already *is* the address.
|
||||
final String location;
|
||||
|
||||
/// The orders still shown on Home, in route order.
|
||||
final List<Map<String, dynamic>> stops;
|
||||
|
||||
@@ -112,7 +132,7 @@ class RouteGroup {
|
||||
/// the rider has collected Bag 1 and it leaves Home, numbering the four that
|
||||
/// remain from the visible list would renumber Bag 2 as Bag 1 — and he would
|
||||
/// be reading a different number off the phone from the one printed on the
|
||||
/// shelf. See [BagManifest].
|
||||
/// shelf. See [OrderManifest].
|
||||
final List<Map<String, dynamic>> allStops;
|
||||
|
||||
/// The trip index of the first stop, for travel time and for numbering.
|
||||
@@ -207,6 +227,7 @@ class RouteGroup {
|
||||
const RouteGroup({
|
||||
required this.key,
|
||||
required this.name,
|
||||
this.location = '',
|
||||
required this.stops,
|
||||
required this.allStops,
|
||||
required this.firstIndex,
|
||||
@@ -256,17 +277,17 @@ class RouteGroup {
|
||||
/// The manifest is built from [allStops] and then filtered, never built from
|
||||
/// [stops]: the count and the identity both come from the pickup group the
|
||||
/// rider was handed, not from whatever is left of it.
|
||||
List<BagLine> get manifest {
|
||||
List<ManifestLine> get manifest {
|
||||
final shown = {for (final s in stops) (s['orderid'] ?? '').toString()};
|
||||
return [
|
||||
for (final line in BagManifest.forGroup(allStops))
|
||||
for (final line in OrderManifest.forGroup(allStops))
|
||||
if (shown.contains(line.orderId)) line,
|
||||
];
|
||||
}
|
||||
|
||||
/// `5 orders · 5 bags`. The two halves of the rule, side by side, so the
|
||||
/// rider can check the shelf against the phone without doing arithmetic.
|
||||
String get countLabel => BagManifest.countLabel(stops.length);
|
||||
String get countLabel => OrderManifest.countLabel(stops.length);
|
||||
|
||||
/// Orders off this counter the rider has not taken on yet.
|
||||
int get pendingCount =>
|
||||
@@ -1200,7 +1221,7 @@ class PickupTimelineGroup extends StatelessWidget {
|
||||
}
|
||||
if (group.isSettled) {
|
||||
final n = group.allStops.length;
|
||||
return '$n ${n == 1 ? 'bag' : 'bags'} collected';
|
||||
return n == 1 ? '1 collected' : '$n collected';
|
||||
}
|
||||
return <String>[
|
||||
// ── A flat node is a stop, and a stop has a position ──
|
||||
@@ -1210,12 +1231,24 @@ class PickupTimelineGroup extends StatelessWidget {
|
||||
// in the run — the trip's own index, which does **not** renumber as stops
|
||||
// finish, so a stop is never renamed mid-shift.
|
||||
if (group.flat) 'Stop ${group.firstIndex + 1}' else group.countLabel,
|
||||
// How much of this counter is not his yet. A group can hold one accepted
|
||||
// order and one still pending, and the count above cannot say so alone.
|
||||
if (!group.flat &&
|
||||
group.pendingCount > 0 &&
|
||||
group.pendingCount < group.stops.length)
|
||||
'${group.pendingCount} to accept',
|
||||
// ── How much of this counter is still waiting on him ──
|
||||
//
|
||||
// The chip beside it states the group's **own rung** with a fraction —
|
||||
// `Pending 8/8` on an untouched counter, `Accepted 1/2` on one he has
|
||||
// started. That answers the all-pending case, which used to say nothing
|
||||
// at all: a fresh morning where the card read `Pending` and `10 bags` and
|
||||
// left the rider to join them into *ten decisions are waiting on me*.
|
||||
//
|
||||
// It cannot answer the **mixed** case, and that is what this clause is
|
||||
// for. A group holding one accepted order and one pending one is on the
|
||||
// accepted rung, so the chip says `Accepted 1/2` — true, and silent about
|
||||
// the one still to decide. The two never overlap: this appears only when
|
||||
// some but not all of the counter is pending, which is precisely when the
|
||||
// chip is showing a different rung.
|
||||
// ── The selection count is not stated here ──
|
||||
//
|
||||
// It used to append `· 5 selected`, which made this the **third** place
|
||||
@@ -1253,6 +1286,10 @@ class PickupTimelineGroup extends StatelessWidget {
|
||||
final journey = rejected || group.isSettled ? '' : group.journeyLabel;
|
||||
final showJourney = journey.isNotEmpty && twoColumn;
|
||||
|
||||
// Suppressed on a place the rider has finished with and on one he declined:
|
||||
// the address is wayfinding, and there is nowhere left to go.
|
||||
final location = rejected || group.isSettled ? '' : group.location;
|
||||
|
||||
final load = [
|
||||
_loadLine(chosen),
|
||||
if (journey.isNotEmpty && !twoColumn) journey,
|
||||
@@ -1361,7 +1398,12 @@ class PickupTimelineGroup extends StatelessWidget {
|
||||
// rider needs them: where, how much, how far. Unaffected by
|
||||
// the two-column break — a reader hears the same three facts
|
||||
// either way.
|
||||
label: [group.name, load, if (showJourney) journey].join(', '),
|
||||
label: [
|
||||
group.name,
|
||||
if (location.isNotEmpty) location,
|
||||
load,
|
||||
if (showJourney) journey,
|
||||
].join(', '),
|
||||
// The non-flat hint said "Opens the pickup preview" — a
|
||||
// sentence about a tap this header stopped owning when the
|
||||
// preview moved to the utility circle. A wrong hint is worse
|
||||
@@ -1472,6 +1514,49 @@ class PickupTimelineGroup extends StatelessWidget {
|
||||
// that row was already spending on a `Spacer`. Same
|
||||
// reading order — identity, load, state — and the
|
||||
// geometry is now identical in every state.
|
||||
// ── Where the counter is, above what is on it ──
|
||||
//
|
||||
// Between the name and the load, because that is
|
||||
// the order the questions arrive in: which place,
|
||||
// where is it, how much is waiting. A pin rather
|
||||
// than a label — "Pickup location" would be a word
|
||||
// spent saying what the glyph and the position
|
||||
// already say.
|
||||
//
|
||||
// Two lines, matching the name above it: real
|
||||
// Coimbatore addresses are long, and an address cut
|
||||
// to `Gandhipuram Main…` answers nothing a blank
|
||||
// line would not have answered. Set in the page's
|
||||
// quiet grey — it is orientation, not a figure the
|
||||
// rider acts on.
|
||||
if (location.isNotEmpty) ...[
|
||||
SizedBox(height: 3.h),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 1.h),
|
||||
child: Icon(
|
||||
LucideIcons.mapPin,
|
||||
size: 12.sp,
|
||||
color: ColorConstants.secondaryText,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5.w),
|
||||
Flexible(
|
||||
child: Text(
|
||||
location,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: MilerType.micro.copyWith(
|
||||
fontSize: 12.5.sp,
|
||||
color: ColorConstants.secondaryText,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
SizedBox(height: 4.h),
|
||||
Row(
|
||||
children: [
|
||||
@@ -1620,23 +1705,38 @@ class PickupTimelineGroup extends StatelessWidget {
|
||||
// silence was worse than the repetition: a rider scanning a run had to
|
||||
// infer "not mine yet" from the *absence* of a word, which is the one thing
|
||||
// a glance cannot do.
|
||||
// ── Pending is neutral, because nothing has gone wrong ──
|
||||
// ── Pending has its own amber, and it is not the warning one ──
|
||||
//
|
||||
// It was amber, on the argument that undecided work "wants something" and
|
||||
// should therefore be visible. The argument is right about visibility and
|
||||
// wrong about the colour: amber is this app's exception hue — a skipped
|
||||
// stop, a late run, a bag that never arrived — and on a fresh morning every
|
||||
// kitchen is pending, so the whole screen opened in warning.
|
||||
// It was grey, and before that it was [ColorConstants.warning]. Both were
|
||||
// half right. Grey said "nothing has gone wrong", which is true; warning
|
||||
// said "this wants something from you", which is also true — and warning
|
||||
// is the hue this app spends on a skip, a short payment, a bag that never
|
||||
// came, so a morning where every kitchen is pending opened the whole screen
|
||||
// in exception and taught the rider to stop reading the colour.
|
||||
//
|
||||
// A state that is true of *all* the work is not an exception; it is the
|
||||
// starting condition. Spending the attention colour on it leaves nothing
|
||||
// to say with when a stop genuinely does go wrong, and a rider who sees
|
||||
// amber on every group learns to stop reading it. Neutral, and the reason
|
||||
// it is still readable is that it sits on its own metadata line rather
|
||||
// than competing with the kitchen name.
|
||||
StopState.pending => const _StateWord(
|
||||
label: 'Pending',
|
||||
color: ColorConstants.secondaryText,
|
||||
// [ColorConstants.pendingAmber] is the third answer: a gold that reads as
|
||||
// *waiting* rather than as *wrong*, far enough from the burnt orange of
|
||||
// warning that Pending and Skipped are two distinct marks down a run. The
|
||||
// words differ too; this is what makes them differ at a glance.
|
||||
// ── And it says how many ──
|
||||
//
|
||||
// A bare `Pending` made the rider join two facts to get the one he wants.
|
||||
// The card said `Pending` and, twenty points above, `10 orders · 10 bags`;
|
||||
// *ten decisions are waiting on me* was the arithmetic between them, and on
|
||||
// a fresh morning — every group pending, nothing accepted — that was the
|
||||
// whole screen asking him to do it.
|
||||
//
|
||||
// Stated as a fraction because that is what Accepted has always done here,
|
||||
// and the two must read as one vocabulary: `Pending 10/10` on a counter
|
||||
// untouched, `Pending 2/4` on one half-taken, `Accepted 2/4` from the other
|
||||
// side of the same decision. The bare word survives where a fraction would
|
||||
// be noise — a flat parcel stop is one order, and `Pending 1/1` says
|
||||
// nothing `Pending` does not.
|
||||
StopState.pending => _StateWord(
|
||||
label: group.groupSize > 1
|
||||
? 'Pending ${group.pendingCount}/${group.groupSize}'
|
||||
: 'Pending',
|
||||
color: ColorConstants.pendingAmber,
|
||||
),
|
||||
// ── "Accepted" alone does not say how much of the counter he took ──
|
||||
//
|
||||
@@ -1649,19 +1749,25 @@ class PickupTimelineGroup extends StatelessWidget {
|
||||
// Only where there is more than one order to be a fraction of — a parcel
|
||||
// route is one order per place, and `Accepted (1 of 1)` is noise.
|
||||
StopState.accepted => _StateWord(
|
||||
// Brand only on the group he is riding to; slate on the ones he has taken
|
||||
// but is not working. Red marks the mission, not the commitment — and it
|
||||
// is now the only thing on the card that can be confused with amber, so
|
||||
// the two states read apart at a glance.
|
||||
// ── Accepted is green ──
|
||||
//
|
||||
// It was brand red on the group being ridden to and slate on the rest,
|
||||
// which made the word carry two facts at once — *taken on* and *this is
|
||||
// the one you are working* — and the second was the louder of them. A
|
||||
// rider glancing down a run read the red as the state.
|
||||
//
|
||||
// Green says the one thing the word is for: this is yours, it is settled,
|
||||
// nothing here needs deciding. Which one he is *on* is already said three
|
||||
// ways that do not need a hue — the lit node, the larger title, and the
|
||||
// Navigate button that only the current place carries.
|
||||
//
|
||||
// `Accepted (2 of 4)` truncated to `Accepted (2 o…` the moment the row
|
||||
// also carried Navigate, Call and the "i" — which is every accepted
|
||||
// group. A state that ellipsises has lost the number it exists to state.
|
||||
label: group.groupSize > 1
|
||||
? 'Accepted ${group.onRungCount}/${group.groupSize}'
|
||||
: 'Accepted',
|
||||
color: isCurrent
|
||||
? ColorConstants.primary
|
||||
: ColorConstants.onSurfaceVariant,
|
||||
color: ColorConstants.acceptGreen,
|
||||
),
|
||||
// ── The one the rider must not miss ──
|
||||
//
|
||||
@@ -1674,9 +1780,19 @@ class PickupTimelineGroup extends StatelessWidget {
|
||||
// fact is *this is the one he is on* — and each says which rung it is.
|
||||
StopState.arrived => const LiveMark(label: 'Arrived'),
|
||||
StopState.active => const LiveMark(),
|
||||
// ── Picked is blue, now that green means accepted ──
|
||||
//
|
||||
// It was green, back when green was free. It cannot stay green beside an
|
||||
// accepted state that is also green: the two rungs a rider most needs to
|
||||
// tell apart on one counter — *taken on* and *in my hands* — would be one
|
||||
// colour and two words.
|
||||
//
|
||||
// Blue is not an arbitrary third: it is [ColorConstants.deliveryAccent],
|
||||
// what this app already paints the delivery leg in, and a picked bag has
|
||||
// become exactly that. The colour says where the work went.
|
||||
StopState.collected || StopState.done => const _StateWord(
|
||||
label: 'Collected',
|
||||
color: ColorConstants.acceptGreen,
|
||||
color: ColorConstants.pickedBlue,
|
||||
),
|
||||
// ── The two ends of "not going to happen here" ──
|
||||
//
|
||||
@@ -1715,7 +1831,7 @@ class PickupTimelineGroup extends StatelessWidget {
|
||||
|
||||
// ── The body: the journey, the selection helper, then the milestones ─────
|
||||
|
||||
Widget _body(BuildContext context, List<BagLine> manifest, int chosen) {
|
||||
Widget _body(BuildContext context, List<ManifestLine> manifest, int chosen) {
|
||||
final ids = _choosableIds;
|
||||
final selectable = _isChoosable(group.state) && onToggleSelect != null;
|
||||
|
||||
@@ -1864,7 +1980,7 @@ class PickupTimelineGroup extends StatelessWidget {
|
||||
/// bag* — the two facts he matches against a shelf — and everything else is one
|
||||
/// tap away in the sheet that already exists.
|
||||
class PickupTimelineStep extends StatelessWidget {
|
||||
final BagLine line;
|
||||
final ManifestLine line;
|
||||
|
||||
/// 1-based position **within its own pickup group**, which is also its bag
|
||||
/// position when the kitchen printed no label. Never route-global.
|
||||
@@ -2013,9 +2129,7 @@ class PickupTimelineStep extends StatelessWidget {
|
||||
child: Semantics(
|
||||
button: true,
|
||||
checked: selectable ? selected : null,
|
||||
label: selectable
|
||||
? 'Select $customer, ${line.bag}'
|
||||
: '$customer, ${line.bag}',
|
||||
label: selectable ? 'Select $customer' : customer,
|
||||
excludeSemantics: true,
|
||||
child: InkWell(
|
||||
onTap: selectable && !busy
|
||||
@@ -2070,7 +2184,11 @@ class PickupTimelineStep extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
_OrderOpenBlock(
|
||||
bag: rejected ? 'Rejected' : line.bag,
|
||||
// The counter's own printed label when there is one,
|
||||
// and the word `Rejected` where the row has been
|
||||
// declined. Never an invented number — see
|
||||
// [OrderManifest.labelFor].
|
||||
label: rejected ? 'Rejected' : line.label,
|
||||
orderId: line.orderId,
|
||||
selected: selected,
|
||||
onTap: canOpen ? onOpen : null,
|
||||
@@ -2087,23 +2205,34 @@ class PickupTimelineStep extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// `Bag 1 ›` over the order's own id — the step's way into its detail sheet.
|
||||
/// The step's way into its detail sheet: the counter's printed label, when
|
||||
/// there is one, and the chevron that opens the order.
|
||||
///
|
||||
/// One block rather than three loose elements, because they answer one
|
||||
/// question between them: *which order is this, and show me it*. The chevron is
|
||||
/// the affordance; the bag and the id are what it is about.
|
||||
/// ── It used to manufacture a reference ──
|
||||
///
|
||||
/// The tag read `Bag 1 … Bag n`, falling back to the order's position in its
|
||||
/// group whenever the payload printed nothing — so on the common route, where
|
||||
/// no counter prints labels, every row carried a shelf reference the shelf had
|
||||
/// never heard of. It was the app numbering its own list and drawing the result
|
||||
/// as a physical fact, two columns from a rail that numbers the same rows.
|
||||
///
|
||||
/// What the counter really printed is a fact about an object in the rider's
|
||||
/// hands, so that still shows, verbatim. Where nothing was printed the corner
|
||||
/// carries the chevron alone, which is the affordance the block existed for.
|
||||
/// The neighbourhood a stop is in. One implementation, shared with the
|
||||
/// Deliveries queue — see [areaOf].
|
||||
String _areaOf(Map<String, dynamic> stop) => areaOf(stop);
|
||||
|
||||
class _OrderOpenBlock extends StatelessWidget {
|
||||
final String bag;
|
||||
/// The counter's own printed label, or `''` when it printed none.
|
||||
final String label;
|
||||
|
||||
final String orderId;
|
||||
final bool selected;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
const _OrderOpenBlock({
|
||||
required this.bag,
|
||||
required this.label,
|
||||
required this.orderId,
|
||||
required this.selected,
|
||||
required this.onTap,
|
||||
@@ -2115,7 +2244,10 @@ class _OrderOpenBlock extends StatelessWidget {
|
||||
|
||||
return Semantics(
|
||||
button: onTap != null,
|
||||
label: id.isEmpty ? bag : '$bag, order $id',
|
||||
label: [
|
||||
if (label.isNotEmpty) label,
|
||||
if (id.isNotEmpty) 'order $id',
|
||||
].join(', '),
|
||||
hint: onTap == null ? null : 'Opens order details',
|
||||
excludeSemantics: true,
|
||||
child: InkWell(
|
||||
@@ -2128,99 +2260,73 @@ class _OrderOpenBlock extends StatelessWidget {
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
// Capped so a kitchen's own long printed label (`DG-100042-A`)
|
||||
// cannot take the customer's name with it.
|
||||
//
|
||||
// ── And the column stops growing before the name does ──
|
||||
//
|
||||
// `Bag 1` is a shelf reference: it is *compared*, not read as
|
||||
// prose, so it does not need to keep pace with a rider's text
|
||||
// size. Left to scale it took ~150pt of a 320pt row at 2×, and
|
||||
// the thing that yielded was the customer's name — which is
|
||||
// the one item on the row the brief says must yield last.
|
||||
//
|
||||
// Clamped at 1.25×, which keeps it comfortably legible against
|
||||
// a physical shelf while handing the rest of the row back to
|
||||
// the name and the area.
|
||||
constraints: BoxConstraints(maxWidth: 104.w),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// ── One order, one bag ──
|
||||
//
|
||||
// Read straight off [BagManifest]: never a quantity, never a
|
||||
// route-global number. Right-aligned on every row so a
|
||||
// column of them lines up against a shelf being counted.
|
||||
if (label.isNotEmpty)
|
||||
ConstrainedBox(
|
||||
// Capped so a kitchen's own long printed label
|
||||
// (`DG-100042-A`) cannot take the customer's name with it.
|
||||
//
|
||||
// ── And the column stops growing before the name does ──
|
||||
//
|
||||
// A printed label is *compared*, not read as prose, so it
|
||||
// does not need to keep pace with a rider's text size. Left
|
||||
// to scale it took ~150pt of a 320pt row at 2×, and the
|
||||
// thing that yielded was the customer's name — the one item
|
||||
// on the row the brief says must yield last.
|
||||
//
|
||||
// Clamped at 1.25×, which keeps it legible against a
|
||||
// physical shelf while handing the rest of the row back to
|
||||
// the name and the area.
|
||||
constraints: BoxConstraints(maxWidth: 104.w),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 7.w,
|
||||
vertical: 3.h,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
// White, because these rows live inside the canvas
|
||||
// well: layer-1-on-canvas, the same 1.29 step the rest
|
||||
// of the system stands on. (Canvas-on-canvas was
|
||||
// invisible the moment the well returned.)
|
||||
color: selected
|
||||
? ColorConstants.primary.withValues(alpha: 0.10)
|
||||
: MilerSurface.working,
|
||||
borderRadius: BorderRadius.circular(
|
||||
DesignConstants.radiusLg,
|
||||
),
|
||||
),
|
||||
// ── It ranks under the name, not over it ──
|
||||
//
|
||||
// Drawn at w700 in full slate this sat *above* the
|
||||
// customer, who is w500 in the mid-tone until he is
|
||||
// ticked. So on a fresh route — every row undecided,
|
||||
// which is how the screen opens — the boldest thing on
|
||||
// every line was a shelf number, and the eye read a
|
||||
// column of bags before it read a single person.
|
||||
//
|
||||
// The reference still needs to be scannable against a
|
||||
// physical shelf, so it keeps a weight of its own: it
|
||||
// steps to w600 in the mid-tone, one rung under the
|
||||
// undecided name, and follows the row up to slate the
|
||||
// moment the row is taken. The bag never leads; it
|
||||
// travels with whatever the row currently is.
|
||||
// ── And it is a tag, because it is a label ──
|
||||
//
|
||||
// A bag number is a physical reference — the sticker on
|
||||
// the shelf — not a sentence, and every delivery app the
|
||||
// rider carries sets that kind of fact as a small tag.
|
||||
// The fill is the page's canvas tone, the only grey that
|
||||
// measurably stands on this white panel (1.29:1), so the
|
||||
// tag reads as an object without borrowing a border. It
|
||||
// still ranks under the name: quiet ink at rest, brand
|
||||
// tint the moment the row is ticked.
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 7.w,
|
||||
vertical: 3.h,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
// White, because these rows live inside the canvas
|
||||
// well: layer-1-on-canvas, the same 1.29 step the
|
||||
// rest of the system stands on. (Canvas-on-canvas
|
||||
// was invisible the moment the well returned.)
|
||||
// ticked — so on a fresh route the boldest thing on every
|
||||
// line was a shelf reference, and the eye read a column of
|
||||
// them before it read a single person. It steps to w600 in
|
||||
// the mid-tone instead, one rung under the undecided name,
|
||||
// and follows the row up to slate the moment it is taken.
|
||||
child: Text(
|
||||
label,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: MilerType.label.copyWith(
|
||||
fontSize: 12.sp,
|
||||
fontWeight: selected
|
||||
? FontWeight.w700
|
||||
: FontWeight.w600,
|
||||
color: selected
|
||||
? ColorConstants.primary.withValues(alpha: 0.10)
|
||||
: MilerSurface.working,
|
||||
borderRadius: BorderRadius.circular(
|
||||
DesignConstants.radiusLg,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
bag,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: MilerType.label.copyWith(
|
||||
fontSize: 12.sp,
|
||||
fontWeight: selected
|
||||
? FontWeight.w700
|
||||
: FontWeight.w600,
|
||||
color: selected
|
||||
? ColorConstants.primary
|
||||
: ColorConstants.secondaryText,
|
||||
),
|
||||
? ColorConstants.primary
|
||||
: ColorConstants.secondaryText,
|
||||
),
|
||||
),
|
||||
// ── The reference is not on the row any more ──
|
||||
//
|
||||
// It is what the office reads down a phone once, when
|
||||
// something has gone wrong. The line it occupied now
|
||||
// carries the drop area, which is what the rider is
|
||||
// deciding on every time he looks at the list. The id is on
|
||||
// the detail sheet this block opens, and on the Activity
|
||||
// record — both places the rare question is actually asked.
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// ── The order id is not on the row ──
|
||||
//
|
||||
// It is what the office reads down a phone once, when something
|
||||
// has gone wrong. The line it occupied carries the drop area,
|
||||
// which is what the rider is deciding on every time he looks at
|
||||
// the list. The id is on the detail sheet this block opens, and
|
||||
// on the Activity record — both places the rare question is
|
||||
// actually asked.
|
||||
if (onTap != null)
|
||||
Icon(
|
||||
LucideIcons.chevronRight,
|
||||
@@ -2629,7 +2735,7 @@ class _StepNode extends StatelessWidget {
|
||||
//
|
||||
// It keeps its **number**. The numeral is the order's bag identity, matched
|
||||
// against a printed label on a shelf, and it must not change or disappear
|
||||
// because the rider accepted it — see [BagManifest]. So accepted is a
|
||||
// because the rider accepted it — see [OrderManifest]. So accepted is a
|
||||
// filled disc with a white numeral, not a tick: a tick would claim work
|
||||
// that has not happened.
|
||||
final filled = selected || done || accepted;
|
||||
|
||||
Reference in New Issue
Block a user