From 4b209d6752b2ff3cf2c323ea262136672e26400f Mon Sep 17 00:00:00 2001 From: vjrj Date: Wed, 8 Jul 2026 14:05:12 +0200 Subject: [PATCH] feat(shell): real sprout logo on home + basic Settings screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bundle the real sprout logo (docs/logo.png → assets/logo.png) and show it on the home menu instead of a Material icon. - Settings screen: switch language (Español / English / system) via slang, plus an About tile (logo, app name, one-line description). Drawer "Settings" is now active and routes to /settings. Tests: drawer Settings opens the screen and shows the language options. 54 green. --- apps/app_seeds/assets/logo.png | Bin 0 -> 17094 bytes apps/app_seeds/lib/app.dart | 5 ++ apps/app_seeds/lib/i18n/en.i18n.json | 8 ++ apps/app_seeds/lib/i18n/es.i18n.json | 8 ++ apps/app_seeds/lib/i18n/strings.g.dart | 4 +- apps/app_seeds/lib/i18n/strings_en.g.dart | 34 ++++++++ apps/app_seeds/lib/i18n/strings_es.g.dart | 22 +++++ apps/app_seeds/lib/ui/app_drawer.dart | 9 +- apps/app_seeds/lib/ui/home_screen.dart | 10 +-- apps/app_seeds/lib/ui/settings_screen.dart | 86 +++++++++++++++++++ apps/app_seeds/pubspec.yaml | 1 + apps/app_seeds/test/ui/home_screen_test.dart | 18 ++++ 12 files changed, 196 insertions(+), 9 deletions(-) create mode 100644 apps/app_seeds/assets/logo.png create mode 100644 apps/app_seeds/lib/ui/settings_screen.dart diff --git a/apps/app_seeds/assets/logo.png b/apps/app_seeds/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a2db48227a6ff852eb50b622fcde71cbc42a8f91 GIT binary patch literal 17094 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 T6}trf<^%+5Vr|@n@w)auzrzhg literal 0 HcmV?d00001 diff --git a/apps/app_seeds/lib/app.dart b/apps/app_seeds/lib/app.dart index 665cfd7..c392f83 100644 --- a/apps/app_seeds/lib/app.dart +++ b/apps/app_seeds/lib/app.dart @@ -10,6 +10,7 @@ import 'state/inventory_cubit.dart'; import 'state/variety_detail_cubit.dart'; import 'ui/home_screen.dart'; import 'ui/inventory_list_screen.dart'; +import 'ui/settings_screen.dart'; import 'ui/theme.dart'; import 'ui/variety_detail_screen.dart'; @@ -27,6 +28,10 @@ class TaneApp extends StatelessWidget { return GoRouter( routes: [ GoRoute(path: '/', builder: (context, state) => const HomeScreen()), + GoRoute( + path: '/settings', + builder: (context, state) => const SettingsScreen(), + ), GoRoute( path: '/inventory', builder: (context, state) => BlocProvider( diff --git a/apps/app_seeds/lib/i18n/en.i18n.json b/apps/app_seeds/lib/i18n/en.i18n.json index 1e586b1..67f7e51 100644 --- a/apps/app_seeds/lib/i18n/en.i18n.json +++ b/apps/app_seeds/lib/i18n/en.i18n.json @@ -23,6 +23,14 @@ "following": "Following", "settings": "Settings" }, + "settings": { + "language": "Language", + "systemLanguage": "System language", + "langEs": "Español", + "langEn": "English", + "about": "About", + "aboutText": "Local-first, encrypted inventory for traditional seeds. AGPL-3.0." + }, "inventory": { "title": "Inventory", "searchHint": "Search seeds", diff --git a/apps/app_seeds/lib/i18n/es.i18n.json b/apps/app_seeds/lib/i18n/es.i18n.json index d11401c..87cc7dd 100644 --- a/apps/app_seeds/lib/i18n/es.i18n.json +++ b/apps/app_seeds/lib/i18n/es.i18n.json @@ -23,6 +23,14 @@ "following": "Siguiendo", "settings": "Ajustes" }, + "settings": { + "language": "Idioma", + "systemLanguage": "Idioma del sistema", + "langEs": "Español", + "langEn": "English", + "about": "Acerca de", + "aboutText": "Inventario local y cifrado para semillas tradicionales. AGPL-3.0." + }, "inventory": { "title": "Inventario", "searchHint": "Buscar semillas", diff --git a/apps/app_seeds/lib/i18n/strings.g.dart b/apps/app_seeds/lib/i18n/strings.g.dart index 31be84b..abe1c4b 100644 --- a/apps/app_seeds/lib/i18n/strings.g.dart +++ b/apps/app_seeds/lib/i18n/strings.g.dart @@ -4,9 +4,9 @@ /// To regenerate, run: `dart run slang` /// /// Locales: 2 -/// Strings: 244 (122 per locale) +/// Strings: 256 (128 per locale) /// -/// Built on 2026-07-08 at 10:27 UTC +/// Built on 2026-07-08 at 12:01 UTC // coverage:ignore-file // ignore_for_file: type=lint, unused_import diff --git a/apps/app_seeds/lib/i18n/strings_en.g.dart b/apps/app_seeds/lib/i18n/strings_en.g.dart index 5376688..80e2540 100644 --- a/apps/app_seeds/lib/i18n/strings_en.g.dart +++ b/apps/app_seeds/lib/i18n/strings_en.g.dart @@ -44,6 +44,7 @@ class Translations with BaseTranslations { late final Translations$common$en common = Translations$common$en.internal(_root); late final Translations$home$en home = Translations$home$en.internal(_root); late final Translations$menu$en menu = Translations$menu$en.internal(_root); + late final Translations$settings$en settings = Translations$settings$en.internal(_root); late final Translations$inventory$en inventory = Translations$inventory$en.internal(_root); late final Translations$quickAdd$en quickAdd = Translations$quickAdd$en.internal(_root); late final Translations$detail$en detail = Translations$detail$en.internal(_root); @@ -139,6 +140,33 @@ class Translations$menu$en { String get settings => 'Settings'; } +// Path: settings +class Translations$settings$en { + Translations$settings$en.internal(this._root); + + final Translations _root; // ignore: unused_field + + // Translations + + /// en: 'Language' + String get language => 'Language'; + + /// en: 'System language' + String get systemLanguage => 'System language'; + + /// en: 'Español' + String get langEs => 'Español'; + + /// en: 'English' + String get langEn => 'English'; + + /// en: 'About' + String get about => 'About'; + + /// en: 'Local-first, encrypted inventory for traditional seeds. AGPL-3.0.' + String get aboutText => 'Local-first, encrypted inventory for traditional seeds. AGPL-3.0.'; +} + // Path: inventory class Translations$inventory$en { Translations$inventory$en.internal(this._root); @@ -752,6 +780,12 @@ extension on Translations { 'menu.wishlist' => 'Wishlist', 'menu.following' => 'Following', 'menu.settings' => 'Settings', + 'settings.language' => 'Language', + 'settings.systemLanguage' => 'System language', + 'settings.langEs' => 'Español', + 'settings.langEn' => 'English', + 'settings.about' => 'About', + 'settings.aboutText' => 'Local-first, encrypted inventory for traditional seeds. AGPL-3.0.', 'inventory.title' => 'Inventory', 'inventory.searchHint' => 'Search seeds', 'inventory.empty' => 'No seeds yet. Tap + to add your first.', diff --git a/apps/app_seeds/lib/i18n/strings_es.g.dart b/apps/app_seeds/lib/i18n/strings_es.g.dart index a224e8b..1591189 100644 --- a/apps/app_seeds/lib/i18n/strings_es.g.dart +++ b/apps/app_seeds/lib/i18n/strings_es.g.dart @@ -43,6 +43,7 @@ class TranslationsEs extends Translations with BaseTranslations 'Ajustes'; } +// Path: settings +class _Translations$settings$es extends Translations$settings$en { + _Translations$settings$es._(TranslationsEs root) : this._root = root, super.internal(root); + + final TranslationsEs _root; // ignore: unused_field + + // Translations + @override String get language => 'Idioma'; + @override String get systemLanguage => 'Idioma del sistema'; + @override String get langEs => 'Español'; + @override String get langEn => 'English'; + @override String get about => 'Acerca de'; + @override String get aboutText => 'Inventario local y cifrado para semillas tradicionales. AGPL-3.0.'; +} + // Path: inventory class _Translations$inventory$es extends Translations$inventory$en { _Translations$inventory$es._(TranslationsEs root) : this._root = root, super.internal(root); @@ -529,6 +545,12 @@ extension on TranslationsEs { 'menu.wishlist' => 'Lista de deseos', 'menu.following' => 'Siguiendo', 'menu.settings' => 'Ajustes', + 'settings.language' => 'Idioma', + 'settings.systemLanguage' => 'Idioma del sistema', + 'settings.langEs' => 'Español', + 'settings.langEn' => 'English', + 'settings.about' => 'Acerca de', + 'settings.aboutText' => 'Inventario local y cifrado para semillas tradicionales. AGPL-3.0.', 'inventory.title' => 'Inventario', 'inventory.searchHint' => 'Buscar semillas', 'inventory.empty' => 'Aún no hay semillas. Toca + para añadir la primera.', diff --git a/apps/app_seeds/lib/ui/app_drawer.dart b/apps/app_seeds/lib/ui/app_drawer.dart index 72aaa2e..172cb46 100644 --- a/apps/app_seeds/lib/ui/app_drawer.dart +++ b/apps/app_seeds/lib/ui/app_drawer.dart @@ -38,7 +38,14 @@ class AppDrawer extends StatelessWidget { _SoonItem(icon: Icons.group_outlined, label: t.menu.following), const Spacer(), const Divider(), - _SoonItem(icon: Icons.settings_outlined, label: t.menu.settings), + _ActiveItem( + icon: const Icon(Icons.settings_outlined), + label: t.menu.settings, + onTap: () { + Navigator.of(context).pop(); + context.go('/settings'); + }, + ), ], ), ), diff --git a/apps/app_seeds/lib/ui/home_screen.dart b/apps/app_seeds/lib/ui/home_screen.dart index be2b9e7..97e51db 100644 --- a/apps/app_seeds/lib/ui/home_screen.dart +++ b/apps/app_seeds/lib/ui/home_screen.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -import 'package:material_symbols_icons/symbols.dart'; import '../i18n/strings.g.dart'; import 'app_drawer.dart'; @@ -31,11 +30,10 @@ class HomeScreen extends StatelessWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - const Icon( - Symbols.eco, - size: 120, - color: seedGreen, - fill: 1, + Image.asset( + 'assets/logo.png', + height: 140, + fit: BoxFit.contain, ), const SizedBox(height: 40), _MenuButton( diff --git a/apps/app_seeds/lib/ui/settings_screen.dart b/apps/app_seeds/lib/ui/settings_screen.dart new file mode 100644 index 0000000..bba7b6a --- /dev/null +++ b/apps/app_seeds/lib/ui/settings_screen.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; + +import '../i18n/strings.g.dart'; +import 'theme.dart'; + +/// Basic settings: app language and an "about" section. +class SettingsScreen extends StatelessWidget { + const SettingsScreen({super.key}); + + @override + Widget build(BuildContext context) { + final t = context.t; + final current = TranslationProvider.of(context).flutterLocale.languageCode; + return Scaffold( + appBar: AppBar(title: Text(t.menu.settings)), + body: ListView( + children: [ + _SectionHeader(t.settings.language), + _LanguageTile( + label: t.settings.langEs, + selected: current == 'es', + onTap: () => LocaleSettings.setLocale(AppLocale.es), + ), + _LanguageTile( + label: t.settings.langEn, + selected: current == 'en', + onTap: () => LocaleSettings.setLocale(AppLocale.en), + ), + ListTile( + leading: const Icon(Icons.smartphone_outlined), + title: Text(t.settings.systemLanguage), + onTap: () => LocaleSettings.useDeviceLocale(), + ), + const Divider(), + _SectionHeader(t.settings.about), + ListTile( + leading: Image.asset('assets/logo.png', width: 32), + title: Text(t.app.title), + subtitle: Text(t.settings.aboutText), + ), + ], + ), + ); + } +} + +class _SectionHeader extends StatelessWidget { + const _SectionHeader(this.text); + + final String text; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.fromLTRB(16, 16, 16, 4), + child: Text( + text, + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: seedGreenDark, + fontWeight: FontWeight.w600, + ), + ), + ); + } +} + +class _LanguageTile extends StatelessWidget { + const _LanguageTile({ + required this.label, + required this.selected, + required this.onTap, + }); + + final String label; + final bool selected; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return ListTile( + title: Text(label), + trailing: selected ? const Icon(Icons.check, color: seedGreen) : null, + onTap: onTap, + ); + } +} diff --git a/apps/app_seeds/pubspec.yaml b/apps/app_seeds/pubspec.yaml index 17faf7c..4b6e7b1 100644 --- a/apps/app_seeds/pubspec.yaml +++ b/apps/app_seeds/pubspec.yaml @@ -68,6 +68,7 @@ flutter: # Seed strings for slang live under lib/i18n (not a Flutter asset; compiled). assets: - assets/catalog/species.json + - assets/logo.png fonts: # Custom seed-themed icon glyphs (chars a–k). See docs/mockups/seedks-glyphs.png. - family: seedks diff --git a/apps/app_seeds/test/ui/home_screen_test.dart b/apps/app_seeds/test/ui/home_screen_test.dart index f573cc3..5ad9805 100644 --- a/apps/app_seeds/test/ui/home_screen_test.dart +++ b/apps/app_seeds/test/ui/home_screen_test.dart @@ -52,4 +52,22 @@ void main() { expect(find.text('No seeds yet. Tap + to add your first.'), findsOneWidget); await disposeTree(tester); }); + + testWidgets('drawer Settings opens the settings screen', (tester) async { + LocaleSettings.setLocaleSync(AppLocale.en); + final db = newTestDatabase(); + addTearDown(db.close); + + await tester.pumpWidget(app(db)); + await tester.pumpAndSettle(); + + await tester.tap(find.byIcon(Icons.menu)); + await tester.pumpAndSettle(); + await tester.tap(find.text('Settings')); + await tester.pumpAndSettle(); + + expect(find.text('Language'), findsOneWidget); + expect(find.text('Español'), findsOneWidget); + await disposeTree(tester); + }); }