perf(startup): paint a splash immediately, run init off the first frame

main() awaited all of configureDependencies() (open the encrypted DB,
derive the social key, seed/scan the species catalog) before the first
runApp, so nothing painted until init finished — startup jank visible as
skipped frames. Now main() runs runApp immediately with a Bootstrap
widget that shows a native-splash-matching green splash while the heavy
work runs, then swaps in TaneApp. On failure it shows a localized error
with a retry (configureDependencies is idempotent, so retry is safe).

Adds a bootstrap i18n group (en/es/pt/ast) and a widget test for the
splash/error frame.
This commit is contained in:
vjrj 2026-07-10 17:34:08 +02:00
parent 33d8b2a4d7
commit 71413f3801
12 changed files with 289 additions and 53 deletions

View file

@ -40,6 +40,7 @@ class TranslationsPt extends Translations with BaseTranslations<AppLocale, Trans
// Translations
@override late final _Translations$app$pt app = _Translations$app$pt._(_root);
@override late final _Translations$bootstrap$pt bootstrap = _Translations$bootstrap$pt._(_root);
@override late final _Translations$common$pt common = _Translations$common$pt._(_root);
@override late final _Translations$home$pt home = _Translations$home$pt._(_root);
@override late final _Translations$photo$pt photo = _Translations$photo$pt._(_root);
@ -85,6 +86,17 @@ class _Translations$app$pt extends Translations$app$en {
@override String get title => 'Tanemaki';
}
// Path: bootstrap
class _Translations$bootstrap$pt extends Translations$bootstrap$en {
_Translations$bootstrap$pt._(TranslationsPt root) : this._root = root, super.internal(root);
final TranslationsPt _root; // ignore: unused_field
// Translations
@override String get failed => 'O Tanemaki não conseguiu iniciar';
@override String get retry => 'Tentar de novo';
}
// Path: common
class _Translations$common$pt extends Translations$common$en {
_Translations$common$pt._(TranslationsPt root) : this._root = root, super.internal(root);
@ -1056,6 +1068,8 @@ extension on TranslationsPt {
dynamic _flatMapFunction(String path) {
return switch (path) {
'app.title' => 'Tanemaki',
'bootstrap.failed' => 'O Tanemaki não conseguiu iniciar',
'bootstrap.retry' => 'Tentar de novo',
'common.save' => 'Guardar',
'common.cancel' => 'Cancelar',
'common.delete' => 'Eliminar',