-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathfestival_header.dart
More file actions
164 lines (154 loc) · 5.2 KB
/
Copy pathfestival_header.dart
File metadata and controls
164 lines (154 loc) · 5.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import 'package:flutter/material.dart';
import '../models/models.dart';
import '../providers/providers.dart';
/// App-bar title for the drinks screen: app icon, current festival name, the
/// drink count, and a coloured status badge.
class FestivalHeader extends StatelessWidget {
const FestivalHeader({required this.provider, super.key});
final BeerProvider provider;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final status = Festival.getStatusInContext(
provider.currentFestival,
provider.sortedFestivals,
);
final drinkCount = provider.drinks.length;
final drinkCountLabel =
'$drinkCount ${drinkCount == 1 ? 'drink' : 'drinks'}';
// Fold the status into the label and exclude child semantics so screen
// readers announce one coherent phrase instead of the name, count, and
// badge separately. Matches the pattern in DrinkCard, DrinkHeroPanel, etc.
return Semantics(
label:
'Current festival: ${provider.currentFestival.name}, '
'$drinkCountLabel, ${FestivalStatusBadge.spokenLabel(status)}',
excludeSemantics: true,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset('assets/app_icon.png', width: 32, height: 32),
const SizedBox(width: 12),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
provider.currentFestival.name,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
Row(
children: [
Flexible(
child: Text(
drinkCountLabel,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(width: 8),
FestivalStatusBadge(status: status, compact: true),
],
),
],
),
),
],
),
);
}
}
/// Small coloured pill summarising a festival's [FestivalStatus].
///
/// [compact] selects both the label wording and the geometry:
/// - `compact: true` (app-bar header) — short labels (LIVE / SOON / RECENT /
/// PAST), tighter padding, smaller radius and font.
/// - `compact: false` (default, festival browser cards) — long labels
/// (LIVE / COMING SOON / MOST RECENT / PAST), roomier padding, larger
/// radius and font.
///
/// Colours adapt to light and dark themes and are identical for both modes.
class FestivalStatusBadge extends StatelessWidget {
const FestivalStatusBadge({
required this.status,
this.compact = false,
super.key,
});
final FestivalStatus status;
final bool compact;
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final (compactLabel, longLabel, _, lightColor, darkColor) = _styleFor(
status,
);
return Container(
padding: compact
? const EdgeInsets.symmetric(horizontal: 6, vertical: 1)
: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: isDark ? darkColor : lightColor,
borderRadius: BorderRadius.circular(compact ? 8 : 12),
),
child: Text(
compact ? compactLabel : longLabel,
style: TextStyle(
color: Colors.white,
fontSize: compact ? 9 : 10,
fontWeight: FontWeight.bold,
),
),
);
}
/// Spoken form of the status for screen-reader labels (the badge text is
/// terse and is excluded from semantics at the parent level).
static String spokenLabel(FestivalStatus status) => _styleFor(status).$3;
/// Returns the compact label, long label, spoken label, and (light, dark)
/// background colours for [status]. Single source of truth for all status
/// styling, shared by the app-bar header and the festival browser cards.
static (String, String, String, Color, Color) _styleFor(
FestivalStatus status,
) {
switch (status) {
case FestivalStatus.live:
return const (
'LIVE',
'LIVE',
'live now',
Color(0xFF2E7D32),
Color(0xFF4CAF50),
);
case FestivalStatus.upcoming:
return const (
'SOON',
'COMING SOON',
'starting soon',
Color(0xFF1976D2),
Color(0xFF42A5F5),
);
case FestivalStatus.mostRecent:
return const (
'RECENT',
'MOST RECENT',
'most recent',
Color(0xFFEF6C00),
Color(0xFFFF9800),
);
case FestivalStatus.past:
return const (
'PAST',
'PAST',
'past',
Color(0xFF616161),
Color(0xFF9E9E9E),
);
}
}
}