Skip to content

Commit b1e63ac

Browse files
Merge pull request #110 from richardthe3rd/claude/visual-variety-drink-cards-01SzprN6sAyq2bqJfjoMTWdM
Add visual variety to drink cards
2 parents a05d012 + 5f3b9dc commit b1e63ac

6 files changed

Lines changed: 784 additions & 98 deletions

File tree

lib/screens/brewery_screen.dart

Lines changed: 191 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,7 @@ class _BreweryScreenState extends State<BreweryScreen> {
5353
final theme = Theme.of(context);
5454

5555
return Scaffold(
56-
appBar: AppBar(
57-
title: Text(producer.name),
58-
),
56+
appBar: AppBar(),
5957
body: CustomScrollView(
6058
slivers: [
6159
SliverToBoxAdapter(
@@ -91,53 +89,210 @@ class _BreweryScreenState extends State<BreweryScreen> {
9189

9290
Widget _buildHeader(BuildContext context, Producer producer, int drinkCount) {
9391
final theme = Theme.of(context);
92+
final brightness = theme.brightness;
93+
final initials = _getInitials(producer.name);
94+
9495
return Container(
9596
width: double.infinity,
96-
padding: const EdgeInsets.all(24),
97-
color: theme.colorScheme.primaryContainer,
98-
child: Column(
99-
crossAxisAlignment: CrossAxisAlignment.start,
97+
decoration: BoxDecoration(
98+
gradient: LinearGradient(
99+
begin: Alignment.topLeft,
100+
end: Alignment.bottomRight,
101+
colors: brightness == Brightness.dark
102+
? [
103+
theme.colorScheme.primaryContainer,
104+
theme.colorScheme.primaryContainer.withOpacity(0.7),
105+
]
106+
: [
107+
theme.colorScheme.primaryContainer,
108+
theme.colorScheme.secondaryContainer.withOpacity(0.5),
109+
],
110+
),
111+
),
112+
child: Stack(
100113
children: [
101-
Text(
102-
producer.name,
103-
style: theme.textTheme.headlineMedium?.copyWith(
104-
fontWeight: FontWeight.bold,
114+
// Large decorative letter pattern
115+
Positioned(
116+
right: -40,
117+
top: -20,
118+
child: Opacity(
119+
opacity: 0.08,
120+
child: Text(
121+
initials,
122+
style: TextStyle(
123+
fontSize: 180,
124+
fontWeight: FontWeight.w900,
125+
color: theme.colorScheme.primary,
126+
letterSpacing: -8,
127+
),
128+
),
105129
),
106130
),
107-
const SizedBox(height: 8),
108-
if (producer.location.isNotEmpty)
109-
Row(
131+
// Decorative circles
132+
Positioned(
133+
left: 20,
134+
bottom: 20,
135+
child: Container(
136+
width: 60,
137+
height: 60,
138+
decoration: BoxDecoration(
139+
shape: BoxShape.circle,
140+
color: theme.colorScheme.secondary.withOpacity(0.1),
141+
),
142+
),
143+
),
144+
Positioned(
145+
left: 50,
146+
bottom: 40,
147+
child: Container(
148+
width: 40,
149+
height: 40,
150+
decoration: BoxDecoration(
151+
shape: BoxShape.circle,
152+
color: theme.colorScheme.tertiary.withOpacity(0.08),
153+
),
154+
),
155+
),
156+
// Content
157+
Padding(
158+
padding: const EdgeInsets.all(24),
159+
child: Column(
160+
crossAxisAlignment: CrossAxisAlignment.start,
110161
children: [
111-
Icon(
112-
Icons.location_on,
113-
size: 16,
114-
color: theme.colorScheme.onPrimaryContainer.withValues(alpha: 0.7),
162+
Row(
163+
children: [
164+
Container(
165+
width: 64,
166+
height: 64,
167+
decoration: BoxDecoration(
168+
gradient: LinearGradient(
169+
begin: Alignment.topLeft,
170+
end: Alignment.bottomRight,
171+
colors: [
172+
theme.colorScheme.primary,
173+
theme.colorScheme.secondary,
174+
],
175+
),
176+
borderRadius: BorderRadius.circular(16),
177+
boxShadow: [
178+
BoxShadow(
179+
color: theme.colorScheme.primary.withOpacity(0.3),
180+
blurRadius: 8,
181+
offset: const Offset(0, 2),
182+
),
183+
],
184+
),
185+
child: Center(
186+
child: Text(
187+
initials,
188+
style: theme.textTheme.headlineSmall?.copyWith(
189+
fontWeight: FontWeight.bold,
190+
color: theme.colorScheme.onPrimary,
191+
),
192+
),
193+
),
194+
),
195+
const SizedBox(width: 16),
196+
Expanded(
197+
child: Text(
198+
producer.name,
199+
style: theme.textTheme.headlineMedium?.copyWith(
200+
fontWeight: FontWeight.bold,
201+
),
202+
),
203+
),
204+
],
115205
),
116-
const SizedBox(width: 4),
117-
Text(
118-
producer.location,
119-
style: theme.textTheme.bodyMedium?.copyWith(
120-
color: theme.colorScheme.onPrimaryContainer.withValues(alpha: 0.7),
206+
const SizedBox(height: 16),
207+
if (producer.location.isNotEmpty)
208+
Row(
209+
children: [
210+
Icon(
211+
Icons.location_on,
212+
size: 16,
213+
color: theme.colorScheme.onPrimaryContainer.withValues(alpha: 0.7),
214+
),
215+
const SizedBox(width: 4),
216+
Text(
217+
producer.location,
218+
style: theme.textTheme.bodyMedium?.copyWith(
219+
color: theme.colorScheme.onPrimaryContainer.withValues(alpha: 0.7),
220+
),
221+
),
222+
],
223+
),
224+
if (producer.yearFounded != null) ...[
225+
const SizedBox(height: 4),
226+
Row(
227+
children: [
228+
Icon(
229+
Icons.calendar_today,
230+
size: 16,
231+
color: theme.colorScheme.onPrimaryContainer.withValues(alpha: 0.7),
232+
),
233+
const SizedBox(width: 4),
234+
Text(
235+
'Est. ${producer.yearFounded}',
236+
style: theme.textTheme.bodyMedium?.copyWith(
237+
color: theme.colorScheme.onPrimaryContainer.withValues(alpha: 0.7),
238+
),
239+
),
240+
],
241+
),
242+
],
243+
const SizedBox(height: 16),
244+
// Stats card
245+
Container(
246+
padding: const EdgeInsets.all(16),
247+
decoration: BoxDecoration(
248+
color: theme.colorScheme.surface.withOpacity(0.5),
249+
borderRadius: BorderRadius.circular(12),
250+
border: Border.all(
251+
color: theme.colorScheme.outline.withOpacity(0.2),
252+
),
253+
),
254+
child: Row(
255+
children: [
256+
Icon(
257+
Icons.local_drink,
258+
size: 20,
259+
color: theme.colorScheme.primary,
260+
),
261+
const SizedBox(width: 8),
262+
Text(
263+
'$drinkCount drinks at this festival',
264+
style: theme.textTheme.bodyMedium?.copyWith(
265+
fontWeight: FontWeight.w500,
266+
),
267+
),
268+
],
121269
),
122270
),
123271
],
124272
),
125-
if (producer.yearFounded != null) ...[
126-
const SizedBox(height: 4),
127-
Text(
128-
'Est. ${producer.yearFounded}',
129-
style: theme.textTheme.bodyMedium?.copyWith(
130-
color: theme.colorScheme.onPrimaryContainer.withValues(alpha: 0.7),
131-
),
132-
),
133-
],
134-
const SizedBox(height: 8),
135-
Text(
136-
'$drinkCount drinks at this festival',
137-
style: theme.textTheme.bodySmall,
138273
),
139274
],
140275
),
141276
);
142277
}
278+
279+
/// Extract initials from brewery name (max 2 letters)
280+
String _getInitials(String name) {
281+
final words = name.trim().split(RegExp(r'\s+')).where((w) => w.isNotEmpty).toList();
282+
if (words.isEmpty) return '?';
283+
284+
if (words.length == 1) {
285+
// Single word: take first 2 letters
286+
final word = words[0];
287+
if (word.length < 2) return word.toUpperCase();
288+
return word.substring(0, 2).toUpperCase();
289+
} else {
290+
// Multiple words: take first letter of first two words
291+
// Safe because we filtered empty words above
292+
final first = words[0].isNotEmpty ? words[0][0] : '';
293+
final second = (words.length > 1 && words[1].isNotEmpty) ? words[1][0] : '';
294+
final initials = first + second;
295+
return initials.isNotEmpty ? initials.toUpperCase() : '?';
296+
}
297+
}
143298
}

0 commit comments

Comments
 (0)