neoagent 2.3.1-beta.57 → 2.3.1-beta.59
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/flutter_app/lib/features/onboarding/onboarding_chrome.dart +569 -0
- package/flutter_app/lib/features/onboarding/onboarding_messaging_step.dart +125 -230
- package/flutter_app/lib/features/onboarding/onboarding_model_step.dart +183 -248
- package/flutter_app/lib/features/onboarding/onboarding_shell.dart +3 -5
- package/flutter_app/lib/features/onboarding/onboarding_video_step.dart +140 -107
- package/flutter_app/lib/features/onboarding/onboarding_welcome_step.dart +32 -150
- package/flutter_app/lib/main_app_shell.dart +439 -19
- package/flutter_app/lib/main_controller.dart +12 -6
- package/flutter_app/lib/main_devices.dart +204 -109
- package/flutter_app/lib/main_settings.dart +16 -77
- package/flutter_app/lib/main_shared.dart +1 -0
- package/package.json +1 -1
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +75022 -74341
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
import 'dart:ui' show ImageFilter;
|
|
2
|
+
|
|
3
|
+
import 'package:flutter/material.dart';
|
|
4
|
+
|
|
5
|
+
class OnboardingScaffold extends StatelessWidget {
|
|
6
|
+
const OnboardingScaffold({
|
|
7
|
+
super.key,
|
|
8
|
+
required this.step,
|
|
9
|
+
required this.totalSteps,
|
|
10
|
+
required this.eyebrow,
|
|
11
|
+
required this.title,
|
|
12
|
+
required this.description,
|
|
13
|
+
required this.child,
|
|
14
|
+
required this.footer,
|
|
15
|
+
this.sidePanel,
|
|
16
|
+
this.dense = false,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
final int step;
|
|
20
|
+
final int totalSteps;
|
|
21
|
+
final String eyebrow;
|
|
22
|
+
final String title;
|
|
23
|
+
final String description;
|
|
24
|
+
final Widget child;
|
|
25
|
+
final Widget footer;
|
|
26
|
+
final Widget? sidePanel;
|
|
27
|
+
final bool dense;
|
|
28
|
+
|
|
29
|
+
@override
|
|
30
|
+
Widget build(BuildContext context) {
|
|
31
|
+
final width = MediaQuery.sizeOf(context).width;
|
|
32
|
+
final compact = width < 980;
|
|
33
|
+
return Stack(
|
|
34
|
+
children: <Widget>[
|
|
35
|
+
const Positioned.fill(child: _OnboardingBackdrop()),
|
|
36
|
+
SafeArea(
|
|
37
|
+
child: Padding(
|
|
38
|
+
padding: EdgeInsets.fromLTRB(
|
|
39
|
+
compact ? 20 : 32,
|
|
40
|
+
compact ? 16 : 22,
|
|
41
|
+
compact ? 20 : 32,
|
|
42
|
+
compact ? 20 : 28,
|
|
43
|
+
),
|
|
44
|
+
child: Column(
|
|
45
|
+
children: <Widget>[
|
|
46
|
+
_OnboardingTopBar(step: step, totalSteps: totalSteps),
|
|
47
|
+
const SizedBox(height: 18),
|
|
48
|
+
Expanded(
|
|
49
|
+
child: compact
|
|
50
|
+
? _OnboardingPanel(
|
|
51
|
+
padding: EdgeInsets.all(dense ? 22 : 26),
|
|
52
|
+
child: _OnboardingContentColumn(
|
|
53
|
+
eyebrow: eyebrow,
|
|
54
|
+
title: title,
|
|
55
|
+
description: description,
|
|
56
|
+
footer: footer,
|
|
57
|
+
sidePanel: sidePanel,
|
|
58
|
+
compact: true,
|
|
59
|
+
child: child,
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
: _OnboardingPanel(
|
|
63
|
+
padding: const EdgeInsets.all(34),
|
|
64
|
+
child: _OnboardingContentColumn(
|
|
65
|
+
eyebrow: eyebrow,
|
|
66
|
+
title: title,
|
|
67
|
+
description: description,
|
|
68
|
+
footer: footer,
|
|
69
|
+
sidePanel: sidePanel,
|
|
70
|
+
compact: false,
|
|
71
|
+
child: child,
|
|
72
|
+
),
|
|
73
|
+
),
|
|
74
|
+
),
|
|
75
|
+
],
|
|
76
|
+
),
|
|
77
|
+
),
|
|
78
|
+
),
|
|
79
|
+
],
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
class OnboardingPanel extends StatelessWidget {
|
|
85
|
+
const OnboardingPanel({
|
|
86
|
+
super.key,
|
|
87
|
+
required this.child,
|
|
88
|
+
this.padding = const EdgeInsets.all(24),
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
final Widget child;
|
|
92
|
+
final EdgeInsetsGeometry padding;
|
|
93
|
+
|
|
94
|
+
@override
|
|
95
|
+
Widget build(BuildContext context) {
|
|
96
|
+
return _OnboardingPanel(padding: padding, child: child);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
class OnboardingOptionCard extends StatelessWidget {
|
|
101
|
+
const OnboardingOptionCard({
|
|
102
|
+
super.key,
|
|
103
|
+
required this.child,
|
|
104
|
+
required this.selected,
|
|
105
|
+
this.onTap,
|
|
106
|
+
this.accent,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
final Widget child;
|
|
110
|
+
final bool selected;
|
|
111
|
+
final VoidCallback? onTap;
|
|
112
|
+
final Color? accent;
|
|
113
|
+
|
|
114
|
+
@override
|
|
115
|
+
Widget build(BuildContext context) {
|
|
116
|
+
final highlight = accent ?? Theme.of(context).colorScheme.primary;
|
|
117
|
+
return Material(
|
|
118
|
+
color: Colors.transparent,
|
|
119
|
+
child: InkWell(
|
|
120
|
+
onTap: onTap,
|
|
121
|
+
borderRadius: BorderRadius.circular(28),
|
|
122
|
+
child: AnimatedContainer(
|
|
123
|
+
duration: const Duration(milliseconds: 260),
|
|
124
|
+
curve: Curves.easeOutCubic,
|
|
125
|
+
padding: const EdgeInsets.all(22),
|
|
126
|
+
decoration: BoxDecoration(
|
|
127
|
+
color: selected
|
|
128
|
+
? highlight.withValues(alpha: 0.16)
|
|
129
|
+
: Colors.white.withValues(alpha: 0.055),
|
|
130
|
+
borderRadius: BorderRadius.circular(28),
|
|
131
|
+
border: Border.all(
|
|
132
|
+
color: selected
|
|
133
|
+
? highlight.withValues(alpha: 0.92)
|
|
134
|
+
: Colors.white.withValues(alpha: 0.1),
|
|
135
|
+
width: selected ? 1.8 : 1,
|
|
136
|
+
),
|
|
137
|
+
boxShadow: selected
|
|
138
|
+
? <BoxShadow>[
|
|
139
|
+
BoxShadow(
|
|
140
|
+
color: highlight.withValues(alpha: 0.18),
|
|
141
|
+
blurRadius: 28,
|
|
142
|
+
offset: const Offset(0, 14),
|
|
143
|
+
),
|
|
144
|
+
]
|
|
145
|
+
: const <BoxShadow>[],
|
|
146
|
+
),
|
|
147
|
+
child: child,
|
|
148
|
+
),
|
|
149
|
+
),
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
class OnboardingGhostButton extends StatelessWidget {
|
|
155
|
+
const OnboardingGhostButton({
|
|
156
|
+
super.key,
|
|
157
|
+
required this.label,
|
|
158
|
+
required this.onPressed,
|
|
159
|
+
this.icon,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
final String label;
|
|
163
|
+
final VoidCallback? onPressed;
|
|
164
|
+
final IconData? icon;
|
|
165
|
+
|
|
166
|
+
@override
|
|
167
|
+
Widget build(BuildContext context) {
|
|
168
|
+
return TextButton.icon(
|
|
169
|
+
onPressed: onPressed,
|
|
170
|
+
style: TextButton.styleFrom(
|
|
171
|
+
foregroundColor: Colors.white.withValues(alpha: 0.72),
|
|
172
|
+
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
|
173
|
+
textStyle: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
|
|
174
|
+
),
|
|
175
|
+
icon: icon == null ? const SizedBox.shrink() : Icon(icon, size: 18),
|
|
176
|
+
label: Text(label),
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
class OnboardingPrimaryButton extends StatelessWidget {
|
|
182
|
+
const OnboardingPrimaryButton({
|
|
183
|
+
super.key,
|
|
184
|
+
required this.label,
|
|
185
|
+
required this.onPressed,
|
|
186
|
+
this.icon,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
final String label;
|
|
190
|
+
final VoidCallback? onPressed;
|
|
191
|
+
final IconData? icon;
|
|
192
|
+
|
|
193
|
+
@override
|
|
194
|
+
Widget build(BuildContext context) {
|
|
195
|
+
final accent = Theme.of(context).colorScheme.primary;
|
|
196
|
+
return Container(
|
|
197
|
+
decoration: BoxDecoration(
|
|
198
|
+
borderRadius: BorderRadius.circular(22),
|
|
199
|
+
boxShadow: <BoxShadow>[
|
|
200
|
+
BoxShadow(
|
|
201
|
+
color: accent.withValues(alpha: 0.26),
|
|
202
|
+
blurRadius: 26,
|
|
203
|
+
offset: const Offset(0, 14),
|
|
204
|
+
),
|
|
205
|
+
],
|
|
206
|
+
),
|
|
207
|
+
child: FilledButton.icon(
|
|
208
|
+
onPressed: onPressed,
|
|
209
|
+
style: FilledButton.styleFrom(
|
|
210
|
+
backgroundColor: accent,
|
|
211
|
+
foregroundColor: Colors.white,
|
|
212
|
+
padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 20),
|
|
213
|
+
shape: RoundedRectangleBorder(
|
|
214
|
+
borderRadius: BorderRadius.circular(22),
|
|
215
|
+
),
|
|
216
|
+
textStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.w700),
|
|
217
|
+
),
|
|
218
|
+
icon: icon == null ? const SizedBox.shrink() : Icon(icon, size: 18),
|
|
219
|
+
label: Text(label),
|
|
220
|
+
),
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
class OnboardingMetricPill extends StatelessWidget {
|
|
226
|
+
const OnboardingMetricPill({
|
|
227
|
+
super.key,
|
|
228
|
+
required this.label,
|
|
229
|
+
required this.value,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
final String label;
|
|
233
|
+
final String value;
|
|
234
|
+
|
|
235
|
+
@override
|
|
236
|
+
Widget build(BuildContext context) {
|
|
237
|
+
return Container(
|
|
238
|
+
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
|
239
|
+
decoration: BoxDecoration(
|
|
240
|
+
color: Colors.white.withValues(alpha: 0.06),
|
|
241
|
+
borderRadius: BorderRadius.circular(18),
|
|
242
|
+
border: Border.all(color: Colors.white.withValues(alpha: 0.08)),
|
|
243
|
+
),
|
|
244
|
+
child: Column(
|
|
245
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
246
|
+
mainAxisSize: MainAxisSize.min,
|
|
247
|
+
children: <Widget>[
|
|
248
|
+
Text(
|
|
249
|
+
label.toUpperCase(),
|
|
250
|
+
style: TextStyle(
|
|
251
|
+
color: Colors.white.withValues(alpha: 0.56),
|
|
252
|
+
fontSize: 11,
|
|
253
|
+
fontWeight: FontWeight.w700,
|
|
254
|
+
letterSpacing: 0.9,
|
|
255
|
+
),
|
|
256
|
+
),
|
|
257
|
+
const SizedBox(height: 6),
|
|
258
|
+
Text(
|
|
259
|
+
value,
|
|
260
|
+
style: const TextStyle(
|
|
261
|
+
color: Colors.white,
|
|
262
|
+
fontSize: 15,
|
|
263
|
+
fontWeight: FontWeight.w700,
|
|
264
|
+
),
|
|
265
|
+
),
|
|
266
|
+
],
|
|
267
|
+
),
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
class _OnboardingTopBar extends StatelessWidget {
|
|
273
|
+
const _OnboardingTopBar({required this.step, required this.totalSteps});
|
|
274
|
+
|
|
275
|
+
final int step;
|
|
276
|
+
final int totalSteps;
|
|
277
|
+
|
|
278
|
+
@override
|
|
279
|
+
Widget build(BuildContext context) {
|
|
280
|
+
final progress = totalSteps <= 1 ? 1.0 : (step + 1) / totalSteps;
|
|
281
|
+
return Row(
|
|
282
|
+
children: <Widget>[
|
|
283
|
+
Container(
|
|
284
|
+
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
|
|
285
|
+
decoration: BoxDecoration(
|
|
286
|
+
color: Colors.white.withValues(alpha: 0.06),
|
|
287
|
+
borderRadius: BorderRadius.circular(999),
|
|
288
|
+
border: Border.all(color: Colors.white.withValues(alpha: 0.08)),
|
|
289
|
+
),
|
|
290
|
+
child: Row(
|
|
291
|
+
mainAxisSize: MainAxisSize.min,
|
|
292
|
+
children: <Widget>[
|
|
293
|
+
Container(
|
|
294
|
+
width: 10,
|
|
295
|
+
height: 10,
|
|
296
|
+
decoration: BoxDecoration(
|
|
297
|
+
color: Theme.of(context).colorScheme.primary,
|
|
298
|
+
shape: BoxShape.circle,
|
|
299
|
+
),
|
|
300
|
+
),
|
|
301
|
+
const SizedBox(width: 10),
|
|
302
|
+
const Text(
|
|
303
|
+
'NeoOS Setup',
|
|
304
|
+
style: TextStyle(
|
|
305
|
+
color: Colors.white,
|
|
306
|
+
fontSize: 14,
|
|
307
|
+
fontWeight: FontWeight.w700,
|
|
308
|
+
),
|
|
309
|
+
),
|
|
310
|
+
],
|
|
311
|
+
),
|
|
312
|
+
),
|
|
313
|
+
const SizedBox(width: 16),
|
|
314
|
+
Expanded(
|
|
315
|
+
child: ClipRRect(
|
|
316
|
+
borderRadius: BorderRadius.circular(999),
|
|
317
|
+
child: TweenAnimationBuilder<double>(
|
|
318
|
+
tween: Tween<double>(begin: 0, end: progress),
|
|
319
|
+
duration: const Duration(milliseconds: 700),
|
|
320
|
+
curve: Curves.easeOutCubic,
|
|
321
|
+
builder: (context, animatedValue, _) {
|
|
322
|
+
return LinearProgressIndicator(
|
|
323
|
+
value: animatedValue,
|
|
324
|
+
minHeight: 8,
|
|
325
|
+
backgroundColor: Colors.white.withValues(alpha: 0.08),
|
|
326
|
+
valueColor: AlwaysStoppedAnimation<Color>(
|
|
327
|
+
Theme.of(context).colorScheme.primary,
|
|
328
|
+
),
|
|
329
|
+
);
|
|
330
|
+
},
|
|
331
|
+
),
|
|
332
|
+
),
|
|
333
|
+
),
|
|
334
|
+
const SizedBox(width: 16),
|
|
335
|
+
Text(
|
|
336
|
+
'${step + 1} / $totalSteps',
|
|
337
|
+
style: TextStyle(
|
|
338
|
+
color: Colors.white.withValues(alpha: 0.7),
|
|
339
|
+
fontSize: 13,
|
|
340
|
+
fontWeight: FontWeight.w700,
|
|
341
|
+
),
|
|
342
|
+
),
|
|
343
|
+
],
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
class _OnboardingContentColumn extends StatelessWidget {
|
|
349
|
+
const _OnboardingContentColumn({
|
|
350
|
+
required this.eyebrow,
|
|
351
|
+
required this.title,
|
|
352
|
+
required this.description,
|
|
353
|
+
required this.child,
|
|
354
|
+
required this.footer,
|
|
355
|
+
required this.sidePanel,
|
|
356
|
+
required this.compact,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
final String eyebrow;
|
|
360
|
+
final String title;
|
|
361
|
+
final String description;
|
|
362
|
+
final Widget child;
|
|
363
|
+
final Widget footer;
|
|
364
|
+
final Widget? sidePanel;
|
|
365
|
+
final bool compact;
|
|
366
|
+
|
|
367
|
+
@override
|
|
368
|
+
Widget build(BuildContext context) {
|
|
369
|
+
final intro = Column(
|
|
370
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
371
|
+
children: <Widget>[
|
|
372
|
+
Text(
|
|
373
|
+
eyebrow,
|
|
374
|
+
style: TextStyle(
|
|
375
|
+
color: Theme.of(
|
|
376
|
+
context,
|
|
377
|
+
).colorScheme.primary.withValues(alpha: 0.92),
|
|
378
|
+
fontSize: 12,
|
|
379
|
+
fontWeight: FontWeight.w800,
|
|
380
|
+
letterSpacing: 1.1,
|
|
381
|
+
),
|
|
382
|
+
),
|
|
383
|
+
const SizedBox(height: 14),
|
|
384
|
+
Text(
|
|
385
|
+
title,
|
|
386
|
+
style: TextStyle(
|
|
387
|
+
color: Colors.white,
|
|
388
|
+
fontSize: compact ? 40 : 56,
|
|
389
|
+
height: compact ? 1.04 : 1.0,
|
|
390
|
+
fontWeight: FontWeight.w800,
|
|
391
|
+
letterSpacing: compact ? -1.6 : -2.3,
|
|
392
|
+
),
|
|
393
|
+
),
|
|
394
|
+
const SizedBox(height: 18),
|
|
395
|
+
ConstrainedBox(
|
|
396
|
+
constraints: const BoxConstraints(maxWidth: 680),
|
|
397
|
+
child: Text(
|
|
398
|
+
description,
|
|
399
|
+
style: TextStyle(
|
|
400
|
+
color: Colors.white.withValues(alpha: 0.74),
|
|
401
|
+
fontSize: compact ? 17 : 19,
|
|
402
|
+
height: 1.55,
|
|
403
|
+
fontWeight: FontWeight.w500,
|
|
404
|
+
),
|
|
405
|
+
),
|
|
406
|
+
),
|
|
407
|
+
],
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
if (compact) {
|
|
411
|
+
return Column(
|
|
412
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
413
|
+
children: <Widget>[
|
|
414
|
+
intro,
|
|
415
|
+
const SizedBox(height: 26),
|
|
416
|
+
if (sidePanel != null) ...<Widget>[
|
|
417
|
+
sidePanel!,
|
|
418
|
+
const SizedBox(height: 18),
|
|
419
|
+
],
|
|
420
|
+
Expanded(child: child),
|
|
421
|
+
const SizedBox(height: 20),
|
|
422
|
+
footer,
|
|
423
|
+
],
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return Column(
|
|
428
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
429
|
+
children: <Widget>[
|
|
430
|
+
Row(
|
|
431
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
432
|
+
children: <Widget>[
|
|
433
|
+
Expanded(flex: 12, child: intro),
|
|
434
|
+
if (sidePanel != null) ...<Widget>[
|
|
435
|
+
const SizedBox(width: 28),
|
|
436
|
+
Expanded(flex: 7, child: sidePanel!),
|
|
437
|
+
],
|
|
438
|
+
],
|
|
439
|
+
),
|
|
440
|
+
const SizedBox(height: 28),
|
|
441
|
+
Expanded(child: child),
|
|
442
|
+
const SizedBox(height: 22),
|
|
443
|
+
footer,
|
|
444
|
+
],
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
class _OnboardingPanel extends StatelessWidget {
|
|
450
|
+
const _OnboardingPanel({required this.child, required this.padding});
|
|
451
|
+
|
|
452
|
+
final Widget child;
|
|
453
|
+
final EdgeInsetsGeometry padding;
|
|
454
|
+
|
|
455
|
+
@override
|
|
456
|
+
Widget build(BuildContext context) {
|
|
457
|
+
return ClipRRect(
|
|
458
|
+
borderRadius: BorderRadius.circular(36),
|
|
459
|
+
child: BackdropFilter(
|
|
460
|
+
filter: ImageFilter.blur(sigmaX: 30, sigmaY: 30),
|
|
461
|
+
child: Container(
|
|
462
|
+
padding: padding,
|
|
463
|
+
decoration: BoxDecoration(
|
|
464
|
+
gradient: LinearGradient(
|
|
465
|
+
colors: <Color>[
|
|
466
|
+
Colors.white.withValues(alpha: 0.16),
|
|
467
|
+
Colors.white.withValues(alpha: 0.08),
|
|
468
|
+
const Color(0xFF111317).withValues(alpha: 0.72),
|
|
469
|
+
],
|
|
470
|
+
begin: Alignment.topLeft,
|
|
471
|
+
end: Alignment.bottomRight,
|
|
472
|
+
),
|
|
473
|
+
borderRadius: BorderRadius.circular(36),
|
|
474
|
+
border: Border.all(color: Colors.white.withValues(alpha: 0.14)),
|
|
475
|
+
boxShadow: <BoxShadow>[
|
|
476
|
+
BoxShadow(
|
|
477
|
+
color: Colors.black.withValues(alpha: 0.34),
|
|
478
|
+
blurRadius: 46,
|
|
479
|
+
offset: const Offset(0, 24),
|
|
480
|
+
),
|
|
481
|
+
],
|
|
482
|
+
),
|
|
483
|
+
child: child,
|
|
484
|
+
),
|
|
485
|
+
),
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
class _OnboardingBackdrop extends StatelessWidget {
|
|
491
|
+
const _OnboardingBackdrop();
|
|
492
|
+
|
|
493
|
+
@override
|
|
494
|
+
Widget build(BuildContext context) {
|
|
495
|
+
final accent = Theme.of(context).colorScheme.primary;
|
|
496
|
+
return DecoratedBox(
|
|
497
|
+
decoration: const BoxDecoration(
|
|
498
|
+
gradient: RadialGradient(
|
|
499
|
+
center: Alignment(-0.8, -0.95),
|
|
500
|
+
radius: 1.8,
|
|
501
|
+
colors: <Color>[
|
|
502
|
+
Color(0xFF20242C),
|
|
503
|
+
Color(0xFF0C0F13),
|
|
504
|
+
Color(0xFF040506),
|
|
505
|
+
],
|
|
506
|
+
),
|
|
507
|
+
),
|
|
508
|
+
child: Stack(
|
|
509
|
+
children: <Widget>[
|
|
510
|
+
Positioned(
|
|
511
|
+
top: -120,
|
|
512
|
+
left: -80,
|
|
513
|
+
child: _GlowOrb(size: 340, color: accent.withValues(alpha: 0.24)),
|
|
514
|
+
),
|
|
515
|
+
const Positioned(
|
|
516
|
+
top: 120,
|
|
517
|
+
right: -60,
|
|
518
|
+
child: _GlowOrb(size: 300, color: Color(0x226EDBFF)),
|
|
519
|
+
),
|
|
520
|
+
const Positioned(
|
|
521
|
+
bottom: -120,
|
|
522
|
+
left: 160,
|
|
523
|
+
child: _GlowOrb(size: 420, color: Color(0x18D7B27C)),
|
|
524
|
+
),
|
|
525
|
+
Positioned.fill(
|
|
526
|
+
child: IgnorePointer(
|
|
527
|
+
child: DecoratedBox(
|
|
528
|
+
decoration: BoxDecoration(
|
|
529
|
+
gradient: LinearGradient(
|
|
530
|
+
colors: <Color>[
|
|
531
|
+
Colors.white.withValues(alpha: 0.04),
|
|
532
|
+
Colors.transparent,
|
|
533
|
+
Colors.black.withValues(alpha: 0.26),
|
|
534
|
+
],
|
|
535
|
+
begin: Alignment.topCenter,
|
|
536
|
+
end: Alignment.bottomCenter,
|
|
537
|
+
),
|
|
538
|
+
),
|
|
539
|
+
),
|
|
540
|
+
),
|
|
541
|
+
),
|
|
542
|
+
],
|
|
543
|
+
),
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
class _GlowOrb extends StatelessWidget {
|
|
549
|
+
const _GlowOrb({required this.size, required this.color});
|
|
550
|
+
|
|
551
|
+
final double size;
|
|
552
|
+
final Color color;
|
|
553
|
+
|
|
554
|
+
@override
|
|
555
|
+
Widget build(BuildContext context) {
|
|
556
|
+
return IgnorePointer(
|
|
557
|
+
child: Container(
|
|
558
|
+
width: size,
|
|
559
|
+
height: size,
|
|
560
|
+
decoration: BoxDecoration(
|
|
561
|
+
shape: BoxShape.circle,
|
|
562
|
+
boxShadow: <BoxShadow>[
|
|
563
|
+
BoxShadow(color: color, blurRadius: 160, spreadRadius: 28),
|
|
564
|
+
],
|
|
565
|
+
),
|
|
566
|
+
),
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
}
|