Chore — Rename loading_widgets.dart → loading_skeletons.dart
Commit:
chore(loading): rename loading_widgets.dart to loading_skeletons.dart and document split· Branch:edit/mobile-audit-ux-polish· Audit: 2026-04-20 UX polish (finding L3, riveduto)
Cosa non andava (L3, riveduto)
L’audit iniziale aveva marcato L3 come “doppio file loading_widget.dart”. La verifica ha mostrato una situazione più sottile:
Due file esistevano nel repo, distinti per una singola lettera:
lib/shared/widgets/loading_widget.dart(singolare)lib/shared/widgets/loading_widgets.dart(plurale)
Non erano duplicati — contenevano classi distinte:
| File | Classi esportate | Registro |
|---|---|---|
loading_widget.dart | LoadingWidget, LoadingOverlay, ShimmerLoading, ListLoadingWidget, CardLoadingWidget, PullToRefreshLoading, InfiniteScrollLoading | Generico, theme-aware (usa Theme.of(context).colorScheme) |
loading_widgets.dart | LoadingScreen, LoadingIndicator, EventCardSkeleton, EventListSkeleton, ProfileHeaderSkeleton, ShimmerBox, InlineLoader, LoadMoreIndicator, RefreshLoadingIndicator | Brand-locked (usa AppTheme.electricCoral), skeleton domain-specific (event card, profile) |
Il vero bug era la trap cognitiva del naming:
- Due file che differiscono per un carattere → impossibile distinguerli in autocomplete.
- Contenuti sovrapposti in concetto (
LoadingWidgetvsLoadingIndicator,InfiniteScrollLoadingvsLoadMoreIndicator) → confusion su quale importare. - Solo
feed_screen.dartconsumava il plurale, e solo perEventListSkeleton→ singleton-consumer, zero impact per rename.
Decisione
Rinominare il plurale in qualcosa di semanticamente distinto. Scelta: loading_skeletons.dart — riflette il contenuto prevalente (skeleton loader per card, profile, event).
Lasciare il singolare con il nome attuale loading_widget.dart — è il file con il consumer count più alto (5 screens) e un rename propagherebbe più diff.
Aggiunto header docstring (/// ... + library; directive) a entrambi i file per esplicitare il contratto:
// loading_widget.dart
/// Generic, theme-aware loading primitives.
/// Rule of thumb:
/// - Need a neutral spinner/overlay/shimmer primitive? → this file.
/// - Need an event-card or profile-header skeleton? → loading_skeletons.dart.
library;// loading_skeletons.dart
/// Brand-locked loading skeletons and branded indicators.
/// Rule of thumb:
/// - Need a shimmer that looks like an event card? → this file.
/// - Need a neutral spinner on a secondary surface? → loading_widget.dart.
library;La library; directive serve a Dart per associare il docstring al file, non alla prima dichiarazione sottostante. Senza library;, flutter analyze emette warning “Dangling library doc comment”.
Diff riassuntivo
Rinominato
lib/shared/widgets/loading_widgets.dart → lib/shared/widgets/loading_skeletons.dart
Git mv preserva la storia del file — git log --follow lib/shared/widgets/loading_skeletons.dart mostra ancora tutti i commit precedenti.
lib/features/feed/screens/feed_screen.dart
-import 'package:flow_app/shared/widgets/loading_widgets.dart';
+import 'package:flow_app/shared/widgets/loading_skeletons.dart';Header aggiunto a entrambi i file (estratto)
Vedi sopra — /// ... + library; directive.
Perché non “merge tutto in un file solo”
Opzione scartata: consolidare i due file in un unico lib/shared/widgets/loading.dart. Motivazioni:
- Separation of concerns semantica: “primitive neutre” e “skeleton brandati” sono due layer diversi. Primitive → base, skeleton → usa le primitive + brand. Tenerle separate documenta la gerarchia.
- Tree-shaking Dart non è Rollup: un file con 15 classi viene compilato tutto insieme — non c’è il beneficio “import solo ciò che serve” che darebbe un merge in ESM/Rollup.
- Rollback-safety: un rename è atomico. Un merge richiede decidere chi tiene i nomi, risolvere conflitti API, aggiornare 6+ import. Un task da ~1h → rimandato a pass dedicato.
Decisioni deferite
- Merge delle classi analoghe:
LoadingWidget(singolare) eLoadingIndicator(plurale, brand) hanno lo stesso API. Idealmente ne resta una sola — quella brand, perché il singolare non è usato mai con il suo colore custom. Task futuro. InfiniteScrollLoadingvsLoadMoreIndicator: entrambi fanno lo stesso lavoro. Eliminare uno richiede scegliere quale API è migliore (message-aware vs zero-config). Task futuro.ShimmerLoading(custom) vsShimmer.fromColors(packageshimmer): il singolare ha una re-implementazione del pattern shimmer; il plurale usa il package. Due implementazioni → un po’ di waste. La re-implementazione fu probabilmente creata prima che il package venisse aggiunto. Candidato per sostituzione + rimozione di un file. Task futuro.
Verifica
flutter analyze lib/shared/widgets/loading_skeletons.dart \
lib/shared/widgets/loading_widget.dart \
lib/features/feed/screens/feed_screen.dart
# → No issues found!Lessons
- “Doppio file” ≠ “duplicato”. L’audit aveva intuito un problema (due nomi quasi identici) ma diagnosticato male (“doppio file”). Un file di audit che descrive solo i sintomi senza verificare le classi è una guida utile ma non un piano esecutivo. Sempre aprire i file prima di agire.
- Naming è UX per i dev. Un autocomplete che suggerisce
loading_widget.dartvsloading_widgets.dartè una trap quotidiana: ci si prende il primo suggerito, si importa il wrong file, si scopre solo in build. Differenziare il nome ha zero costo e ripaga per sempre. library;directive è Dart-specifico e non opzionale per docstring top-of-file. In Dart, un///comment prima di un import/class si associa a quella dichiarazione. Per avere un doc del file intero (visibile in dartdoc, IDE hover sul file) servelibrarydirective anonima dopo il comment. Senza, il comment è dangling.- Un consumer solo rende safe il rename. Grep prima dell’azione ha mostrato che solo
feed_screen.dartimporta il plurale. Un rename con 1 import è un minuto di lavoro. Con 20 import sarebbe un task da 10x e coordinamento multi-PR. Regola flow: rename files only when consumer count is low; else wrap + deprecate first.