kasy-cli 1.18.0 → 1.19.1

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.
Files changed (34) hide show
  1. package/bin/kasy.js +3 -1
  2. package/lib/commands/new.js +99 -105
  3. package/lib/commands/run.js +34 -6
  4. package/lib/scaffold/backends/firebase/setup-from-scratch.js +79 -0
  5. package/lib/utils/brand.js +1 -1
  6. package/lib/utils/i18n/messages-en.js +6 -0
  7. package/lib/utils/i18n/messages-es.js +6 -0
  8. package/lib/utils/i18n/messages-pt.js +6 -0
  9. package/package.json +1 -2
  10. package/templates/firebase/lib/components/kasy_date_picker.dart +1670 -331
  11. package/templates/firebase/lib/components/kasy_tabs.dart +111 -72
  12. package/templates/firebase/lib/components/kasy_text_area.dart +9 -4
  13. package/templates/firebase/lib/components/kasy_text_field.dart +96 -36
  14. package/templates/firebase/lib/components/kasy_text_field_otp.dart +1 -2
  15. package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +88 -35
  16. package/templates/firebase/lib/core/bottom_menu/bottom_router.dart +7 -43
  17. package/templates/firebase/lib/core/dev_inspector/dev_inspector.dart +118 -16
  18. package/templates/firebase/lib/core/dev_inspector/dev_inspector_service.dart +14 -20
  19. package/templates/firebase/lib/core/security/secured_storage.dart +56 -15
  20. package/templates/firebase/lib/core/theme/providers/theme_provider.dart +3 -0
  21. package/templates/firebase/lib/core/theme/web_background_sync.dart +3 -0
  22. package/templates/firebase/lib/core/theme/web_background_sync_web.dart +18 -0
  23. package/templates/firebase/lib/core/web_device_preview/web_device_preview.dart +3 -6
  24. package/templates/firebase/lib/features/authentication/api/authentication_api.dart +6 -0
  25. package/templates/firebase/lib/features/home/home_components_page.dart +3 -2
  26. package/templates/firebase/lib/features/home/home_components_preview_registry.dart +457 -73
  27. package/templates/firebase/lib/features/home/home_page.dart +17 -40
  28. package/templates/firebase/lib/features/notifications/ui/notifications_page.dart +1 -16
  29. package/templates/firebase/lib/features/notifications/ui/widgets/empty_notifications.dart +0 -4
  30. package/templates/firebase/lib/main.dart +34 -34
  31. package/templates/firebase/pubspec.yaml +1 -0
  32. package/templates/firebase/storage.cors.json +8 -0
  33. package/templates/firebase/web/index.html +15 -2
  34. 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
- class _HomeInnerRoute extends StatefulWidget {
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
- State<_HomeInnerRoute> createState() => _HomeInnerRouteState();
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
- WidgetsBinding.instance.debugShowWidgetInspectorOverrideNotifier
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
- WidgetsBinding.instance.debugShowWidgetInspectorOverrideNotifier
94
- .removeListener(_handleInspectorOverrideChanged);
95
- if (_active) {
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
- WidgetsBinding.instance.debugShowWidgetInspectorOverride = false;
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
- setState(() => _active = value);
196
- WidgetsBinding.instance.debugShowWidgetInspectorOverride = value;
212
+ devInspectorActiveNotifier.value = value;
197
213
  HapticFeedback.lightImpact();
198
214
  }
199
215
 
200
- void _handleInspectorOverrideChanged() {
201
- final bool override =
202
- WidgetsBinding.instance.debugShowWidgetInspectorOverride;
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(() => _active = override);
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 = DevInspectorService.inspectSelected();
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
- /// Reads the widget currently selected in Flutter's native inspector.
284
- static DevInspectorInfo? inspectSelected() {
285
- if (!kDebugMode) return null;
286
- try {
287
- // ignore: invalid_use_of_visible_for_testing_member
288
- final RenderObject? renderObject =
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
- // ignore: invalid_use_of_protected_member
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
- final FlutterSecureStorage _storage;
14
+ abstract class AuthSecuredStorage {
15
+ Future<void> write({required Credentials value});
16
+ Future<Credentials?> read();
17
+ Future<void> clear();
14
18
 
15
- AuthSecuredStorage({
16
- required FlutterSecureStorage storage,
17
- }) : _storage = storage;
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
- factory AuthSecuredStorage.fromEnv() => AuthSecuredStorage(
20
- storage: const FlutterSecureStorage(),
21
- );
31
+ _NativeAuthStorage({required FlutterSecureStorage storage}) : _storage = storage;
22
32
 
23
- Future<void> write({
24
- required Credentials value,
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,3 @@
1
+ import 'package:flutter/material.dart';
2
+
3
+ void syncWebBackgroundColor(Brightness brightness) {}
@@ -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
- WidgetsBinding.instance.debugShowWidgetInspectorOverride =
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
- WidgetsBinding.instance
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: WidgetsBinding
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'),