@appiq/flutter-workflow 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +92 -0
- package/CONTRIBUTING.md +298 -0
- package/agents/claude/cubit-agent.md +63 -0
- package/agents/claude/data-agent.md +83 -0
- package/agents/claude/domain-agent.md +76 -0
- package/agents/claude/orchestrator.md +41 -0
- package/agents/claude/po-agent.md +42 -0
- package/agents/claude/security-agent.md +91 -0
- package/agents/claude/test-agent.md +114 -0
- package/agents/claude/ui-agent.md +56 -0
- package/agents/ui-agent.md +231 -67
- package/bin/cli.js +7 -2
- package/package.json +3 -2
- package/templates/platform-adaptive-widget-template.dart +407 -0
- package/templates/pretty-ui-examples.md +597 -0
@@ -0,0 +1,597 @@
|
|
1
|
+
# Pretty UI Examples for AppIQ Flutter Workflow
|
2
|
+
|
3
|
+
This document contains examples of modern, beautiful UI components following Pretty UI design principles.
|
4
|
+
|
5
|
+
## Color Palette Examples
|
6
|
+
|
7
|
+
### Example 1: Modern Blue Palette
|
8
|
+
```dart
|
9
|
+
class ModernBlueTheme {
|
10
|
+
static const Color primary = Color(0xFF6366F1);
|
11
|
+
static const Color primaryVariant = Color(0xFF4F46E5);
|
12
|
+
static const Color secondary = Color(0xFF06B6D4);
|
13
|
+
static const Color surface = Color(0xFFF8FAFC);
|
14
|
+
static const Color background = Color(0xFFFFFFFF);
|
15
|
+
static const Color error = Color(0xFFEF4444);
|
16
|
+
static const Color success = Color(0xFF10B981);
|
17
|
+
}
|
18
|
+
```
|
19
|
+
|
20
|
+
### Example 2: Warm Gradient Palette
|
21
|
+
```dart
|
22
|
+
class WarmGradientTheme {
|
23
|
+
static const LinearGradient primaryGradient = LinearGradient(
|
24
|
+
colors: [Color(0xFFFF6B6B), Color(0xFFFFE66D)],
|
25
|
+
begin: Alignment.topLeft,
|
26
|
+
end: Alignment.bottomRight,
|
27
|
+
);
|
28
|
+
|
29
|
+
static const LinearGradient secondaryGradient = LinearGradient(
|
30
|
+
colors: [Color(0xFF4ECDC4), Color(0xFF44A08D)],
|
31
|
+
begin: Alignment.topCenter,
|
32
|
+
end: Alignment.bottomCenter,
|
33
|
+
);
|
34
|
+
}
|
35
|
+
```
|
36
|
+
|
37
|
+
## Component Examples
|
38
|
+
|
39
|
+
### Modern Card Design
|
40
|
+
```dart
|
41
|
+
class PrettyProductCard extends StatelessWidget {
|
42
|
+
const PrettyProductCard({
|
43
|
+
super.key,
|
44
|
+
required this.title,
|
45
|
+
required this.price,
|
46
|
+
required this.imageUrl,
|
47
|
+
this.onTap,
|
48
|
+
});
|
49
|
+
|
50
|
+
final String title;
|
51
|
+
final String price;
|
52
|
+
final String imageUrl;
|
53
|
+
final VoidCallback? onTap;
|
54
|
+
|
55
|
+
@override
|
56
|
+
Widget build(BuildContext context) {
|
57
|
+
return Container(
|
58
|
+
margin: const EdgeInsets.all(8.0),
|
59
|
+
decoration: BoxDecoration(
|
60
|
+
color: Colors.white,
|
61
|
+
borderRadius: BorderRadius.circular(16.0),
|
62
|
+
boxShadow: [
|
63
|
+
BoxShadow(
|
64
|
+
color: Colors.black.withOpacity(0.08),
|
65
|
+
blurRadius: 20.0,
|
66
|
+
offset: const Offset(0, 4),
|
67
|
+
),
|
68
|
+
],
|
69
|
+
),
|
70
|
+
child: Material(
|
71
|
+
color: Colors.transparent,
|
72
|
+
child: InkWell(
|
73
|
+
onTap: onTap,
|
74
|
+
borderRadius: BorderRadius.circular(16.0),
|
75
|
+
child: Column(
|
76
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
77
|
+
children: [
|
78
|
+
ClipRRect(
|
79
|
+
borderRadius: const BorderRadius.vertical(
|
80
|
+
top: Radius.circular(16.0),
|
81
|
+
),
|
82
|
+
child: AspectRatio(
|
83
|
+
aspectRatio: 16 / 9,
|
84
|
+
child: Image.network(
|
85
|
+
imageUrl,
|
86
|
+
fit: BoxFit.cover,
|
87
|
+
),
|
88
|
+
),
|
89
|
+
),
|
90
|
+
Padding(
|
91
|
+
padding: const EdgeInsets.all(16.0),
|
92
|
+
child: Column(
|
93
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
94
|
+
children: [
|
95
|
+
Text(
|
96
|
+
title,
|
97
|
+
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
98
|
+
fontWeight: FontWeight.w600,
|
99
|
+
),
|
100
|
+
maxLines: 2,
|
101
|
+
overflow: TextOverflow.ellipsis,
|
102
|
+
),
|
103
|
+
const SizedBox(height: 8.0),
|
104
|
+
Text(
|
105
|
+
price,
|
106
|
+
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
107
|
+
color: Theme.of(context).primaryColor,
|
108
|
+
fontWeight: FontWeight.bold,
|
109
|
+
),
|
110
|
+
),
|
111
|
+
],
|
112
|
+
),
|
113
|
+
),
|
114
|
+
],
|
115
|
+
),
|
116
|
+
),
|
117
|
+
),
|
118
|
+
);
|
119
|
+
}
|
120
|
+
}
|
121
|
+
```
|
122
|
+
|
123
|
+
### Elegant Profile Header
|
124
|
+
```dart
|
125
|
+
class PrettyProfileHeader extends StatelessWidget {
|
126
|
+
const PrettyProfileHeader({
|
127
|
+
super.key,
|
128
|
+
required this.name,
|
129
|
+
required this.subtitle,
|
130
|
+
required this.avatarUrl,
|
131
|
+
this.backgroundGradient,
|
132
|
+
});
|
133
|
+
|
134
|
+
final String name;
|
135
|
+
final String subtitle;
|
136
|
+
final String avatarUrl;
|
137
|
+
final Gradient? backgroundGradient;
|
138
|
+
|
139
|
+
@override
|
140
|
+
Widget build(BuildContext context) {
|
141
|
+
return Container(
|
142
|
+
padding: const EdgeInsets.all(24.0),
|
143
|
+
decoration: BoxDecoration(
|
144
|
+
gradient: backgroundGradient ?? LinearGradient(
|
145
|
+
colors: [
|
146
|
+
Theme.of(context).primaryColor,
|
147
|
+
Theme.of(context).primaryColor.withOpacity(0.7),
|
148
|
+
],
|
149
|
+
begin: Alignment.topLeft,
|
150
|
+
end: Alignment.bottomRight,
|
151
|
+
),
|
152
|
+
borderRadius: const BorderRadius.vertical(
|
153
|
+
bottom: Radius.circular(32.0),
|
154
|
+
),
|
155
|
+
),
|
156
|
+
child: SafeArea(
|
157
|
+
child: Column(
|
158
|
+
children: [
|
159
|
+
const SizedBox(height: 20.0),
|
160
|
+
Container(
|
161
|
+
decoration: BoxDecoration(
|
162
|
+
shape: BoxShape.circle,
|
163
|
+
border: Border.all(
|
164
|
+
color: Colors.white,
|
165
|
+
width: 4.0,
|
166
|
+
),
|
167
|
+
boxShadow: [
|
168
|
+
BoxShadow(
|
169
|
+
color: Colors.black.withOpacity(0.2),
|
170
|
+
blurRadius: 20.0,
|
171
|
+
offset: const Offset(0, 8),
|
172
|
+
),
|
173
|
+
],
|
174
|
+
),
|
175
|
+
child: CircleAvatar(
|
176
|
+
radius: 50.0,
|
177
|
+
backgroundImage: NetworkImage(avatarUrl),
|
178
|
+
),
|
179
|
+
),
|
180
|
+
const SizedBox(height: 16.0),
|
181
|
+
Text(
|
182
|
+
name,
|
183
|
+
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
184
|
+
color: Colors.white,
|
185
|
+
fontWeight: FontWeight.bold,
|
186
|
+
),
|
187
|
+
),
|
188
|
+
const SizedBox(height: 4.0),
|
189
|
+
Text(
|
190
|
+
subtitle,
|
191
|
+
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
192
|
+
color: Colors.white.withOpacity(0.9),
|
193
|
+
),
|
194
|
+
),
|
195
|
+
],
|
196
|
+
),
|
197
|
+
),
|
198
|
+
);
|
199
|
+
}
|
200
|
+
}
|
201
|
+
```
|
202
|
+
|
203
|
+
### Modern Search Bar
|
204
|
+
```dart
|
205
|
+
class PrettySearchBar extends StatefulWidget {
|
206
|
+
const PrettySearchBar({
|
207
|
+
super.key,
|
208
|
+
this.hintText = 'Search...',
|
209
|
+
this.onChanged,
|
210
|
+
this.onSubmitted,
|
211
|
+
});
|
212
|
+
|
213
|
+
final String hintText;
|
214
|
+
final ValueChanged<String>? onChanged;
|
215
|
+
final ValueChanged<String>? onSubmitted;
|
216
|
+
|
217
|
+
@override
|
218
|
+
State<PrettySearchBar> createState() => _PrettySearchBarState();
|
219
|
+
}
|
220
|
+
|
221
|
+
class _PrettySearchBarState extends State<PrettySearchBar> {
|
222
|
+
final _controller = TextEditingController();
|
223
|
+
final _focusNode = FocusNode();
|
224
|
+
|
225
|
+
@override
|
226
|
+
Widget build(BuildContext context) {
|
227
|
+
return Container(
|
228
|
+
margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
229
|
+
decoration: BoxDecoration(
|
230
|
+
color: Theme.of(context).colorScheme.surface,
|
231
|
+
borderRadius: BorderRadius.circular(16.0),
|
232
|
+
boxShadow: [
|
233
|
+
BoxShadow(
|
234
|
+
color: Colors.black.withOpacity(0.06),
|
235
|
+
blurRadius: 10.0,
|
236
|
+
offset: const Offset(0, 2),
|
237
|
+
),
|
238
|
+
],
|
239
|
+
),
|
240
|
+
child: TextField(
|
241
|
+
controller: _controller,
|
242
|
+
focusNode: _focusNode,
|
243
|
+
onChanged: widget.onChanged,
|
244
|
+
onSubmitted: widget.onSubmitted,
|
245
|
+
decoration: InputDecoration(
|
246
|
+
hintText: widget.hintText,
|
247
|
+
hintStyle: TextStyle(
|
248
|
+
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
|
249
|
+
),
|
250
|
+
prefixIcon: Icon(
|
251
|
+
Icons.search,
|
252
|
+
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
|
253
|
+
),
|
254
|
+
suffixIcon: _controller.text.isNotEmpty
|
255
|
+
? IconButton(
|
256
|
+
icon: const Icon(Icons.clear),
|
257
|
+
onPressed: () {
|
258
|
+
_controller.clear();
|
259
|
+
widget.onChanged?.call('');
|
260
|
+
},
|
261
|
+
)
|
262
|
+
: null,
|
263
|
+
border: InputBorder.none,
|
264
|
+
contentPadding: const EdgeInsets.symmetric(
|
265
|
+
horizontal: 16.0,
|
266
|
+
vertical: 12.0,
|
267
|
+
),
|
268
|
+
),
|
269
|
+
),
|
270
|
+
);
|
271
|
+
}
|
272
|
+
|
273
|
+
@override
|
274
|
+
void dispose() {
|
275
|
+
_controller.dispose();
|
276
|
+
_focusNode.dispose();
|
277
|
+
super.dispose();
|
278
|
+
}
|
279
|
+
}
|
280
|
+
```
|
281
|
+
|
282
|
+
### Smooth Bottom Navigation
|
283
|
+
```dart
|
284
|
+
class PrettyBottomNavigation extends StatelessWidget {
|
285
|
+
const PrettyBottomNavigation({
|
286
|
+
super.key,
|
287
|
+
required this.currentIndex,
|
288
|
+
required this.onTap,
|
289
|
+
required this.items,
|
290
|
+
});
|
291
|
+
|
292
|
+
final int currentIndex;
|
293
|
+
final ValueChanged<int> onTap;
|
294
|
+
final List<PrettyBottomNavItem> items;
|
295
|
+
|
296
|
+
@override
|
297
|
+
Widget build(BuildContext context) {
|
298
|
+
return Container(
|
299
|
+
margin: const EdgeInsets.all(16.0),
|
300
|
+
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
301
|
+
decoration: BoxDecoration(
|
302
|
+
color: Colors.white,
|
303
|
+
borderRadius: BorderRadius.circular(24.0),
|
304
|
+
boxShadow: [
|
305
|
+
BoxShadow(
|
306
|
+
color: Colors.black.withOpacity(0.1),
|
307
|
+
blurRadius: 20.0,
|
308
|
+
offset: const Offset(0, 8),
|
309
|
+
),
|
310
|
+
],
|
311
|
+
),
|
312
|
+
child: Row(
|
313
|
+
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
314
|
+
children: items.asMap().entries.map((entry) {
|
315
|
+
final index = entry.key;
|
316
|
+
final item = entry.value;
|
317
|
+
final isSelected = index == currentIndex;
|
318
|
+
|
319
|
+
return GestureDetector(
|
320
|
+
onTap: () => onTap(index),
|
321
|
+
child: AnimatedContainer(
|
322
|
+
duration: const Duration(milliseconds: 200),
|
323
|
+
curve: Curves.easeInOut,
|
324
|
+
padding: const EdgeInsets.symmetric(
|
325
|
+
horizontal: 16.0,
|
326
|
+
vertical: 8.0,
|
327
|
+
),
|
328
|
+
decoration: BoxDecoration(
|
329
|
+
color: isSelected
|
330
|
+
? Theme.of(context).primaryColor.withOpacity(0.1)
|
331
|
+
: Colors.transparent,
|
332
|
+
borderRadius: BorderRadius.circular(16.0),
|
333
|
+
),
|
334
|
+
child: Row(
|
335
|
+
mainAxisSize: MainAxisSize.min,
|
336
|
+
children: [
|
337
|
+
Icon(
|
338
|
+
item.icon,
|
339
|
+
color: isSelected
|
340
|
+
? Theme.of(context).primaryColor
|
341
|
+
: Colors.grey,
|
342
|
+
),
|
343
|
+
if (isSelected) ...[
|
344
|
+
const SizedBox(width: 8.0),
|
345
|
+
Text(
|
346
|
+
item.label,
|
347
|
+
style: TextStyle(
|
348
|
+
color: Theme.of(context).primaryColor,
|
349
|
+
fontWeight: FontWeight.w600,
|
350
|
+
),
|
351
|
+
),
|
352
|
+
],
|
353
|
+
],
|
354
|
+
),
|
355
|
+
),
|
356
|
+
);
|
357
|
+
}).toList(),
|
358
|
+
),
|
359
|
+
);
|
360
|
+
}
|
361
|
+
}
|
362
|
+
|
363
|
+
class PrettyBottomNavItem {
|
364
|
+
const PrettyBottomNavItem({
|
365
|
+
required this.icon,
|
366
|
+
required this.label,
|
367
|
+
});
|
368
|
+
|
369
|
+
final IconData icon;
|
370
|
+
final String label;
|
371
|
+
}
|
372
|
+
```
|
373
|
+
|
374
|
+
## Animation Examples
|
375
|
+
|
376
|
+
### Staggered List Animation
|
377
|
+
```dart
|
378
|
+
class StaggeredListView extends StatefulWidget {
|
379
|
+
const StaggeredListView({
|
380
|
+
super.key,
|
381
|
+
required this.children,
|
382
|
+
});
|
383
|
+
|
384
|
+
final List<Widget> children;
|
385
|
+
|
386
|
+
@override
|
387
|
+
State<StaggeredListView> createState() => _StaggeredListViewState();
|
388
|
+
}
|
389
|
+
|
390
|
+
class _StaggeredListViewState extends State<StaggeredListView>
|
391
|
+
with TickerProviderStateMixin {
|
392
|
+
late AnimationController _controller;
|
393
|
+
List<Animation<double>> _animations = [];
|
394
|
+
|
395
|
+
@override
|
396
|
+
void initState() {
|
397
|
+
super.initState();
|
398
|
+
_controller = AnimationController(
|
399
|
+
duration: Duration(milliseconds: widget.children.length * 100),
|
400
|
+
vsync: this,
|
401
|
+
);
|
402
|
+
|
403
|
+
_animations = widget.children.map((child) {
|
404
|
+
final index = widget.children.indexOf(child);
|
405
|
+
return Tween<double>(
|
406
|
+
begin: 0.0,
|
407
|
+
end: 1.0,
|
408
|
+
).animate(
|
409
|
+
CurvedAnimation(
|
410
|
+
parent: _controller,
|
411
|
+
curve: Interval(
|
412
|
+
index * 0.1,
|
413
|
+
(index * 0.1) + 0.5,
|
414
|
+
curve: Curves.easeOutBack,
|
415
|
+
),
|
416
|
+
),
|
417
|
+
);
|
418
|
+
}).toList();
|
419
|
+
|
420
|
+
_controller.forward();
|
421
|
+
}
|
422
|
+
|
423
|
+
@override
|
424
|
+
Widget build(BuildContext context) {
|
425
|
+
return ListView.builder(
|
426
|
+
itemCount: widget.children.length,
|
427
|
+
itemBuilder: (context, index) {
|
428
|
+
return AnimatedBuilder(
|
429
|
+
animation: _animations[index],
|
430
|
+
builder: (context, child) {
|
431
|
+
return Transform.translate(
|
432
|
+
offset: Offset(0, 50 * (1 - _animations[index].value)),
|
433
|
+
child: Opacity(
|
434
|
+
opacity: _animations[index].value,
|
435
|
+
child: widget.children[index],
|
436
|
+
),
|
437
|
+
);
|
438
|
+
},
|
439
|
+
);
|
440
|
+
},
|
441
|
+
);
|
442
|
+
}
|
443
|
+
|
444
|
+
@override
|
445
|
+
void dispose() {
|
446
|
+
_controller.dispose();
|
447
|
+
super.dispose();
|
448
|
+
}
|
449
|
+
}
|
450
|
+
```
|
451
|
+
|
452
|
+
### Floating Action Button with Morphing
|
453
|
+
```dart
|
454
|
+
class MorphingFAB extends StatefulWidget {
|
455
|
+
const MorphingFAB({super.key});
|
456
|
+
|
457
|
+
@override
|
458
|
+
State<MorphingFAB> createState() => _MorphingFABState();
|
459
|
+
}
|
460
|
+
|
461
|
+
class _MorphingFABState extends State<MorphingFAB>
|
462
|
+
with SingleTickerProviderStateMixin {
|
463
|
+
late AnimationController _controller;
|
464
|
+
late Animation<double> _scaleAnimation;
|
465
|
+
late Animation<double> _rotationAnimation;
|
466
|
+
|
467
|
+
bool _isExpanded = false;
|
468
|
+
|
469
|
+
@override
|
470
|
+
void initState() {
|
471
|
+
super.initState();
|
472
|
+
_controller = AnimationController(
|
473
|
+
duration: const Duration(milliseconds: 300),
|
474
|
+
vsync: this,
|
475
|
+
);
|
476
|
+
|
477
|
+
_scaleAnimation = Tween<double>(
|
478
|
+
begin: 1.0,
|
479
|
+
end: 1.2,
|
480
|
+
).animate(CurvedAnimation(
|
481
|
+
parent: _controller,
|
482
|
+
curve: Curves.easeInOut,
|
483
|
+
));
|
484
|
+
|
485
|
+
_rotationAnimation = Tween<double>(
|
486
|
+
begin: 0.0,
|
487
|
+
end: 0.125, // 45 degrees
|
488
|
+
).animate(CurvedAnimation(
|
489
|
+
parent: _controller,
|
490
|
+
curve: Curves.easeInOut,
|
491
|
+
));
|
492
|
+
}
|
493
|
+
|
494
|
+
void _toggle() {
|
495
|
+
setState(() {
|
496
|
+
_isExpanded = !_isExpanded;
|
497
|
+
});
|
498
|
+
|
499
|
+
if (_isExpanded) {
|
500
|
+
_controller.forward();
|
501
|
+
} else {
|
502
|
+
_controller.reverse();
|
503
|
+
}
|
504
|
+
}
|
505
|
+
|
506
|
+
@override
|
507
|
+
Widget build(BuildContext context) {
|
508
|
+
return AnimatedBuilder(
|
509
|
+
animation: _controller,
|
510
|
+
builder: (context, child) {
|
511
|
+
return Transform.scale(
|
512
|
+
scale: _scaleAnimation.value,
|
513
|
+
child: Transform.rotate(
|
514
|
+
angle: _rotationAnimation.value * 2 * 3.14159,
|
515
|
+
child: FloatingActionButton(
|
516
|
+
onPressed: _toggle,
|
517
|
+
child: Icon(_isExpanded ? Icons.close : Icons.add),
|
518
|
+
),
|
519
|
+
),
|
520
|
+
);
|
521
|
+
},
|
522
|
+
);
|
523
|
+
}
|
524
|
+
|
525
|
+
@override
|
526
|
+
void dispose() {
|
527
|
+
_controller.dispose();
|
528
|
+
super.dispose();
|
529
|
+
}
|
530
|
+
}
|
531
|
+
```
|
532
|
+
|
533
|
+
## Performance Optimization Examples
|
534
|
+
|
535
|
+
### Efficient List with const Widgets
|
536
|
+
```dart
|
537
|
+
class OptimizedListItem extends StatelessWidget {
|
538
|
+
const OptimizedListItem({
|
539
|
+
super.key,
|
540
|
+
required this.title,
|
541
|
+
required this.subtitle,
|
542
|
+
required this.imageUrl,
|
543
|
+
});
|
544
|
+
|
545
|
+
final String title;
|
546
|
+
final String subtitle;
|
547
|
+
final String imageUrl;
|
548
|
+
|
549
|
+
@override
|
550
|
+
Widget build(BuildContext context) {
|
551
|
+
return const Card(
|
552
|
+
margin: EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
|
553
|
+
child: ListTile(
|
554
|
+
leading: _OptimizedAvatar(),
|
555
|
+
title: _OptimizedTitle(),
|
556
|
+
subtitle: _OptimizedSubtitle(),
|
557
|
+
trailing: Icon(Icons.chevron_right),
|
558
|
+
),
|
559
|
+
);
|
560
|
+
}
|
561
|
+
}
|
562
|
+
|
563
|
+
class _OptimizedAvatar extends StatelessWidget {
|
564
|
+
const _OptimizedAvatar();
|
565
|
+
|
566
|
+
@override
|
567
|
+
Widget build(BuildContext context) {
|
568
|
+
return const CircleAvatar(
|
569
|
+
radius: 24.0,
|
570
|
+
backgroundImage: NetworkImage('https://example.com/avatar.jpg'),
|
571
|
+
);
|
572
|
+
}
|
573
|
+
}
|
574
|
+
|
575
|
+
class _OptimizedTitle extends StatelessWidget {
|
576
|
+
const _OptimizedTitle();
|
577
|
+
|
578
|
+
@override
|
579
|
+
Widget build(BuildContext context) {
|
580
|
+
return const Text(
|
581
|
+
'Title Text',
|
582
|
+
style: TextStyle(fontWeight: FontWeight.w600),
|
583
|
+
);
|
584
|
+
}
|
585
|
+
}
|
586
|
+
|
587
|
+
class _OptimizedSubtitle extends StatelessWidget {
|
588
|
+
const _OptimizedSubtitle();
|
589
|
+
|
590
|
+
@override
|
591
|
+
Widget build(BuildContext context) {
|
592
|
+
return const Text('Subtitle text');
|
593
|
+
}
|
594
|
+
}
|
595
|
+
```
|
596
|
+
|
597
|
+
These examples demonstrate modern Flutter UI design following Pretty UI principles with platform-adaptive components, smooth animations, and performance optimizations.
|