neoagent 2.4.1-beta.19 → 2.4.1-beta.21
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/README.md +4 -1
- package/docs/getting-started.md +9 -3
- package/flutter_app/assets/branding/app_icon_light_1024.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_128.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_192.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_256.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_32.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_512.png +0 -0
- package/flutter_app/assets/branding/app_icon_light_64.png +0 -0
- package/flutter_app/assets/branding/tray_icon_light_template.png +0 -0
- package/flutter_app/lib/features/location/location_service.dart +3 -0
- package/flutter_app/lib/main.dart +1 -0
- package/flutter_app/lib/main_account_settings.dart +9 -33
- package/flutter_app/lib/main_app_shell.dart +237 -197
- package/flutter_app/lib/main_controller.dart +0 -25
- package/flutter_app/lib/main_devices.dart +2 -0
- package/flutter_app/lib/main_models.dart +144 -0
- package/flutter_app/lib/main_operations.dart +150 -19
- package/flutter_app/lib/main_shared.dart +642 -195
- package/flutter_app/lib/main_theme.dart +2 -0
- package/flutter_app/lib/src/android_apk_drop_zone_web.dart +3 -1
- package/flutter_app/lib/src/security/password_strength.dart +84 -0
- package/flutter_app/lib/src/theme/palette.dart +15 -15
- package/flutter_app/pubspec.yaml +3 -0
- package/flutter_app/web/favicon_light.svg +3 -0
- package/flutter_app/web/icons/Icon-192-light.png +0 -0
- package/flutter_app/web/icons/Icon-512-light.png +0 -0
- package/flutter_app/web/icons/Icon-maskable-192-light.png +0 -0
- package/flutter_app/web/icons/Icon-maskable-512-light.png +0 -0
- package/lib/manager.js +282 -81
- package/package.json +17 -3
- package/server/config/origins.js +3 -1
- package/server/db/database.js +73 -0
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/AssetManifest.bin +1 -1
- package/server/public/assets/AssetManifest.bin.json +1 -1
- package/server/public/assets/assets/branding/app_icon_light_256.png +0 -0
- package/server/public/assets/assets/branding/app_icon_light_512.png +0 -0
- package/server/public/assets/assets/branding/tray_icon_light_template.png +0 -0
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/favicon_light.svg +3 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/icons/Icon-192-light.png +0 -0
- package/server/public/icons/Icon-512-light.png +0 -0
- package/server/public/icons/Icon-maskable-192-light.png +0 -0
- package/server/public/icons/Icon-maskable-512-light.png +0 -0
- package/server/public/main.dart.js +68769 -68268
- package/server/routes/agent_profiles.js +3 -0
- package/server/routes/memory.js +22 -1
- package/server/services/account/password_policy.js +6 -1
- package/server/services/memory/intelligence.js +181 -0
- package/server/services/memory/manager.js +475 -25
- package/server/utils/security.js +3 -0
- package/server/services/memory/openhuman_uplift.test.js +0 -98
- package/server/utils/version.test.js +0 -39
|
@@ -11,6 +11,8 @@ EdgeInsets _pagePadding(BuildContext context) {
|
|
|
11
11
|
return const EdgeInsets.fromLTRB(20, 20, 20, 28);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
final ValueNotifier<bool> _partyModeEnabled = ValueNotifier<bool>(false);
|
|
15
|
+
|
|
14
16
|
class _AmbientBackdrop extends StatefulWidget {
|
|
15
17
|
const _AmbientBackdrop({required this.child});
|
|
16
18
|
|
|
@@ -41,81 +43,241 @@ class _AmbientBackdropState extends State<_AmbientBackdrop>
|
|
|
41
43
|
|
|
42
44
|
@override
|
|
43
45
|
Widget build(BuildContext context) {
|
|
44
|
-
return
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
color: _accentAlt.withValues(alpha: 0.85),
|
|
66
|
-
),
|
|
67
|
-
),
|
|
68
|
-
Positioned(
|
|
69
|
-
bottom: -140 + (t * 16),
|
|
70
|
-
left: 100 - (t * 24),
|
|
71
|
-
child: _BlurOrb(
|
|
72
|
-
size: 360,
|
|
73
|
-
color: _accent.withValues(alpha: 0.45),
|
|
74
|
-
),
|
|
75
|
-
),
|
|
76
|
-
Positioned.fill(
|
|
77
|
-
child: IgnorePointer(
|
|
78
|
-
child: DecoratedBox(
|
|
79
|
-
decoration: BoxDecoration(
|
|
80
|
-
gradient: LinearGradient(
|
|
81
|
-
colors: <Color>[
|
|
82
|
-
Colors.white.withValues(alpha: 0.05),
|
|
83
|
-
Colors.transparent,
|
|
84
|
-
Colors.black.withValues(alpha: 0.12),
|
|
85
|
-
],
|
|
86
|
-
stops: const <double>[0, 0.32, 1],
|
|
87
|
-
begin: Alignment.topCenter,
|
|
88
|
-
end: Alignment.bottomCenter,
|
|
46
|
+
return ValueListenableBuilder<bool>(
|
|
47
|
+
valueListenable: _partyModeEnabled,
|
|
48
|
+
builder: (context, partyMode, _) {
|
|
49
|
+
return DecoratedBox(
|
|
50
|
+
decoration: BoxDecoration(gradient: _appBackgroundGradient),
|
|
51
|
+
child: AnimatedBuilder(
|
|
52
|
+
animation: _controller,
|
|
53
|
+
builder: (context, _) {
|
|
54
|
+
final t = Curves.easeInOut.transform(_controller.value);
|
|
55
|
+
return Stack(
|
|
56
|
+
children: <Widget>[
|
|
57
|
+
Positioned.fill(
|
|
58
|
+
child: IgnorePointer(
|
|
59
|
+
child: CustomPaint(
|
|
60
|
+
painter: _AuroraFieldPainter(
|
|
61
|
+
progress: t,
|
|
62
|
+
partyMode: partyMode,
|
|
63
|
+
primary: _accent,
|
|
64
|
+
secondary: _accentAlt,
|
|
65
|
+
base: _bgPrimary,
|
|
66
|
+
),
|
|
89
67
|
),
|
|
90
68
|
),
|
|
91
69
|
),
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
radius: 0.95,
|
|
101
|
-
colors: <Color>[
|
|
102
|
-
_glassHighlight.withValues(alpha: 0.14),
|
|
103
|
-
Colors.transparent,
|
|
104
|
-
],
|
|
70
|
+
Positioned.fill(
|
|
71
|
+
child: IgnorePointer(
|
|
72
|
+
child: CustomPaint(
|
|
73
|
+
painter: _ArcadeGridPainter(
|
|
74
|
+
progress: t,
|
|
75
|
+
color: _accentAlt,
|
|
76
|
+
partyMode: partyMode,
|
|
77
|
+
),
|
|
105
78
|
),
|
|
106
79
|
),
|
|
107
80
|
),
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
81
|
+
Positioned.fill(
|
|
82
|
+
child: IgnorePointer(
|
|
83
|
+
child: DecoratedBox(
|
|
84
|
+
decoration: BoxDecoration(
|
|
85
|
+
gradient: LinearGradient(
|
|
86
|
+
colors: <Color>[
|
|
87
|
+
Colors.white.withValues(alpha: 0.05),
|
|
88
|
+
Colors.transparent,
|
|
89
|
+
Colors.black.withValues(alpha: 0.12),
|
|
90
|
+
],
|
|
91
|
+
stops: const <double>[0, 0.32, 1],
|
|
92
|
+
begin: Alignment.topCenter,
|
|
93
|
+
end: Alignment.bottomCenter,
|
|
94
|
+
),
|
|
95
|
+
),
|
|
96
|
+
),
|
|
97
|
+
),
|
|
98
|
+
),
|
|
99
|
+
Positioned.fill(
|
|
100
|
+
child: IgnorePointer(
|
|
101
|
+
child: CustomPaint(
|
|
102
|
+
painter: _ConfettiBitsPainter(
|
|
103
|
+
progress: t,
|
|
104
|
+
active: partyMode,
|
|
105
|
+
primary: _accent,
|
|
106
|
+
secondary: _accentAlt,
|
|
107
|
+
),
|
|
108
|
+
),
|
|
109
|
+
),
|
|
110
|
+
),
|
|
111
|
+
widget.child,
|
|
112
|
+
],
|
|
113
|
+
);
|
|
114
|
+
},
|
|
115
|
+
),
|
|
116
|
+
);
|
|
117
|
+
},
|
|
115
118
|
);
|
|
116
119
|
}
|
|
117
120
|
}
|
|
118
121
|
|
|
122
|
+
class _AuroraFieldPainter extends CustomPainter {
|
|
123
|
+
const _AuroraFieldPainter({
|
|
124
|
+
required this.progress,
|
|
125
|
+
required this.partyMode,
|
|
126
|
+
required this.primary,
|
|
127
|
+
required this.secondary,
|
|
128
|
+
required this.base,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
final double progress;
|
|
132
|
+
final bool partyMode;
|
|
133
|
+
final Color primary;
|
|
134
|
+
final Color secondary;
|
|
135
|
+
final Color base;
|
|
136
|
+
|
|
137
|
+
@override
|
|
138
|
+
void paint(Canvas canvas, Size size) {
|
|
139
|
+
final rect = Offset.zero & size;
|
|
140
|
+
final energy = partyMode ? 1.45 : 1.0;
|
|
141
|
+
final wash = Paint()
|
|
142
|
+
..shader = LinearGradient(
|
|
143
|
+
colors: <Color>[
|
|
144
|
+
primary.withValues(alpha: 0.18 * energy),
|
|
145
|
+
secondary.withValues(alpha: 0.12 * energy),
|
|
146
|
+
base.withValues(alpha: 0),
|
|
147
|
+
],
|
|
148
|
+
stops: const <double>[0, 0.42, 1],
|
|
149
|
+
begin: Alignment(-0.95 + progress * 0.35, -1),
|
|
150
|
+
end: Alignment(0.85 - progress * 0.25, 1),
|
|
151
|
+
).createShader(rect);
|
|
152
|
+
canvas.drawRect(rect, wash);
|
|
153
|
+
|
|
154
|
+
for (var lane = 0; lane < 4; lane++) {
|
|
155
|
+
final yBase = size.height * (0.16 + lane * 0.18);
|
|
156
|
+
final path = Path()..moveTo(-size.width * 0.12, yBase);
|
|
157
|
+
for (var i = 0; i <= 8; i++) {
|
|
158
|
+
final x = size.width * (i / 8);
|
|
159
|
+
final phase = progress * math.pi * 2 + lane * 0.9 + i * 0.28;
|
|
160
|
+
final y = yBase + math.sin(phase) * (28 + lane * 9) * energy;
|
|
161
|
+
path.lineTo(x, y);
|
|
162
|
+
}
|
|
163
|
+
path.lineTo(size.width * 1.12, yBase + size.height * 0.34);
|
|
164
|
+
path.lineTo(-size.width * 0.12, yBase + size.height * 0.28);
|
|
165
|
+
path.close();
|
|
166
|
+
|
|
167
|
+
final lanePaint = Paint()
|
|
168
|
+
..shader = LinearGradient(
|
|
169
|
+
colors: <Color>[
|
|
170
|
+
(lane.isEven ? primary : secondary).withValues(
|
|
171
|
+
alpha: (0.07 + lane * 0.012) * energy,
|
|
172
|
+
),
|
|
173
|
+
Colors.transparent,
|
|
174
|
+
],
|
|
175
|
+
begin: Alignment.topLeft,
|
|
176
|
+
end: Alignment.bottomRight,
|
|
177
|
+
).createShader(rect)
|
|
178
|
+
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 42);
|
|
179
|
+
canvas.drawPath(path, lanePaint);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
@override
|
|
184
|
+
bool shouldRepaint(covariant _AuroraFieldPainter oldDelegate) {
|
|
185
|
+
return oldDelegate.progress != progress ||
|
|
186
|
+
oldDelegate.partyMode != partyMode ||
|
|
187
|
+
oldDelegate.primary != primary ||
|
|
188
|
+
oldDelegate.secondary != secondary ||
|
|
189
|
+
oldDelegate.base != base;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
class _ArcadeGridPainter extends CustomPainter {
|
|
194
|
+
const _ArcadeGridPainter({
|
|
195
|
+
required this.progress,
|
|
196
|
+
required this.color,
|
|
197
|
+
required this.partyMode,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
final double progress;
|
|
201
|
+
final Color color;
|
|
202
|
+
final bool partyMode;
|
|
203
|
+
|
|
204
|
+
@override
|
|
205
|
+
void paint(Canvas canvas, Size size) {
|
|
206
|
+
final paint = Paint()
|
|
207
|
+
..color = color.withValues(alpha: partyMode ? 0.13 : 0.055)
|
|
208
|
+
..strokeWidth = 1;
|
|
209
|
+
const step = 48.0;
|
|
210
|
+
final drift = progress * step;
|
|
211
|
+
for (double x = -step + drift; x < size.width + step; x += step) {
|
|
212
|
+
canvas.drawLine(
|
|
213
|
+
Offset(x, 0),
|
|
214
|
+
Offset(x - size.width * 0.08, size.height),
|
|
215
|
+
paint,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
for (double y = size.height * 0.58; y < size.height + step; y += step) {
|
|
219
|
+
canvas.drawLine(
|
|
220
|
+
Offset(0, y),
|
|
221
|
+
Offset(size.width, y - progress * 12),
|
|
222
|
+
paint,
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
@override
|
|
228
|
+
bool shouldRepaint(covariant _ArcadeGridPainter oldDelegate) {
|
|
229
|
+
return oldDelegate.progress != progress ||
|
|
230
|
+
oldDelegate.color != color ||
|
|
231
|
+
oldDelegate.partyMode != partyMode;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
class _ConfettiBitsPainter extends CustomPainter {
|
|
236
|
+
const _ConfettiBitsPainter({
|
|
237
|
+
required this.progress,
|
|
238
|
+
required this.active,
|
|
239
|
+
required this.primary,
|
|
240
|
+
required this.secondary,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
final double progress;
|
|
244
|
+
final bool active;
|
|
245
|
+
final Color primary;
|
|
246
|
+
final Color secondary;
|
|
247
|
+
|
|
248
|
+
@override
|
|
249
|
+
void paint(Canvas canvas, Size size) {
|
|
250
|
+
if (!active) return;
|
|
251
|
+
final paint = Paint()..style = PaintingStyle.fill;
|
|
252
|
+
for (var i = 0; i < 26; i++) {
|
|
253
|
+
final seed = i * 37.0;
|
|
254
|
+
final x = ((seed * 17 + progress * size.width * 0.35) % size.width);
|
|
255
|
+
final y = ((seed * 29 + progress * size.height * 0.9) % size.height);
|
|
256
|
+
final color = i.isEven ? primary : secondary;
|
|
257
|
+
paint.color = color.withValues(alpha: 0.16 + (i % 4) * 0.035);
|
|
258
|
+
canvas.save();
|
|
259
|
+
canvas.translate(x, y);
|
|
260
|
+
canvas.rotate(progress * math.pi * 2 + i);
|
|
261
|
+
canvas.drawRRect(
|
|
262
|
+
RRect.fromRectAndRadius(
|
|
263
|
+
Rect.fromCenter(center: Offset.zero, width: 5 + (i % 3), height: 2.5),
|
|
264
|
+
const Radius.circular(2),
|
|
265
|
+
),
|
|
266
|
+
paint,
|
|
267
|
+
);
|
|
268
|
+
canvas.restore();
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
@override
|
|
273
|
+
bool shouldRepaint(covariant _ConfettiBitsPainter oldDelegate) {
|
|
274
|
+
return oldDelegate.progress != progress ||
|
|
275
|
+
oldDelegate.active != active ||
|
|
276
|
+
oldDelegate.primary != primary ||
|
|
277
|
+
oldDelegate.secondary != secondary;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
119
281
|
class _EntranceMotion extends StatefulWidget {
|
|
120
282
|
const _EntranceMotion({required this.child});
|
|
121
283
|
|
|
@@ -548,6 +710,7 @@ class _ToolEventTimelineRow extends StatelessWidget {
|
|
|
548
710
|
@override
|
|
549
711
|
Widget build(BuildContext context) {
|
|
550
712
|
Color color;
|
|
713
|
+
final running = tool.status == 'running';
|
|
551
714
|
switch (tool.status) {
|
|
552
715
|
case 'running':
|
|
553
716
|
color = _warning;
|
|
@@ -565,14 +728,26 @@ class _ToolEventTimelineRow extends StatelessWidget {
|
|
|
565
728
|
width: 28,
|
|
566
729
|
child: Column(
|
|
567
730
|
children: <Widget>[
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
731
|
+
_PulseHalo(
|
|
732
|
+
color: color,
|
|
733
|
+
animate: running,
|
|
734
|
+
child: Container(
|
|
735
|
+
width: 28,
|
|
736
|
+
height: 28,
|
|
737
|
+
decoration: BoxDecoration(
|
|
738
|
+
gradient: LinearGradient(
|
|
739
|
+
colors: <Color>[
|
|
740
|
+
color.withValues(alpha: running ? 0.26 : 0.16),
|
|
741
|
+
color.withValues(alpha: 0.08),
|
|
742
|
+
],
|
|
743
|
+
begin: Alignment.topLeft,
|
|
744
|
+
end: Alignment.bottomRight,
|
|
745
|
+
),
|
|
746
|
+
shape: BoxShape.circle,
|
|
747
|
+
border: Border.all(color: color.withValues(alpha: 0.24)),
|
|
748
|
+
),
|
|
749
|
+
child: Icon(tool.laneIcon, size: 16, color: color),
|
|
574
750
|
),
|
|
575
|
-
child: Icon(tool.laneIcon, size: 16, color: color),
|
|
576
751
|
),
|
|
577
752
|
if (!isLast)
|
|
578
753
|
Container(
|
|
@@ -592,9 +767,18 @@ class _ToolEventTimelineRow extends StatelessWidget {
|
|
|
592
767
|
child: Container(
|
|
593
768
|
padding: const EdgeInsets.all(12),
|
|
594
769
|
decoration: BoxDecoration(
|
|
595
|
-
|
|
770
|
+
gradient: LinearGradient(
|
|
771
|
+
colors: <Color>[
|
|
772
|
+
_bgSecondary,
|
|
773
|
+
if (running) color.withValues(alpha: 0.055) else _bgCard,
|
|
774
|
+
],
|
|
775
|
+
begin: Alignment.topLeft,
|
|
776
|
+
end: Alignment.bottomRight,
|
|
777
|
+
),
|
|
596
778
|
borderRadius: BorderRadius.circular(14),
|
|
597
|
-
border: Border.all(
|
|
779
|
+
border: Border.all(
|
|
780
|
+
color: running ? color.withValues(alpha: 0.25) : _border,
|
|
781
|
+
),
|
|
598
782
|
),
|
|
599
783
|
child: Column(
|
|
600
784
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
@@ -770,28 +954,119 @@ class _DotStatus extends StatelessWidget {
|
|
|
770
954
|
|
|
771
955
|
@override
|
|
772
956
|
Widget build(BuildContext context) {
|
|
773
|
-
return
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
957
|
+
return _PulseHalo(
|
|
958
|
+
color: color,
|
|
959
|
+
child: _GlassSurface(
|
|
960
|
+
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
961
|
+
borderRadius: BorderRadius.circular(999),
|
|
962
|
+
blurSigma: 16,
|
|
963
|
+
fillColor: _bgSecondary.withValues(alpha: 0.3),
|
|
964
|
+
borderColor: color.withValues(alpha: 0.24),
|
|
965
|
+
child: Row(
|
|
966
|
+
mainAxisSize: MainAxisSize.min,
|
|
967
|
+
children: <Widget>[
|
|
968
|
+
Container(
|
|
969
|
+
width: 8,
|
|
970
|
+
height: 8,
|
|
971
|
+
decoration: BoxDecoration(
|
|
972
|
+
color: color,
|
|
973
|
+
shape: BoxShape.circle,
|
|
974
|
+
boxShadow: <BoxShadow>[
|
|
975
|
+
BoxShadow(
|
|
976
|
+
color: color.withValues(alpha: 0.42),
|
|
977
|
+
blurRadius: 9,
|
|
978
|
+
spreadRadius: 1,
|
|
979
|
+
),
|
|
980
|
+
],
|
|
981
|
+
),
|
|
982
|
+
),
|
|
983
|
+
const SizedBox(width: 8),
|
|
984
|
+
Text(label),
|
|
985
|
+
],
|
|
986
|
+
),
|
|
790
987
|
),
|
|
791
988
|
);
|
|
792
989
|
}
|
|
793
990
|
}
|
|
794
991
|
|
|
992
|
+
class _PulseHalo extends StatefulWidget {
|
|
993
|
+
const _PulseHalo({
|
|
994
|
+
required this.child,
|
|
995
|
+
required this.color,
|
|
996
|
+
this.animate = true,
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
final Widget child;
|
|
1000
|
+
final Color color;
|
|
1001
|
+
final bool animate;
|
|
1002
|
+
|
|
1003
|
+
@override
|
|
1004
|
+
State<_PulseHalo> createState() => _PulseHaloState();
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
class _PulseHaloState extends State<_PulseHalo>
|
|
1008
|
+
with SingleTickerProviderStateMixin {
|
|
1009
|
+
late final AnimationController _controller;
|
|
1010
|
+
|
|
1011
|
+
@override
|
|
1012
|
+
void initState() {
|
|
1013
|
+
super.initState();
|
|
1014
|
+
_controller = AnimationController(
|
|
1015
|
+
vsync: this,
|
|
1016
|
+
duration: const Duration(milliseconds: 1600),
|
|
1017
|
+
);
|
|
1018
|
+
if (widget.animate) {
|
|
1019
|
+
_controller.repeat(reverse: true);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
@override
|
|
1024
|
+
void didUpdateWidget(_PulseHalo oldWidget) {
|
|
1025
|
+
super.didUpdateWidget(oldWidget);
|
|
1026
|
+
if (widget.animate != oldWidget.animate) {
|
|
1027
|
+
if (widget.animate) {
|
|
1028
|
+
_controller.repeat(reverse: true);
|
|
1029
|
+
} else {
|
|
1030
|
+
_controller.stop();
|
|
1031
|
+
_controller.value = 0.0;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
@override
|
|
1037
|
+
void dispose() {
|
|
1038
|
+
_controller.dispose();
|
|
1039
|
+
super.dispose();
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
@override
|
|
1043
|
+
Widget build(BuildContext context) {
|
|
1044
|
+
if (!widget.animate) {
|
|
1045
|
+
return widget.child;
|
|
1046
|
+
}
|
|
1047
|
+
return AnimatedBuilder(
|
|
1048
|
+
animation: _controller,
|
|
1049
|
+
builder: (context, child) {
|
|
1050
|
+
final t = Curves.easeInOut.transform(_controller.value);
|
|
1051
|
+
return DecoratedBox(
|
|
1052
|
+
decoration: BoxDecoration(
|
|
1053
|
+
borderRadius: BorderRadius.circular(999),
|
|
1054
|
+
boxShadow: <BoxShadow>[
|
|
1055
|
+
BoxShadow(
|
|
1056
|
+
color: widget.color.withValues(alpha: 0.04 + t * 0.08),
|
|
1057
|
+
blurRadius: 12 + t * 10,
|
|
1058
|
+
spreadRadius: t * 1.5,
|
|
1059
|
+
),
|
|
1060
|
+
],
|
|
1061
|
+
),
|
|
1062
|
+
child: child,
|
|
1063
|
+
);
|
|
1064
|
+
},
|
|
1065
|
+
child: widget.child,
|
|
1066
|
+
);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
|
|
795
1070
|
class _SidebarButton extends StatelessWidget {
|
|
796
1071
|
const _SidebarButton({
|
|
797
1072
|
required this.label,
|
|
@@ -817,25 +1092,23 @@ class _SidebarButton extends StatelessWidget {
|
|
|
817
1092
|
Widget build(BuildContext context) {
|
|
818
1093
|
return Padding(
|
|
819
1094
|
padding: const EdgeInsets.only(bottom: 6),
|
|
820
|
-
child:
|
|
821
|
-
|
|
822
|
-
curve: Curves.easeOutCubic,
|
|
823
|
-
scale: active ? 1.01 : 1,
|
|
1095
|
+
child: _HoverLift(
|
|
1096
|
+
active: active,
|
|
824
1097
|
child: _GlassSurface(
|
|
825
1098
|
borderRadius: BorderRadius.circular(18),
|
|
826
|
-
blurSigma: 18,
|
|
1099
|
+
blurSigma: active ? 22 : 18,
|
|
827
1100
|
fillColor: active
|
|
828
|
-
? _accentMuted.withValues(alpha: 0.
|
|
829
|
-
: _bgCard.withValues(alpha: 0.
|
|
1101
|
+
? _accentMuted.withValues(alpha: 0.36)
|
|
1102
|
+
: _bgCard.withValues(alpha: 0.22),
|
|
830
1103
|
borderColor: active
|
|
831
|
-
? _accent.withValues(alpha: 0.
|
|
832
|
-
: Colors.white.withValues(alpha: 0.
|
|
1104
|
+
? _accent.withValues(alpha: 0.45)
|
|
1105
|
+
: Colors.white.withValues(alpha: 0.04),
|
|
833
1106
|
boxShadow: active
|
|
834
1107
|
? <BoxShadow>[
|
|
835
1108
|
BoxShadow(
|
|
836
|
-
color: _accent.withValues(alpha: 0.
|
|
837
|
-
blurRadius:
|
|
838
|
-
offset: const Offset(0,
|
|
1109
|
+
color: _accent.withValues(alpha: 0.16),
|
|
1110
|
+
blurRadius: 24,
|
|
1111
|
+
offset: const Offset(0, 10),
|
|
839
1112
|
),
|
|
840
1113
|
]
|
|
841
1114
|
: null,
|
|
@@ -844,52 +1117,81 @@ class _SidebarButton extends StatelessWidget {
|
|
|
844
1117
|
child: InkWell(
|
|
845
1118
|
borderRadius: BorderRadius.circular(18),
|
|
846
1119
|
onTap: onTap,
|
|
847
|
-
child:
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
1120
|
+
child: Stack(
|
|
1121
|
+
children: <Widget>[
|
|
1122
|
+
if (active)
|
|
1123
|
+
Positioned.fill(
|
|
1124
|
+
child: IgnorePointer(
|
|
1125
|
+
child: DecoratedBox(
|
|
1126
|
+
decoration: BoxDecoration(
|
|
1127
|
+
borderRadius: BorderRadius.circular(18),
|
|
1128
|
+
gradient: LinearGradient(
|
|
1129
|
+
colors: <Color>[
|
|
1130
|
+
Colors.white.withValues(alpha: 0.08),
|
|
1131
|
+
Colors.transparent,
|
|
1132
|
+
_accentAlt.withValues(alpha: 0.08),
|
|
1133
|
+
],
|
|
1134
|
+
stops: const <double>[0, 0.44, 1],
|
|
1135
|
+
begin: Alignment.topLeft,
|
|
1136
|
+
end: Alignment.bottomRight,
|
|
1137
|
+
),
|
|
865
1138
|
),
|
|
866
|
-
borderRadius: BorderRadius.circular(999),
|
|
867
1139
|
),
|
|
868
1140
|
),
|
|
869
|
-
Icon(
|
|
870
|
-
icon,
|
|
871
|
-
size: iconSize,
|
|
872
|
-
color: active ? _accentHover : _textSecondary,
|
|
873
1141
|
),
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
1142
|
+
Container(
|
|
1143
|
+
width: double.infinity,
|
|
1144
|
+
padding: EdgeInsets.fromLTRB(12 + indent, 12, 12, 12),
|
|
1145
|
+
child: Row(
|
|
1146
|
+
children: <Widget>[
|
|
1147
|
+
AnimatedContainer(
|
|
1148
|
+
duration: const Duration(milliseconds: 220),
|
|
1149
|
+
curve: Curves.easeOutCubic,
|
|
1150
|
+
width: active ? 6 : 3,
|
|
1151
|
+
height: active ? 26 : 16,
|
|
1152
|
+
margin: EdgeInsets.only(right: active ? 10 : 13),
|
|
1153
|
+
decoration: BoxDecoration(
|
|
1154
|
+
gradient: LinearGradient(
|
|
1155
|
+
colors: <Color>[
|
|
1156
|
+
active
|
|
1157
|
+
? _accentHover
|
|
1158
|
+
: _textMuted.withValues(alpha: 0.35),
|
|
1159
|
+
active
|
|
1160
|
+
? _accentAlt.withValues(alpha: 0.95)
|
|
1161
|
+
: _textMuted.withValues(alpha: 0.08),
|
|
1162
|
+
],
|
|
1163
|
+
begin: Alignment.topCenter,
|
|
1164
|
+
end: Alignment.bottomCenter,
|
|
1165
|
+
),
|
|
1166
|
+
borderRadius: BorderRadius.circular(999),
|
|
1167
|
+
),
|
|
884
1168
|
),
|
|
885
|
-
|
|
1169
|
+
Icon(
|
|
1170
|
+
icon,
|
|
1171
|
+
size: iconSize,
|
|
1172
|
+
color: active ? _accentHover : _textSecondary,
|
|
1173
|
+
),
|
|
1174
|
+
const SizedBox(width: 10),
|
|
1175
|
+
Expanded(
|
|
1176
|
+
child: Text(
|
|
1177
|
+
label,
|
|
1178
|
+
style: TextStyle(
|
|
1179
|
+
fontSize: fontSize,
|
|
1180
|
+
fontWeight: active
|
|
1181
|
+
? FontWeight.w800
|
|
1182
|
+
: FontWeight.w600,
|
|
1183
|
+
color: active ? _textPrimary : _textSecondary,
|
|
1184
|
+
),
|
|
1185
|
+
),
|
|
1186
|
+
),
|
|
1187
|
+
if (trailing != null) ...<Widget>[
|
|
1188
|
+
const SizedBox(width: 8),
|
|
1189
|
+
trailing!,
|
|
1190
|
+
],
|
|
1191
|
+
],
|
|
886
1192
|
),
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
trailing!,
|
|
890
|
-
],
|
|
891
|
-
],
|
|
892
|
-
),
|
|
1193
|
+
),
|
|
1194
|
+
],
|
|
893
1195
|
),
|
|
894
1196
|
),
|
|
895
1197
|
),
|
|
@@ -899,6 +1201,42 @@ class _SidebarButton extends StatelessWidget {
|
|
|
899
1201
|
}
|
|
900
1202
|
}
|
|
901
1203
|
|
|
1204
|
+
class _HoverLift extends StatefulWidget {
|
|
1205
|
+
const _HoverLift({required this.child, this.active = false});
|
|
1206
|
+
|
|
1207
|
+
final Widget child;
|
|
1208
|
+
final bool active;
|
|
1209
|
+
|
|
1210
|
+
@override
|
|
1211
|
+
State<_HoverLift> createState() => _HoverLiftState();
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
class _HoverLiftState extends State<_HoverLift> {
|
|
1215
|
+
bool _hovering = false;
|
|
1216
|
+
|
|
1217
|
+
@override
|
|
1218
|
+
Widget build(BuildContext context) {
|
|
1219
|
+
final lifted = widget.active || _hovering;
|
|
1220
|
+
return MouseRegion(
|
|
1221
|
+
onEnter: (_) => setState(() => _hovering = true),
|
|
1222
|
+
onExit: (_) => setState(() => _hovering = false),
|
|
1223
|
+
child: AnimatedScale(
|
|
1224
|
+
duration: const Duration(milliseconds: 180),
|
|
1225
|
+
curve: Curves.easeOutCubic,
|
|
1226
|
+
scale: lifted ? 1.012 : 1,
|
|
1227
|
+
child: AnimatedSlide(
|
|
1228
|
+
duration: const Duration(milliseconds: 180),
|
|
1229
|
+
curve: Curves.easeOutCubic,
|
|
1230
|
+
offset: _hovering && !widget.active
|
|
1231
|
+
? const Offset(0.01, 0)
|
|
1232
|
+
: Offset.zero,
|
|
1233
|
+
child: widget.child,
|
|
1234
|
+
),
|
|
1235
|
+
),
|
|
1236
|
+
);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
|
|
902
1240
|
class _SidebarIconButton extends StatelessWidget {
|
|
903
1241
|
const _SidebarIconButton({
|
|
904
1242
|
required this.tooltip,
|
|
@@ -936,49 +1274,87 @@ class _SidebarIconButton extends StatelessWidget {
|
|
|
936
1274
|
}
|
|
937
1275
|
}
|
|
938
1276
|
|
|
939
|
-
class
|
|
940
|
-
const
|
|
1277
|
+
class _LogoBadge extends StatefulWidget {
|
|
1278
|
+
const _LogoBadge({required this.size});
|
|
941
1279
|
|
|
942
1280
|
final double size;
|
|
943
|
-
final Color color;
|
|
944
1281
|
|
|
945
1282
|
@override
|
|
946
|
-
|
|
947
|
-
return IgnorePointer(
|
|
948
|
-
child: Container(
|
|
949
|
-
width: size,
|
|
950
|
-
height: size,
|
|
951
|
-
decoration: BoxDecoration(
|
|
952
|
-
shape: BoxShape.circle,
|
|
953
|
-
boxShadow: <BoxShadow>[
|
|
954
|
-
BoxShadow(
|
|
955
|
-
color: color.withValues(alpha: 0.18),
|
|
956
|
-
blurRadius: 120,
|
|
957
|
-
spreadRadius: 30,
|
|
958
|
-
),
|
|
959
|
-
],
|
|
960
|
-
),
|
|
961
|
-
),
|
|
962
|
-
);
|
|
963
|
-
}
|
|
1283
|
+
State<_LogoBadge> createState() => _LogoBadgeState();
|
|
964
1284
|
}
|
|
965
1285
|
|
|
966
|
-
class
|
|
967
|
-
|
|
1286
|
+
class _LogoBadgeState extends State<_LogoBadge> {
|
|
1287
|
+
int _tapCount = 0;
|
|
1288
|
+
Timer? _tapResetTimer;
|
|
1289
|
+
Timer? _partyResetTimer;
|
|
968
1290
|
|
|
969
|
-
|
|
1291
|
+
@override
|
|
1292
|
+
void dispose() {
|
|
1293
|
+
_tapResetTimer?.cancel();
|
|
1294
|
+
if (_partyResetTimer != null && _partyResetTimer!.isActive) {
|
|
1295
|
+
_partyResetTimer?.cancel();
|
|
1296
|
+
_partyModeEnabled.value = false;
|
|
1297
|
+
}
|
|
1298
|
+
super.dispose();
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
void _handleTap() {
|
|
1302
|
+
_tapResetTimer?.cancel();
|
|
1303
|
+
_tapCount += 1;
|
|
1304
|
+
if (_tapCount >= 5) {
|
|
1305
|
+
_tapCount = 0;
|
|
1306
|
+
_partyModeEnabled.value = true;
|
|
1307
|
+
_partyResetTimer?.cancel();
|
|
1308
|
+
_partyResetTimer = Timer(const Duration(seconds: 7), () {
|
|
1309
|
+
_partyModeEnabled.value = false;
|
|
1310
|
+
});
|
|
1311
|
+
} else {
|
|
1312
|
+
_tapResetTimer = Timer(const Duration(milliseconds: 1250), () {
|
|
1313
|
+
_tapCount = 0;
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
970
1317
|
|
|
971
1318
|
@override
|
|
972
1319
|
Widget build(BuildContext context) {
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
1320
|
+
final isDark = MediaQuery.platformBrightnessOf(context) == Brightness.dark;
|
|
1321
|
+
return ValueListenableBuilder<bool>(
|
|
1322
|
+
valueListenable: _partyModeEnabled,
|
|
1323
|
+
builder: (context, partyMode, _) {
|
|
1324
|
+
return GestureDetector(
|
|
1325
|
+
onTap: _handleTap,
|
|
1326
|
+
child: AnimatedScale(
|
|
1327
|
+
duration: const Duration(milliseconds: 240),
|
|
1328
|
+
curve: Curves.easeOutBack,
|
|
1329
|
+
scale: partyMode ? 1.08 : 1,
|
|
1330
|
+
child: AnimatedContainer(
|
|
1331
|
+
duration: const Duration(milliseconds: 260),
|
|
1332
|
+
width: widget.size,
|
|
1333
|
+
height: widget.size,
|
|
1334
|
+
decoration: BoxDecoration(
|
|
1335
|
+
borderRadius: BorderRadius.circular(widget.size * 0.24),
|
|
1336
|
+
boxShadow: <BoxShadow>[
|
|
1337
|
+
BoxShadow(
|
|
1338
|
+
color: (partyMode ? _accentAlt : _accent).withValues(
|
|
1339
|
+
alpha: partyMode ? 0.34 : 0.16,
|
|
1340
|
+
),
|
|
1341
|
+
blurRadius: partyMode ? 28 : 14,
|
|
1342
|
+
spreadRadius: partyMode ? 1 : 0,
|
|
1343
|
+
),
|
|
1344
|
+
],
|
|
1345
|
+
),
|
|
1346
|
+
child: Image.asset(
|
|
1347
|
+
isDark
|
|
1348
|
+
? 'assets/branding/app_icon_512.png'
|
|
1349
|
+
: 'assets/branding/app_icon_light_512.png',
|
|
1350
|
+
width: widget.size,
|
|
1351
|
+
height: widget.size,
|
|
1352
|
+
filterQuality: FilterQuality.high,
|
|
1353
|
+
),
|
|
1354
|
+
),
|
|
1355
|
+
),
|
|
1356
|
+
);
|
|
1357
|
+
},
|
|
982
1358
|
);
|
|
983
1359
|
}
|
|
984
1360
|
}
|
|
@@ -1033,7 +1409,6 @@ class _BrandLockup extends StatelessWidget {
|
|
|
1033
1409
|
}
|
|
1034
1410
|
}
|
|
1035
1411
|
|
|
1036
|
-
|
|
1037
1412
|
class _EmptyState extends StatelessWidget {
|
|
1038
1413
|
const _EmptyState({required this.title, required this.subtitle});
|
|
1039
1414
|
|
|
@@ -1045,7 +1420,27 @@ class _EmptyState extends StatelessWidget {
|
|
|
1045
1420
|
return Column(
|
|
1046
1421
|
mainAxisSize: MainAxisSize.min,
|
|
1047
1422
|
children: <Widget>[
|
|
1048
|
-
|
|
1423
|
+
Stack(
|
|
1424
|
+
alignment: Alignment.center,
|
|
1425
|
+
children: <Widget>[
|
|
1426
|
+
Container(
|
|
1427
|
+
width: 74,
|
|
1428
|
+
height: 74,
|
|
1429
|
+
decoration: BoxDecoration(
|
|
1430
|
+
shape: BoxShape.circle,
|
|
1431
|
+
gradient: SweepGradient(
|
|
1432
|
+
colors: <Color>[
|
|
1433
|
+
_accent.withValues(alpha: 0.18),
|
|
1434
|
+
_accentAlt.withValues(alpha: 0.28),
|
|
1435
|
+
Colors.transparent,
|
|
1436
|
+
_accent.withValues(alpha: 0.18),
|
|
1437
|
+
],
|
|
1438
|
+
),
|
|
1439
|
+
),
|
|
1440
|
+
),
|
|
1441
|
+
const _LogoBadge(size: 52),
|
|
1442
|
+
],
|
|
1443
|
+
),
|
|
1049
1444
|
const SizedBox(height: 12),
|
|
1050
1445
|
Text(
|
|
1051
1446
|
title,
|
|
@@ -1104,26 +1499,50 @@ class _ChatBubble extends StatelessWidget {
|
|
|
1104
1499
|
const SizedBox(width: 12),
|
|
1105
1500
|
],
|
|
1106
1501
|
Flexible(
|
|
1107
|
-
child:
|
|
1502
|
+
child: AnimatedContainer(
|
|
1503
|
+
duration: const Duration(milliseconds: 220),
|
|
1504
|
+
curve: Curves.easeOutCubic,
|
|
1108
1505
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 11),
|
|
1109
1506
|
decoration: BoxDecoration(
|
|
1110
|
-
|
|
1507
|
+
gradient: isUser
|
|
1508
|
+
? LinearGradient(
|
|
1509
|
+
colors: <Color>[_accent, _accentAlt],
|
|
1510
|
+
begin: Alignment.topLeft,
|
|
1511
|
+
end: Alignment.bottomRight,
|
|
1512
|
+
)
|
|
1513
|
+
: LinearGradient(
|
|
1514
|
+
colors: <Color>[
|
|
1515
|
+
_bgCard,
|
|
1516
|
+
_bgSecondary.withValues(alpha: 0.94),
|
|
1517
|
+
],
|
|
1518
|
+
begin: Alignment.topLeft,
|
|
1519
|
+
end: Alignment.bottomRight,
|
|
1520
|
+
),
|
|
1111
1521
|
borderRadius: BorderRadius.only(
|
|
1112
1522
|
topLeft: const Radius.circular(14),
|
|
1113
1523
|
topRight: const Radius.circular(14),
|
|
1114
1524
|
bottomLeft: Radius.circular(isUser ? 14 : 4),
|
|
1115
1525
|
bottomRight: Radius.circular(isUser ? 4 : 14),
|
|
1116
1526
|
),
|
|
1117
|
-
border:
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1527
|
+
border: Border.all(
|
|
1528
|
+
color: isUser
|
|
1529
|
+
? Colors.white.withValues(alpha: 0.18)
|
|
1530
|
+
: _borderLight.withValues(alpha: 0.72),
|
|
1531
|
+
),
|
|
1532
|
+
boxShadow: <BoxShadow>[
|
|
1533
|
+
if (isUser)
|
|
1534
|
+
BoxShadow(
|
|
1535
|
+
color: _accentAlt.withValues(alpha: 0.30),
|
|
1536
|
+
blurRadius: 12,
|
|
1537
|
+
offset: const Offset(0, 2),
|
|
1538
|
+
),
|
|
1539
|
+
if (!isUser)
|
|
1540
|
+
BoxShadow(
|
|
1541
|
+
color: Colors.black.withValues(alpha: 0.08),
|
|
1542
|
+
blurRadius: 16,
|
|
1543
|
+
offset: const Offset(0, 8),
|
|
1544
|
+
),
|
|
1545
|
+
],
|
|
1127
1546
|
),
|
|
1128
1547
|
child: Column(
|
|
1129
1548
|
crossAxisAlignment: isUser
|
|
@@ -1511,14 +1930,18 @@ class _MessageAvatar extends StatelessWidget {
|
|
|
1511
1930
|
decoration: BoxDecoration(
|
|
1512
1931
|
borderRadius: BorderRadius.circular(8),
|
|
1513
1932
|
gradient: assistant
|
|
1514
|
-
? LinearGradient(
|
|
1933
|
+
? LinearGradient(
|
|
1934
|
+
colors: <Color>[_accentHover, _accent, _accentAlt],
|
|
1935
|
+
begin: Alignment.topLeft,
|
|
1936
|
+
end: Alignment.bottomRight,
|
|
1937
|
+
)
|
|
1515
1938
|
: null,
|
|
1516
1939
|
color: assistant ? null : _bgTertiary,
|
|
1517
1940
|
boxShadow: assistant
|
|
1518
1941
|
? <BoxShadow>[
|
|
1519
1942
|
BoxShadow(
|
|
1520
|
-
color: _accentAlt.withValues(alpha: 0.
|
|
1521
|
-
blurRadius:
|
|
1943
|
+
color: _accentAlt.withValues(alpha: 0.42),
|
|
1944
|
+
blurRadius: 14,
|
|
1522
1945
|
offset: const Offset(0, 2),
|
|
1523
1946
|
),
|
|
1524
1947
|
]
|
|
@@ -1546,8 +1969,22 @@ class _StatusPill extends StatelessWidget {
|
|
|
1546
1969
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
|
1547
1970
|
decoration: BoxDecoration(
|
|
1548
1971
|
borderRadius: BorderRadius.circular(999),
|
|
1549
|
-
|
|
1550
|
-
|
|
1972
|
+
gradient: LinearGradient(
|
|
1973
|
+
colors: <Color>[
|
|
1974
|
+
color.withValues(alpha: 0.2),
|
|
1975
|
+
color.withValues(alpha: 0.08),
|
|
1976
|
+
],
|
|
1977
|
+
begin: Alignment.topLeft,
|
|
1978
|
+
end: Alignment.bottomRight,
|
|
1979
|
+
),
|
|
1980
|
+
border: Border.all(color: color.withValues(alpha: 0.28)),
|
|
1981
|
+
boxShadow: <BoxShadow>[
|
|
1982
|
+
BoxShadow(
|
|
1983
|
+
color: color.withValues(alpha: 0.08),
|
|
1984
|
+
blurRadius: 12,
|
|
1985
|
+
offset: const Offset(0, 4),
|
|
1986
|
+
),
|
|
1987
|
+
],
|
|
1551
1988
|
),
|
|
1552
1989
|
child: Text(
|
|
1553
1990
|
label,
|
|
@@ -3201,6 +3638,16 @@ int _asInt(dynamic value) {
|
|
|
3201
3638
|
return int.tryParse(value?.toString() ?? '') ?? 0;
|
|
3202
3639
|
}
|
|
3203
3640
|
|
|
3641
|
+
double _asDouble(dynamic value, {double fallback = 0}) {
|
|
3642
|
+
if (value is double) {
|
|
3643
|
+
return value;
|
|
3644
|
+
}
|
|
3645
|
+
if (value is int) {
|
|
3646
|
+
return value.toDouble();
|
|
3647
|
+
}
|
|
3648
|
+
return double.tryParse(value?.toString() ?? '') ?? fallback;
|
|
3649
|
+
}
|
|
3650
|
+
|
|
3204
3651
|
DateTime _parseTimestamp(String? raw) {
|
|
3205
3652
|
if (raw == null || raw.isEmpty) {
|
|
3206
3653
|
return DateTime.now();
|