kasy-cli 1.18.0 → 1.19.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/bin/kasy.js +1 -0
- package/lib/commands/new.js +99 -105
- package/lib/commands/run.js +16 -3
- package/lib/scaffold/backends/firebase/setup-from-scratch.js +79 -0
- package/lib/utils/brand.js +1 -1
- package/lib/utils/i18n/messages-en.js +4 -0
- package/lib/utils/i18n/messages-es.js +4 -0
- package/lib/utils/i18n/messages-pt.js +4 -0
- package/package.json +1 -2
- package/templates/firebase/lib/components/kasy_date_picker.dart +1670 -331
- package/templates/firebase/lib/components/kasy_tabs.dart +111 -72
- package/templates/firebase/lib/components/kasy_text_area.dart +9 -4
- package/templates/firebase/lib/components/kasy_text_field.dart +96 -36
- package/templates/firebase/lib/components/kasy_text_field_otp.dart +1 -2
- package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +88 -35
- package/templates/firebase/lib/core/bottom_menu/bottom_router.dart +7 -43
- package/templates/firebase/lib/core/dev_inspector/dev_inspector.dart +118 -16
- package/templates/firebase/lib/core/dev_inspector/dev_inspector_service.dart +14 -20
- package/templates/firebase/lib/core/security/secured_storage.dart +56 -15
- package/templates/firebase/lib/core/theme/providers/theme_provider.dart +3 -0
- package/templates/firebase/lib/core/theme/web_background_sync.dart +3 -0
- package/templates/firebase/lib/core/theme/web_background_sync_web.dart +18 -0
- package/templates/firebase/lib/core/web_device_preview/web_device_preview.dart +3 -6
- package/templates/firebase/lib/features/authentication/api/authentication_api.dart +6 -0
- package/templates/firebase/lib/features/home/home_components_page.dart +3 -2
- package/templates/firebase/lib/features/home/home_components_preview_registry.dart +457 -73
- package/templates/firebase/lib/features/home/home_page.dart +17 -40
- package/templates/firebase/lib/features/notifications/ui/notifications_page.dart +1 -16
- package/templates/firebase/lib/features/notifications/ui/widgets/empty_notifications.dart +0 -4
- package/templates/firebase/lib/main.dart +34 -34
- package/templates/firebase/pubspec.yaml +1 -0
- package/templates/firebase/storage.cors.json +8 -0
- package/templates/firebase/web/index.html +15 -2
- package/templates/firebase/lib/core/bottom_menu/kasy_bart_navigation.dart +0 -22
|
@@ -2,7 +2,6 @@ import 'package:bart/bart.dart';
|
|
|
2
2
|
import 'package:flutter/material.dart';
|
|
3
3
|
import 'package:kasy_kit/components/kasy_app_bar.dart';
|
|
4
4
|
import 'package:kasy_kit/core/bottom_menu/bart_inner_paths.dart';
|
|
5
|
-
import 'package:kasy_kit/core/bottom_menu/kasy_bart_navigation.dart';
|
|
6
5
|
import 'package:kasy_kit/core/bottom_menu/notification_bottom_item.dart';
|
|
7
6
|
import 'package:kasy_kit/core/navigation/kasy_navigation_config.dart';
|
|
8
7
|
import 'package:kasy_kit/core/navigation/kasy_route_transition.dart';
|
|
@@ -110,53 +109,18 @@ List<BartMenuRoute> subRoutes() {
|
|
|
110
109
|
];
|
|
111
110
|
}
|
|
112
111
|
|
|
113
|
-
|
|
112
|
+
/// Marker wrapper for inner home routes. Visibility is owned by
|
|
113
|
+
/// [BottomMenu] (see [BottomMenu._onRouteChanged] + [BartMenuRoute.innerRoute]
|
|
114
|
+
/// `showBottomBar: false`); this widget exists for symmetry with the dashboard
|
|
115
|
+
/// cards and to keep `HomeFeaturesPage` / `HomeComponentsPage` / etc. agnostic
|
|
116
|
+
/// of how they were pushed.
|
|
117
|
+
class _HomeInnerRoute extends StatelessWidget {
|
|
114
118
|
final Widget child;
|
|
115
119
|
|
|
116
120
|
const _HomeInnerRoute({required this.child});
|
|
117
121
|
|
|
118
122
|
@override
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
class _HomeInnerRouteState extends State<_HomeInnerRoute> {
|
|
123
|
-
ModalRoute<Object?>? _route;
|
|
124
|
-
|
|
125
|
-
@override
|
|
126
|
-
void didChangeDependencies() {
|
|
127
|
-
super.didChangeDependencies();
|
|
128
|
-
final ModalRoute<Object?>? route = ModalRoute.of(context);
|
|
129
|
-
if (_route != route) {
|
|
130
|
-
_route?.animation?.removeStatusListener(_onRouteAnimStatus);
|
|
131
|
-
_route = route;
|
|
132
|
-
final Animation<double>? anim = route?.animation;
|
|
133
|
-
if (anim != null && !anim.isCompleted) {
|
|
134
|
-
anim.addStatusListener(_onRouteAnimStatus);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
kasyHideBottomBar(context);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
void _onRouteAnimStatus(AnimationStatus status) {
|
|
141
|
-
if (mounted && status == AnimationStatus.completed) {
|
|
142
|
-
kasyHideBottomBar(context);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
@override
|
|
147
|
-
void dispose() {
|
|
148
|
-
_route?.animation?.removeStatusListener(_onRouteAnimStatus);
|
|
149
|
-
super.dispose();
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
@override
|
|
153
|
-
void reassemble() {
|
|
154
|
-
super.reassemble();
|
|
155
|
-
kasyHideBottomBar(context);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
@override
|
|
159
|
-
Widget build(BuildContext context) => widget.child;
|
|
123
|
+
Widget build(BuildContext context) => child;
|
|
160
124
|
}
|
|
161
125
|
|
|
162
126
|
/// Placeholder page for the wishlist tab.
|
|
@@ -2,6 +2,7 @@ import 'dart:async';
|
|
|
2
2
|
|
|
3
3
|
import 'package:flutter/foundation.dart';
|
|
4
4
|
import 'package:flutter/material.dart';
|
|
5
|
+
import 'package:flutter/scheduler.dart';
|
|
5
6
|
import 'package:flutter/services.dart';
|
|
6
7
|
import 'package:kasy_kit/core/dev_inspector/dev_inspector_info.dart';
|
|
7
8
|
import 'package:kasy_kit/core/dev_inspector/dev_inspector_service.dart';
|
|
@@ -12,6 +13,10 @@ import 'package:kasy_kit/core/theme/providers/theme_provider.dart';
|
|
|
12
13
|
import 'package:kasy_kit/i18n/translations.g.dart';
|
|
13
14
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
14
15
|
|
|
16
|
+
const Color _highlightColor = Color(0xFFD2F51E);
|
|
17
|
+
const double _highlightStrokeWidth = 2.0;
|
|
18
|
+
const double _highlightCornerRadius = 4.0;
|
|
19
|
+
|
|
15
20
|
final GlobalKey<ScaffoldMessengerState> devInspectorRootScaffoldMessengerKey =
|
|
16
21
|
GlobalKey<ScaffoldMessengerState>(debugLabel: 'devInspectorRoot');
|
|
17
22
|
|
|
@@ -31,6 +36,12 @@ final ValueNotifier<bool> devInspectorRevealNowNotifier =
|
|
|
31
36
|
final ValueNotifier<bool> devInspectorCopyTriggerNotifier =
|
|
32
37
|
ValueNotifier<bool>(false);
|
|
33
38
|
|
|
39
|
+
/// Active state of the custom inspector. Toggled by the FAB (native) and by
|
|
40
|
+
/// the Web Device Preview pill; observed by [DevInspector] to mount the
|
|
41
|
+
/// tap-absorbing overlay and the highlight.
|
|
42
|
+
final ValueNotifier<bool> devInspectorActiveNotifier =
|
|
43
|
+
ValueNotifier<bool>(false);
|
|
44
|
+
|
|
34
45
|
class DevInspector extends StatefulWidget {
|
|
35
46
|
const DevInspector({super.key, required this.child});
|
|
36
47
|
|
|
@@ -45,7 +56,8 @@ class DevInspector extends StatefulWidget {
|
|
|
45
56
|
State<DevInspector> createState() => _DevInspectorState();
|
|
46
57
|
}
|
|
47
58
|
|
|
48
|
-
class _DevInspectorState extends State<DevInspector>
|
|
59
|
+
class _DevInspectorState extends State<DevInspector>
|
|
60
|
+
with SingleTickerProviderStateMixin {
|
|
49
61
|
static const Duration _fabRevealDelay = Duration(seconds: 2);
|
|
50
62
|
static const Duration _fabDeemphasizeDelay = Duration(seconds: 2);
|
|
51
63
|
static const Duration _fabOpacityAnimation = Duration(milliseconds: 700);
|
|
@@ -71,11 +83,15 @@ class _DevInspectorState extends State<DevInspector> {
|
|
|
71
83
|
bool _fabFadeIn = false;
|
|
72
84
|
bool _fabEmphasized = false;
|
|
73
85
|
|
|
86
|
+
DevInspectorInfo? _selectedInfo;
|
|
87
|
+
RenderObject? _selectedRender;
|
|
88
|
+
Rect? _highlightRect;
|
|
89
|
+
Ticker? _highlightTicker;
|
|
90
|
+
|
|
74
91
|
@override
|
|
75
92
|
void initState() {
|
|
76
93
|
super.initState();
|
|
77
|
-
|
|
78
|
-
.addListener(_handleInspectorOverrideChanged);
|
|
94
|
+
devInspectorActiveNotifier.addListener(_handleActiveChanged);
|
|
79
95
|
devInspectorFabEnabledNotifier.addListener(_onFabEnabledNotifierChanged);
|
|
80
96
|
devInspectorRevealNowNotifier.addListener(_onRevealNow);
|
|
81
97
|
devInspectorCopyTriggerNotifier.addListener(_onCopyTriggered);
|
|
@@ -87,13 +103,13 @@ class _DevInspectorState extends State<DevInspector> {
|
|
|
87
103
|
_revealTimer?.cancel();
|
|
88
104
|
_fabDeemphasizeTimer?.cancel();
|
|
89
105
|
_copyFeedbackTimer?.cancel();
|
|
106
|
+
_highlightTicker?.dispose();
|
|
90
107
|
devInspectorFabEnabledNotifier.removeListener(_onFabEnabledNotifierChanged);
|
|
91
108
|
devInspectorRevealNowNotifier.removeListener(_onRevealNow);
|
|
92
109
|
devInspectorCopyTriggerNotifier.removeListener(_onCopyTriggered);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
WidgetsBinding.instance.debugShowWidgetInspectorOverride = false;
|
|
110
|
+
devInspectorActiveNotifier.removeListener(_handleActiveChanged);
|
|
111
|
+
if (devInspectorActiveNotifier.value) {
|
|
112
|
+
devInspectorActiveNotifier.value = false;
|
|
97
113
|
}
|
|
98
114
|
super.dispose();
|
|
99
115
|
}
|
|
@@ -141,7 +157,7 @@ class _DevInspectorState extends State<DevInspector> {
|
|
|
141
157
|
if (!mounted) return;
|
|
142
158
|
if (!enabled) {
|
|
143
159
|
if (_active) {
|
|
144
|
-
|
|
160
|
+
devInspectorActiveNotifier.value = false;
|
|
145
161
|
}
|
|
146
162
|
_copyFeedbackTimer?.cancel();
|
|
147
163
|
setState(() {
|
|
@@ -152,6 +168,7 @@ class _DevInspectorState extends State<DevInspector> {
|
|
|
152
168
|
_dragging = false;
|
|
153
169
|
_copyFeedbackText = null;
|
|
154
170
|
_copyFeedbackIsError = false;
|
|
171
|
+
_clearSelection();
|
|
155
172
|
});
|
|
156
173
|
_copyFeedbackTimer?.cancel();
|
|
157
174
|
return;
|
|
@@ -192,17 +209,62 @@ class _DevInspectorState extends State<DevInspector> {
|
|
|
192
209
|
}
|
|
193
210
|
|
|
194
211
|
void _setInspectorActive(bool value) {
|
|
195
|
-
|
|
196
|
-
WidgetsBinding.instance.debugShowWidgetInspectorOverride = value;
|
|
212
|
+
devInspectorActiveNotifier.value = value;
|
|
197
213
|
HapticFeedback.lightImpact();
|
|
198
214
|
}
|
|
199
215
|
|
|
200
|
-
void
|
|
201
|
-
final bool
|
|
202
|
-
|
|
203
|
-
if (_active == override) return;
|
|
216
|
+
void _handleActiveChanged() {
|
|
217
|
+
final bool active = devInspectorActiveNotifier.value;
|
|
218
|
+
if (_active == active) return;
|
|
204
219
|
if (!mounted) return;
|
|
205
|
-
setState(()
|
|
220
|
+
setState(() {
|
|
221
|
+
_active = active;
|
|
222
|
+
if (!active) _clearSelection();
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
void _clearSelection() {
|
|
227
|
+
_selectedInfo = null;
|
|
228
|
+
_selectedRender = null;
|
|
229
|
+
_highlightRect = null;
|
|
230
|
+
_stopHighlightTicker();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
void _onInspectorTap(Offset globalPosition) {
|
|
234
|
+
final picked = DevInspectorService.pickAt(globalPosition);
|
|
235
|
+
if (picked == null) {
|
|
236
|
+
HapticFeedback.heavyImpact();
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
setState(() {
|
|
240
|
+
_selectedInfo = picked.info;
|
|
241
|
+
_selectedRender = picked.renderObject;
|
|
242
|
+
_highlightRect = picked.info.boundingBox;
|
|
243
|
+
});
|
|
244
|
+
_startHighlightTicker();
|
|
245
|
+
HapticFeedback.selectionClick();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
void _startHighlightTicker() {
|
|
249
|
+
_highlightTicker ??= createTicker(_onHighlightTick);
|
|
250
|
+
if (!_highlightTicker!.isActive) _highlightTicker!.start();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
void _stopHighlightTicker() {
|
|
254
|
+
if (_highlightTicker?.isActive ?? false) _highlightTicker!.stop();
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
void _onHighlightTick(Duration _) {
|
|
258
|
+
final RenderObject? target = _selectedRender;
|
|
259
|
+
if (target == null) return;
|
|
260
|
+
final Rect? rect = DevInspectorService.remeasure(target);
|
|
261
|
+
if (rect == null) {
|
|
262
|
+
setState(_clearSelection);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (_highlightRect != rect) {
|
|
266
|
+
setState(() => _highlightRect = rect);
|
|
267
|
+
}
|
|
206
268
|
}
|
|
207
269
|
|
|
208
270
|
String? _extractRouteHint(DevInspectorInfo info) {
|
|
@@ -233,7 +295,7 @@ class _DevInspectorState extends State<DevInspector> {
|
|
|
233
295
|
if (!_active || _copyBusy) return;
|
|
234
296
|
_copyBusy = true;
|
|
235
297
|
try {
|
|
236
|
-
final DevInspectorInfo? info =
|
|
298
|
+
final DevInspectorInfo? info = _selectedInfo;
|
|
237
299
|
if (!mounted) return;
|
|
238
300
|
if (info == null) {
|
|
239
301
|
_showCopyFeedback(t.devInspector.selectWidgetFirst, isError: true);
|
|
@@ -308,6 +370,20 @@ class _DevInspectorState extends State<DevInspector> {
|
|
|
308
370
|
clipBehavior: Clip.none,
|
|
309
371
|
children: <Widget>[
|
|
310
372
|
widget.child,
|
|
373
|
+
if (_active)
|
|
374
|
+
Positioned.fill(
|
|
375
|
+
child: Listener(
|
|
376
|
+
behavior: HitTestBehavior.opaque,
|
|
377
|
+
onPointerDown: (PointerDownEvent ev) =>
|
|
378
|
+
_onInspectorTap(ev.position),
|
|
379
|
+
child: IgnorePointer(
|
|
380
|
+
child: CustomPaint(
|
|
381
|
+
painter: _HighlightPainter(rect: _highlightRect),
|
|
382
|
+
size: Size.infinite,
|
|
383
|
+
),
|
|
384
|
+
),
|
|
385
|
+
),
|
|
386
|
+
),
|
|
311
387
|
if (_fabLayerMounted &&
|
|
312
388
|
devInspectorFabEnabledNotifier.value)
|
|
313
389
|
Positioned(
|
|
@@ -462,3 +538,29 @@ class _EntryFab extends StatelessWidget {
|
|
|
462
538
|
);
|
|
463
539
|
}
|
|
464
540
|
}
|
|
541
|
+
|
|
542
|
+
class _HighlightPainter extends CustomPainter {
|
|
543
|
+
_HighlightPainter({required this.rect});
|
|
544
|
+
|
|
545
|
+
final Rect? rect;
|
|
546
|
+
|
|
547
|
+
@override
|
|
548
|
+
void paint(Canvas canvas, Size size) {
|
|
549
|
+
final Rect? r = rect;
|
|
550
|
+
if (r == null || r.isEmpty) return;
|
|
551
|
+
final Paint paint = Paint()
|
|
552
|
+
..color = _highlightColor
|
|
553
|
+
..style = PaintingStyle.stroke
|
|
554
|
+
..strokeWidth = _highlightStrokeWidth;
|
|
555
|
+
final Rect outer = r.inflate(_highlightStrokeWidth / 2);
|
|
556
|
+
final RRect rrect = RRect.fromRectAndRadius(
|
|
557
|
+
outer,
|
|
558
|
+
const Radius.circular(_highlightCornerRadius),
|
|
559
|
+
);
|
|
560
|
+
canvas.drawRRect(rrect, paint);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
@override
|
|
564
|
+
bool shouldRepaint(_HighlightPainter oldDelegate) =>
|
|
565
|
+
oldDelegate.rect != rect;
|
|
566
|
+
}
|
|
@@ -280,23 +280,12 @@ class DevInspectorService {
|
|
|
280
280
|
);
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
-
///
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
WidgetInspectorService.instance.selection.current;
|
|
290
|
-
if (renderObject == null) return null;
|
|
291
|
-
return _extractFromRenderObject(renderObject);
|
|
292
|
-
} catch (_) {
|
|
293
|
-
return null;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/// Inspects the widget at a global position, syncs the native inspector
|
|
298
|
-
/// selection (tooltip + highlight), and return the info.
|
|
299
|
-
static DevInspectorInfo? inspectAndSelect(Offset globalPosition) {
|
|
283
|
+
/// Hit-tests at [globalPosition] and returns the inspector info plus the
|
|
284
|
+
/// underlying render object so the caller can re-measure the bounds during
|
|
285
|
+
/// animations.
|
|
286
|
+
static ({DevInspectorInfo info, RenderObject renderObject})? pickAt(
|
|
287
|
+
Offset globalPosition,
|
|
288
|
+
) {
|
|
300
289
|
if (!kDebugMode) return null;
|
|
301
290
|
try {
|
|
302
291
|
final result = BoxHitTestResult();
|
|
@@ -308,9 +297,7 @@ class DevInspectorService {
|
|
|
308
297
|
final RenderObject candidate = entry.target as RenderObject;
|
|
309
298
|
final DevInspectorInfo? info = _extractFromRenderObject(candidate);
|
|
310
299
|
if (info != null) {
|
|
311
|
-
|
|
312
|
-
WidgetInspectorService.instance.setSelection(candidate, 'kit_inspector');
|
|
313
|
-
return info;
|
|
300
|
+
return (info: info, renderObject: candidate);
|
|
314
301
|
}
|
|
315
302
|
}
|
|
316
303
|
return null;
|
|
@@ -318,4 +305,11 @@ class DevInspectorService {
|
|
|
318
305
|
return null;
|
|
319
306
|
}
|
|
320
307
|
}
|
|
308
|
+
|
|
309
|
+
/// Re-measures the global bounding rect of a previously picked render
|
|
310
|
+
/// object. Returns null if it became detached from the tree.
|
|
311
|
+
static Rect? remeasure(RenderObject target) {
|
|
312
|
+
if (!target.attached) return null;
|
|
313
|
+
return _globalBoundingRect(target);
|
|
314
|
+
}
|
|
321
315
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import 'package:flutter/foundation.dart';
|
|
1
2
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
2
3
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
3
4
|
import 'package:kasy_kit/core/data/entities/user_entity.dart';
|
|
5
|
+
import 'package:shared_preferences/shared_preferences.dart';
|
|
4
6
|
|
|
5
7
|
const _kTokenKey = "token";
|
|
6
8
|
const _kUserIdKey = "userId";
|
|
@@ -9,38 +11,77 @@ final authSecuredStorageProvider = Provider<AuthSecuredStorage>(
|
|
|
9
11
|
(ref) => AuthSecuredStorage.fromEnv(),
|
|
10
12
|
);
|
|
11
13
|
|
|
12
|
-
class AuthSecuredStorage {
|
|
13
|
-
|
|
14
|
+
abstract class AuthSecuredStorage {
|
|
15
|
+
Future<void> write({required Credentials value});
|
|
16
|
+
Future<Credentials?> read();
|
|
17
|
+
Future<void> clear();
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
// Web uses SharedPreferences (LocalStorage) because flutter_secure_storage
|
|
20
|
+
// has no real on-disk persistence in the browser. Mobile keeps the Keychain
|
|
21
|
+
// / EncryptedSharedPreferences backend it had before.
|
|
22
|
+
factory AuthSecuredStorage.fromEnv() {
|
|
23
|
+
if (kIsWeb) return _WebAuthStorage();
|
|
24
|
+
return _NativeAuthStorage(storage: const FlutterSecureStorage());
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class _NativeAuthStorage implements AuthSecuredStorage {
|
|
29
|
+
final FlutterSecureStorage _storage;
|
|
18
30
|
|
|
19
|
-
|
|
20
|
-
storage: const FlutterSecureStorage(),
|
|
21
|
-
);
|
|
31
|
+
_NativeAuthStorage({required FlutterSecureStorage storage}) : _storage = storage;
|
|
22
32
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}) async {
|
|
33
|
+
@override
|
|
34
|
+
Future<void> write({required Credentials value}) async {
|
|
26
35
|
await _storage.write(key: _kUserIdKey, value: value.id);
|
|
27
36
|
await _storage.write(key: _kTokenKey, value: value.token);
|
|
28
37
|
}
|
|
29
38
|
|
|
39
|
+
@override
|
|
30
40
|
Future<Credentials?> read() async {
|
|
31
41
|
final userId = await _storage.read(key: _kUserIdKey);
|
|
32
42
|
final token = await _storage.read(key: _kTokenKey);
|
|
33
43
|
if (userId != null && token != null) {
|
|
34
|
-
return Credentials(
|
|
35
|
-
id: userId,
|
|
36
|
-
token: token,
|
|
37
|
-
);
|
|
44
|
+
return Credentials(id: userId, token: token);
|
|
38
45
|
}
|
|
39
46
|
return null;
|
|
40
47
|
}
|
|
41
48
|
|
|
49
|
+
@override
|
|
42
50
|
Future<void> clear() async {
|
|
43
51
|
await _storage.delete(key: _kUserIdKey);
|
|
44
52
|
await _storage.delete(key: _kTokenKey);
|
|
45
53
|
}
|
|
46
54
|
}
|
|
55
|
+
|
|
56
|
+
class _WebAuthStorage implements AuthSecuredStorage {
|
|
57
|
+
Future<SharedPreferences> get _prefs => SharedPreferences.getInstance();
|
|
58
|
+
|
|
59
|
+
@override
|
|
60
|
+
Future<void> write({required Credentials value}) async {
|
|
61
|
+
final prefs = await _prefs;
|
|
62
|
+
await prefs.setString(_kUserIdKey, value.id);
|
|
63
|
+
if (value.token != null) {
|
|
64
|
+
await prefs.setString(_kTokenKey, value.token!);
|
|
65
|
+
} else {
|
|
66
|
+
await prefs.remove(_kTokenKey);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@override
|
|
71
|
+
Future<Credentials?> read() async {
|
|
72
|
+
final prefs = await _prefs;
|
|
73
|
+
final userId = prefs.getString(_kUserIdKey);
|
|
74
|
+
final token = prefs.getString(_kTokenKey);
|
|
75
|
+
if (userId != null && token != null) {
|
|
76
|
+
return Credentials(id: userId, token: token);
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@override
|
|
82
|
+
Future<void> clear() async {
|
|
83
|
+
final prefs = await _prefs;
|
|
84
|
+
await prefs.remove(_kUserIdKey);
|
|
85
|
+
await prefs.remove(_kTokenKey);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -4,6 +4,8 @@ import 'package:kasy_kit/core/theme/colors.dart';
|
|
|
4
4
|
import 'package:kasy_kit/core/theme/providers/kasy_theme.dart';
|
|
5
5
|
import 'package:kasy_kit/core/theme/texts.dart';
|
|
6
6
|
import 'package:kasy_kit/core/theme/theme_data/theme_data_factory.dart';
|
|
7
|
+
import 'package:kasy_kit/core/theme/web_background_sync.dart'
|
|
8
|
+
if (dart.library.js_interop) 'package:kasy_kit/core/theme/web_background_sync_web.dart';
|
|
7
9
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
8
10
|
|
|
9
11
|
/// We use this to access the theme from the BuildContext in all our widgets
|
|
@@ -182,6 +184,7 @@ class AppTheme with ChangeNotifier, WidgetsBindingObserver {
|
|
|
182
184
|
statusBarIconBrightness: isLight ? Brightness.dark : Brightness.light,
|
|
183
185
|
),
|
|
184
186
|
);
|
|
187
|
+
syncWebBackgroundColor(isLight ? Brightness.light : Brightness.dark);
|
|
185
188
|
}
|
|
186
189
|
|
|
187
190
|
ThemeData get dark {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
import 'package:web/web.dart' as web;
|
|
3
|
+
|
|
4
|
+
void syncWebBackgroundColor(Brightness brightness) {
|
|
5
|
+
final color = brightness == Brightness.dark ? '#000000' : '#FFFFFF';
|
|
6
|
+
(web.document.documentElement as web.HTMLElement?)
|
|
7
|
+
?.style
|
|
8
|
+
.setProperty('background-color', color);
|
|
9
|
+
web.document.body?.style.setProperty('background-color', color);
|
|
10
|
+
final views = web.document.querySelectorAll(
|
|
11
|
+
'flutter-view, flt-glass-pane, flt-scene-host',
|
|
12
|
+
);
|
|
13
|
+
for (var i = 0; i < views.length; i++) {
|
|
14
|
+
(views.item(i) as web.HTMLElement?)
|
|
15
|
+
?.style
|
|
16
|
+
.setProperty('background-color', color);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -237,8 +237,7 @@ class _WebDevicePreviewState extends State<WebDevicePreview> {
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
void _toggleInspector() {
|
|
240
|
-
|
|
241
|
-
!WidgetsBinding.instance.debugShowWidgetInspectorOverride;
|
|
240
|
+
devInspectorActiveNotifier.value = !devInspectorActiveNotifier.value;
|
|
242
241
|
}
|
|
243
242
|
|
|
244
243
|
void _copyInspection() {
|
|
@@ -383,8 +382,7 @@ class _WebDevicePreviewState extends State<WebDevicePreview> {
|
|
|
383
382
|
_landscapeNotifier,
|
|
384
383
|
_textScaleNotifier,
|
|
385
384
|
_localeNotifier,
|
|
386
|
-
|
|
387
|
-
.debugShowWidgetInspectorOverrideNotifier,
|
|
385
|
+
devInspectorActiveNotifier,
|
|
388
386
|
]),
|
|
389
387
|
builder: (context, child) => _PreviewControls(
|
|
390
388
|
platform: _platform,
|
|
@@ -395,8 +393,7 @@ class _WebDevicePreviewState extends State<WebDevicePreview> {
|
|
|
395
393
|
isLandscape: _landscapeNotifier.value,
|
|
396
394
|
textScale: _textScaleNotifier.value,
|
|
397
395
|
currentLocale: _localeNotifier.value,
|
|
398
|
-
inspectorEnabled:
|
|
399
|
-
.instance.debugShowWidgetInspectorOverride,
|
|
396
|
+
inspectorEnabled: devInspectorActiveNotifier.value,
|
|
400
397
|
onPlatformChanged: _setPlatform,
|
|
401
398
|
onPrev: _prev,
|
|
402
399
|
onNext: _next,
|
|
@@ -34,6 +34,12 @@ class FirebaseAuthenticationApi implements AuthenticationApi {
|
|
|
34
34
|
|
|
35
35
|
@override
|
|
36
36
|
Future<void> init() async {
|
|
37
|
+
if (kIsWeb) {
|
|
38
|
+
// Force LOCAL persistence (IndexedDB) on web. It's the default for the JS
|
|
39
|
+
// SDK, but Firebase's web bootstrap sometimes resets it to SESSION when
|
|
40
|
+
// the page loads through a service worker — being explicit avoids that.
|
|
41
|
+
await _auth.setPersistence(Persistence.LOCAL);
|
|
42
|
+
}
|
|
37
43
|
_auth.userChanges().listen((event) {
|
|
38
44
|
if (!hasInit) {
|
|
39
45
|
hasInit = true;
|
|
@@ -181,6 +181,7 @@ const Set<String> _kReadyComponents = <String>{
|
|
|
181
181
|
'Button',
|
|
182
182
|
'Card',
|
|
183
183
|
'Checkbox',
|
|
184
|
+
'DatePicker',
|
|
184
185
|
'Design System',
|
|
185
186
|
'Dialog',
|
|
186
187
|
'Hover',
|
|
@@ -205,6 +206,7 @@ const Set<String> _kWebReadyComponents = <String>{
|
|
|
205
206
|
'Button',
|
|
206
207
|
'Card',
|
|
207
208
|
'Checkbox',
|
|
209
|
+
'DatePicker',
|
|
208
210
|
'Design System',
|
|
209
211
|
'Dialog',
|
|
210
212
|
'Hover',
|
|
@@ -220,7 +222,6 @@ const Set<String> _kWebReadyComponents = <String>{
|
|
|
220
222
|
const Set<String> _kUrgentComponents = <String>{
|
|
221
223
|
'Switch',
|
|
222
224
|
'Radio Group',
|
|
223
|
-
'DatePicker',
|
|
224
225
|
};
|
|
225
226
|
|
|
226
227
|
const List<_CatalogRow> _kCatalog = [
|
|
@@ -236,6 +237,7 @@ const List<_CatalogRow> _kCatalog = [
|
|
|
236
237
|
_CatalogRow('Button'),
|
|
237
238
|
_CatalogRow('Card'),
|
|
238
239
|
_CatalogRow('Checkbox'),
|
|
240
|
+
_CatalogRow('DatePicker'),
|
|
239
241
|
_CatalogRow('Dialog'),
|
|
240
242
|
_CatalogRow('Hover'),
|
|
241
243
|
_CatalogRow('Sidebar'),
|
|
@@ -246,7 +248,6 @@ const List<_CatalogRow> _kCatalog = [
|
|
|
246
248
|
_CatalogRow('TextFieldOTP'),
|
|
247
249
|
_CatalogRow('Toast'),
|
|
248
250
|
// Prioridade: Urgente (_kUrgentComponents), A–Z
|
|
249
|
-
_CatalogRow('DatePicker'),
|
|
250
251
|
_CatalogRow('Radio Group'),
|
|
251
252
|
_CatalogRow('Switch'),
|
|
252
253
|
_CatalogRow('Tabs'),
|