281 lines
5.1 KiB
Markdown
281 lines
5.1 KiB
Markdown
file lib/widgets/common/dialogs/miui_safe_dialog.dart
|
||
import 'package:aves/widgets/common/dialogs/miu>
|
||
|
||
Perfetto Fabio — ora che il tuo primo dialog MIUI‑safe funziona, ti preparo una guida completa, chiara e salvabile, che puoi applicare a qualsiasi dialog di Aves (o di qualunque app Flutter) per renderlo completamente MIUI‑proof.
|
||
|
||
È una guida pratica, con pattern, checklist e snippet pronti da incollare.
|
||
|
||
---
|
||
|
||
⭐ GUIDA COMPLETA — Come rendere qualsiasi dialog Flutter MIUI‑safe
|
||
|
||
🎯 Obiettivo
|
||
Evitare i problemi tipici di MIUI:
|
||
|
||
- tastiera lenta o che non si apre
|
||
- impossibilità di scrivere
|
||
- animazioni IME infinite
|
||
- callback pendenti che esplodono
|
||
- errori tipo:
|
||
- 'attached': is not true
|
||
- '_elements.contains(element)': is not true
|
||
- '_dependents.isEmpty': is not true
|
||
|
||
---
|
||
|
||
🧩 1. Usa sempre il wrapper globale
|
||
|
||
Invece di:
|
||
|
||
`dart
|
||
showDialog(...)
|
||
`
|
||
|
||
usa:
|
||
|
||
`dart
|
||
showMiuiSafeDialog(...)
|
||
`
|
||
|
||
Questo evita problemi di lifecycle quando MIUI distrugge la Activity.
|
||
|
||
---
|
||
|
||
🧩 2. Mai usare autofocus: true nei dialog
|
||
|
||
MIUI odia autofocus: true nei dialog.
|
||
|
||
Sostituisci sempre con:
|
||
|
||
`dart
|
||
autofocus: false
|
||
`
|
||
|
||
---
|
||
|
||
🧩 3. Mai usare addPostFrameCallback per il focus
|
||
|
||
Questo crea callback pendenti che MIUI può far esplodere.
|
||
|
||
Sostituisci con:
|
||
|
||
`dart
|
||
Future.microtask(() async {
|
||
if (!context.mounted) return;
|
||
await Future.delayed(const Duration(milliseconds: 30)); // MIUI-safe
|
||
if (!context.mounted) return;
|
||
FocusScope.of(context).requestFocus(focusNode);
|
||
});
|
||
`
|
||
|
||
---
|
||
|
||
🧩 4. Sempre controllare context.mounted
|
||
|
||
Ogni volta che fai:
|
||
|
||
- Navigator.pop
|
||
- FocusScope.of(context)
|
||
- setState
|
||
- requestFocus
|
||
|
||
aggiungi:
|
||
|
||
`dart
|
||
if (!context.mounted) return;
|
||
`
|
||
|
||
MIUI distrugge la Activity mentre il dialog è aperto → senza questa guardia, crash.
|
||
|
||
---
|
||
|
||
🧩 5. Mai usare FocusManager.instance.primaryFocus?.unfocus()
|
||
|
||
MIUI applica una animazione IME di 600 ms quando fai unfocus().
|
||
|
||
Se devi chiudere la tastiera:
|
||
|
||
`dart
|
||
FocusScope.of(context).unfocus();
|
||
`
|
||
|
||
oppure non farlo affatto (meglio).
|
||
|
||
---
|
||
|
||
🧩 6. Usa sempre un FocusNode dedicato
|
||
|
||
Per ogni TextField in un dialog:
|
||
|
||
`dart
|
||
final focusNode = FocusNode();
|
||
`
|
||
|
||
e poi:
|
||
|
||
`dart
|
||
focusNode.dispose();
|
||
`
|
||
|
||
dopo il dialog.
|
||
|
||
---
|
||
|
||
🧩 7. Usa sempre un delay minimo per MIUI
|
||
|
||
MIUI ha bisogno di ~30 ms per agganciare il nuovo ViewRootImpl.
|
||
|
||
Quindi:
|
||
|
||
`dart
|
||
await Future.delayed(const Duration(milliseconds: 30));
|
||
`
|
||
|
||
---
|
||
|
||
🧩 8. Usa i tuoi dialog MIUI‑safe
|
||
|
||
Hai già:
|
||
|
||
- MiuiSafeTextDialog
|
||
- showMiuiSafeDialog
|
||
|
||
Puoi creare altri tipi:
|
||
|
||
✔ Conferma
|
||
|
||
`dart
|
||
class MiuiSafeConfirmDialog extends StatelessWidget {
|
||
final String title;
|
||
final String message;
|
||
|
||
const MiuiSafeConfirmDialog({
|
||
super.key,
|
||
required this.title,
|
||
required this.message,
|
||
});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return AlertDialog(
|
||
title: Text(title),
|
||
content: Text(message),
|
||
actions: [
|
||
TextButton(
|
||
onPressed: () {
|
||
if (!context.mounted) return;
|
||
Navigator.of(context).pop(false);
|
||
},
|
||
child: const Text('Annulla'),
|
||
),
|
||
TextButton(
|
||
onPressed: () {
|
||
if (!context.mounted) return;
|
||
Navigator.of(context).pop(true);
|
||
},
|
||
child: const Text('OK'),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
}
|
||
`
|
||
|
||
✔ Lista di opzioni
|
||
|
||
`dart
|
||
class MiuiSafeListDialog<T> extends StatelessWidget {
|
||
final String title;
|
||
final List<T> items;
|
||
final String Function(T) label;
|
||
|
||
const MiuiSafeListDialog({
|
||
super.key,
|
||
required this.title,
|
||
required this.items,
|
||
required this.label,
|
||
});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return AlertDialog(
|
||
title: Text(title),
|
||
content: SizedBox(
|
||
width: double.maxFinite,
|
||
child: ListView.builder(
|
||
shrinkWrap: true,
|
||
itemCount: items.length,
|
||
itemBuilder: (_, i) {
|
||
final item = items[i];
|
||
return ListTile(
|
||
title: Text(label(item)),
|
||
onTap: () {
|
||
if (!context.mounted) return;
|
||
Navigator.of(context).pop(item);
|
||
},
|
||
);
|
||
},
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|
||
`
|
||
|
||
---
|
||
|
||
🧩 9. Checklist finale (salvala!)
|
||
|
||
✔ Per ogni dialog:
|
||
- [ ] usare showMiuiSafeDialog
|
||
- [ ] niente autofocus: true
|
||
- [ ] niente addPostFrameCallback
|
||
- [ ] usare Future.microtask + delay MIUI
|
||
- [ ] controllare context.mounted
|
||
- [ ] evitare unfocus() con delay
|
||
- [ ] usare FocusNode dedicato
|
||
- [ ] evitare animazioni IME
|
||
- [ ] evitare callback pendenti
|
||
|
||
---
|
||
|
||
🧩 10. Esempio completo (da salvare)
|
||
|
||
Prima (Flutter normale)
|
||
|
||
`dart
|
||
showDialog(
|
||
context: context,
|
||
builder: (_) => AlertDialog(
|
||
content: TextField(autofocus: true),
|
||
),
|
||
);
|
||
`
|
||
|
||
Dopo (MIUI‑safe)
|
||
|
||
`dart
|
||
showMiuiSafeDialog(
|
||
context: context,
|
||
builder: (ctx) => MiuiSafeTextDialog(
|
||
title: 'Titolo',
|
||
label: 'Campo',
|
||
hint: 'Scrivi qui',
|
||
initialText: '',
|
||
),
|
||
);
|
||
`
|
||
|
||
---
|
||
|
||
🎉 Risultato
|
||
|
||
Con questa guida puoi convertire qualsiasi dialog di Aves in versione MIUI‑safe:
|
||
|
||
- tastiera istantanea
|
||
- input immediato
|
||
- nessun lag
|
||
- nessun crash
|
||
- nessun assert del framework
|
||
- nessun callback pendente
|
||
- nessuna animazione IME infinita
|