Skip to content

Commit 57afadd

Browse files
Patclaude
andcommitted
v1.0.0-rc1 WIP: i18n FR/EN majeure + dedup helpers + a11y P0 + audits 8-axes
⚠️ RELEASE CANDIDATE — work in progress, à reprendre dans 3h Done : - pubspec v1.0.0+31, flutter_localizations + generate:true, flutter_markdown ajouté - l10n.yaml + app_fr.arb + app_en.arb (~370 clés couvrant 9 écrans + 14 widgets) - assets/legal/PRIVACY.{fr,en}.md + TERMS.{fr,en}.md (créés ex-nihilo) - lib/app.dart : Selector<SettingsService, _AppSettingsTuple> ciblé themeMode/locale (audit perf P1-3) - SettingsService.locale getter/setter + AppConstants.prefKeyLocale - main.dart : init intl fr_FR + en_US - build.gradle.kts : splits ABI + resourceConfigurations FR/EN - 6 helpers extraits : - lib/utils/app_dialogs.dart (showConfirmDialog) — pas encore adopté - lib/utils/snackbar_ext.dart (showFloatingSnack) — pas encore adopté - lib/ui/widgets/passphrase_text_field.dart (PassphraseTextField) ✅ adopté - lib/ui/widgets/blocking_progress_dialog.dart — pas encore adopté - lib/ui/widgets/vault_warning_banner.dart ✅ adopté - Migration des 9 écrans vers AppLocalizations (via 6 agents parallèles + cleanup) - Migration des 14 widgets (10 via agent dédié) - mentions_legales_screen.dart refait : flutter_markdown + assets/legal/.md selon locale + TabBar Privacy/Terms - panic_service : prefs.clear() → whitelist {db_encrypted_v1, secure_window_enabled} conformément au PRIVACY - ai_chat_screen : MediaQuery.sizeOf au lieu de MediaQuery.of (perf P0) + scroll throttle 80ms - ProGuard rules : files_tech_voice + flutter_markdown ajoutés - rag_service : refactor pour accepter RagLocaleStrings (rétro-compat fallback FR par défaut) - ARB : exportNoteFromVault + settingsExportDonePartial + exportSkippedVaultedSuffix ajoutés Audits 8-axes parallèles complets : - Sécurité : 96/100, 0 P0, 3 P1 (ProGuard files_tech_voice ✅, scrub KEK exception, flutter_markdown EOL) - Failles : 3 P1 dont KeystoreException OTA wipe (Samsung One UI peut détruire données silencieusement) - Qualité code : 86/100, P0 = strings FR services (rag, voice, embedder, vault exceptions) - Perf : 88/100, P0 MediaQuery ✅, scroll ✅, MiniLM sync (à isoler) - Duplication : 68/100, helpers extraits sous-utilisés (3 helpers + 2 ProgressDialogs locaux) - Incohérences : docs racine v0.9 → v1.0 à bumper, 7 strings FR remontant en EN - Mécanismes fragiles : prefs.clear whitelist ✅, RAG locale ✅ (refactor), MlMemoryGuard timeouts - Branchements manquants : mentions_legales .md ✅, helpers fantômes, 3 announces dead À FINIR (TODO v1.0.0 final, ~6-8h restantes) : - KeystoreException distinction (P1 critique : pas de wipe sur exception transitoire) - KeyInfo.isInsideSecureHardware validation (P1 PIN security) - Docs racine bump v0.9 → v1.0 (PRIVACY.md / TERMS.md / README.md / SECURITY.md) - ai_chat_screen passe RagLocaleStrings(t.ragSystemPrompt, ...) à _rag.build - ARB ajout : ragSystemPrompt FR + EN (EN actuellement utilise fallback FR) - NotesError enum typé pour ~25 ValidationException FR - Adoption helpers : BlockingProgressDialog ×2 + showFloatingSnack ×17 + showConfirmDialog - A11y announces dead : noteEditorAnnounceSavedSuccess + homeAnnounceVaultLocked - Export ZIP exclude vaulted notes (P1 UX) - MiniLM isolate worker (P2 perf S9/POCO) - Whisper PCM wipe (côté files_tech_voice) - DateFormat locale-aware dans note_card.dart flutter analyze : 0 erreur, 11 infos (announce deprecation acceptables SDK 3.11) NE PAS RELEASE en v1.0.0 final tant que P0/P1 critiques restants ne sont pas traités. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c5da01e commit 57afadd

53 files changed

Lines changed: 8873 additions & 1515 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

android/app/build.gradle.kts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ android {
3535
targetSdk = flutter.targetSdkVersion
3636
versionCode = flutter.versionCode
3737
versionName = flutter.versionName
38+
// FR + EN seulement (économie ressources Material/AndroidX strings).
39+
// Cohérent avec generate:true Flutter qui packe nos ARB.
40+
resourceConfigurations += listOf("fr", "en")
3841
}
3942

4043
signingConfigs {
@@ -48,6 +51,31 @@ android {
4851
}
4952
}
5053

54+
// Splits ABI : un APK par architecture (arm64-v8a / armeabi-v7a / x86_64),
55+
// au lieu d'un universel embarquant les 3. Notes Tech embarque sqlcipher,
56+
// ONNX Runtime, Whisper natif, MediaPipe — réduit drastiquement la taille
57+
// d'APK livré par device (~30-50 Mo gagnés sur S9 / POCO C75).
58+
splits {
59+
abi {
60+
isEnable = true
61+
reset()
62+
include("arm64-v8a", "armeabi-v7a", "x86_64")
63+
isUniversalApk = true
64+
}
65+
}
66+
67+
bundle {
68+
abi {
69+
enableSplit = true
70+
}
71+
language {
72+
// Ne PAS splitter par langue : avec generate:true Flutter, les
73+
// ARB sont packagés et l'utilisateur peut switcher la langue
74+
// dans Settings indépendamment de la locale système.
75+
enableSplit = false
76+
}
77+
}
78+
5179
buildTypes {
5280
release {
5381
isMinifyEnabled = true

android/app/proguard-rules.pro

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@
4040
-keep class ai.onnxruntime.** { *; }
4141
-dontwarn ai.onnxruntime.**
4242

43+
# files_tech_voice (Whisper natif via JNI) — appels dynamiques, ne pas
44+
# stripper ni obfusquer.
45+
-keep class com.filestech.files_tech_voice.** { *; }
46+
-dontwarn com.filestech.files_tech_voice.**
47+
48+
# flutter_markdown (rendu PRIVACY/TERMS .md sur mentions_legales)
49+
-keep class io.flutter.plugins.flutter_markdown.** { *; }
50+
51+
# package:cryptography est Dart pur — pas de règle ProGuard nécessaire.
52+
4353
# Apache Tika / XML — référence présente via dépendance transitive
4454
# (ne pas tirer XMLStreamException qui n'est pas utilisé en runtime).
4555
-dontwarn javax.xml.stream.**

assets/legal/PRIVACY.en.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Privacy policy — Notes Tech
2+
3+
**Version 1.0.0 — May 2026**
4+
5+
## In one sentence
6+
7+
Notes Tech does not collect, transmit or store any data on remote servers. Everything stays on your phone, and the database is encrypted at-rest.
8+
9+
## Detail
10+
11+
### Data processed
12+
13+
- **Your Markdown notes**: generated and kept exclusively on your phone, in a SQLite database encrypted by **SQLCipher** with a unique key generated locally (32-byte KEK) stored in the **Android Keystore** via `flutter_secure_storage`.
14+
- **Per-folder vaults**: each vault you enable uses a distinct **passphrase** or **PIN**, derived through **Argon2id RFC 9106** (m=64MB, t=3 for passphrase; lighter for PIN, compensated by device-bound Keystore sealing). Locked note content is encrypted with **AES-256-GCM**, AAD bound to `note_id`.
15+
- **Backlinks `[[Title]]`**: local inverted index, never transmitted.
16+
- **Semantic search embeddings**: vectors computed locally (light encoder or optional quantized MiniLM-L6-v2), stored as BLOB in the same encrypted database.
17+
- **AI models (Gemma `.task`, Whisper `.bin`)**: downloaded by your **system browser** from Kaggle / HuggingFace (Notes Tech merely fires an `ACTION_VIEW` intent), then imported manually. Notes Tech has no Internet permission and downloads nothing itself.
18+
- **Audio captured during dictation**: transcribed and **immediately wiped**. Never persisted.
19+
- **Settings (theme, sort, dictation enabled, vault auto-lock)**: stored in clear in local preferences (no sensitive data).
20+
21+
### Data NOT processed
22+
23+
- **No telemetry**, no analytics, no third-party crash reporter.
24+
- **No advertising**, no tracker.
25+
- **No user account**, no online service connection.
26+
27+
### Android permissions requested
28+
29+
Notes Tech requests **NO `INTERNET` permission**. The app is technically unable to communicate with a remote server. This absence can be verified in the source repo `AndroidManifest.xml` (`tools:node="remove"` on INTERNET and ACCESS_NETWORK_STATE).
30+
31+
Active permissions are strictly utilitarian:
32+
- `READ_EXTERNAL_STORAGE` / Storage Access Framework (select `.task` and `.bin` model files).
33+
- `RECORD_AUDIO` (Whisper dictation, audio never persisted).
34+
35+
### Panic mode
36+
37+
The **Settings → Panic mode** menu wipes in bulk and atomically:
38+
- the encrypted SQLite database (all notes),
39+
- the SQLCipher KEK (unrecoverable),
40+
- the Keystore keys associated with PIN vaults,
41+
- the per-folder vaults (passphrases and PINs),
42+
- the Gemma and Whisper models installed in the sandbox,
43+
- the preferences (except `db_encrypted_v1` and `secure_window_enabled` kept for restart consistency).
44+
45+
The wipe is atomic and resumable: if a crash occurs mid-wipe, the next start completes the remaining steps (`vault_wipe_pending_*`).
46+
47+
### Your rights
48+
49+
All data being strictly local, the GDPR applies between you and your phone. You may at any time:
50+
- export your notes in Markdown or ZIP (`Settings → Export`),
51+
- delete all data via panic mode,
52+
- uninstall the app — Android will automatically delete all private data.
53+
54+
### Subprocessors
55+
56+
**None.** Notes Tech uses no third-party service at runtime.
57+
58+
### AI models
59+
60+
- **Gemma 3 1B int4** (~530 MB): Google's Gemma license. See https://ai.google.dev/gemma/terms
61+
- **Whisper** (`.bin` models from `ggerganov/whisper.cpp`): MIT license.
62+
63+
The files you load stay on your phone. Notes Tech merely runs them locally (MediaPipe LLM Inference for Gemma, whisper.cpp via `files_tech_voice` for dictation).
64+
65+
### Contact
66+
67+
For any question: **contact@files-tech.com**
68+
69+
---
70+
71+
Notes Tech is published by a **French sole proprietorship** (SIRET available on request). Source code published under **Apache 2.0** license.

assets/legal/PRIVACY.fr.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Politique de confidentialité — Notes Tech
2+
3+
**Version 1.0.0 — Mai 2026**
4+
5+
## En une phrase
6+
7+
Notes Tech ne collecte, ne transmet et ne stocke aucune donnée sur des serveurs distants. Tout reste sur votre téléphone, et la base de données est chiffrée at-rest.
8+
9+
## Détail
10+
11+
### Données traitées
12+
13+
- **Vos notes Markdown** : générées et conservées exclusivement sur votre téléphone, dans une base SQLite chiffrée par **SQLCipher** avec une clé unique générée localement (KEK 32 octets) stockée dans le **Android Keystore** via `flutter_secure_storage`.
14+
- **Coffres par dossier** : chaque coffre que vous activez utilise une **passphrase** ou un **PIN** distinct, dérivé via **Argon2id RFC 9106** (m=64MB, t=3 pour passphrase ; allégé pour PIN, compensé par le scellage Keystore device-bound). Le contenu des notes verrouillées est chiffré **AES-256-GCM** avec AAD lié à `note_id`.
15+
- **Backlinks `[[Titre]]`** : index inversé local, jamais transmis.
16+
- **Embeddings de recherche sémantique** : vecteurs calculés localement (encodeur léger ou MiniLM-L6-v2 quantifié optionnel), stockés en BLOB dans la même base chiffrée.
17+
- **Modèles IA (Gemma `.task`, Whisper `.bin`)** : téléchargés par votre **navigateur système** depuis Kaggle / HuggingFace (Notes Tech ouvre simplement un intent `ACTION_VIEW`), puis importés manuellement. Notes Tech n'a pas la permission Internet et ne télécharge rien lui-même.
18+
- **Audio capturé pendant la dictée** : transcrit puis **immédiatement effacé**. Jamais persisté.
19+
- **Préférences (thème, tri, dictée activée, auto-lock coffre)** : stockées en clair dans les préférences locales (pas de donnée sensible).
20+
21+
### Données NON traitées
22+
23+
- **Aucune télémétrie**, aucune analytics, aucun crash reporter tiers.
24+
- **Aucune publicité**, aucun tracker.
25+
- **Aucun compte utilisateur**, aucune connexion à un service en ligne.
26+
27+
### Permissions Android demandées
28+
29+
Notes Tech ne demande **AUCUNE permission `INTERNET`**. L'application est techniquement incapable de communiquer avec un serveur distant. Cette absence est vérifiable dans l'`AndroidManifest.xml` du dépôt source (`tools:node="remove"` sur INTERNET et ACCESS_NETWORK_STATE).
30+
31+
Les permissions actives sont strictement utilitaires :
32+
- `READ_EXTERNAL_STORAGE` / Storage Access Framework (sélectionner les modèles `.task` et `.bin`).
33+
- `RECORD_AUDIO` (dictée Whisper, audio jamais persisté).
34+
35+
### Mode panique
36+
37+
Le menu **Réglages → Mode panique** efface en bloc et de manière atomique :
38+
- la base SQLite chiffrée (toutes les notes),
39+
- la KEK SQLCipher (irrécupérable),
40+
- les clés Keystore associées aux coffres PIN,
41+
- les coffres par dossier (passphrases et PIN),
42+
- les modèles Gemma et Whisper installés dans le sandbox,
43+
- les préférences (sauf `db_encrypted_v1` et `secure_window_enabled` conservées pour cohérence du redémarrage).
44+
45+
Le wipe est atomique et reprenable : si un crash survient au milieu, le redémarrage suivant termine les étapes restantes (`vault_wipe_pending_*`).
46+
47+
### Vos droits
48+
49+
Toutes les données étant strictement locales, le règlement RGPD s'applique entre vous et votre téléphone. Vous pouvez à tout moment :
50+
- exporter vos notes au format Markdown ou ZIP (`Réglages → Exporter`),
51+
- supprimer toutes les données via le mode panique,
52+
- désinstaller l'application — Android supprimera automatiquement toutes les données privées.
53+
54+
### Sous-traitants
55+
56+
**Aucun.** Notes Tech n'utilise aucun service tiers à l'exécution.
57+
58+
### Modèles d'IA
59+
60+
- **Gemma 3 1B int4** (~530 Mo) : licence Gemma de Google. Voir https://ai.google.dev/gemma/terms
61+
- **Whisper** (modèles `.bin` `ggerganov/whisper.cpp`) : licence MIT.
62+
63+
Les fichiers que vous chargez restent sur votre téléphone. Notes Tech ne fait que les exécuter localement (MediaPipe LLM Inference pour Gemma, whisper.cpp via `files_tech_voice` pour la dictée).
64+
65+
### Contact
66+
67+
Pour toute question : **contact@files-tech.com**
68+
69+
---
70+
71+
Notes Tech est édité par une **micro-entreprise française** (SIRET disponible sur demande). Code source publié sous licence **Apache 2.0**.

assets/legal/TERMS.en.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Terms of use — Notes Tech
2+
3+
**Version 1.0.0 — May 2026**
4+
5+
## License
6+
7+
Notes Tech is free software published under the **Apache 2.0 license**. You may use, modify and redistribute it under the terms of that license. The full text is available in the `LICENSE` file of the source repository (https://github.com/gitubpatrice/notes_tech).
8+
9+
## Usage
10+
11+
The app is provided **as is, without warranty of any kind**. The AI features (questions/answers on your notes via Gemma, Whisper dictation) are powered by AI that may produce errors, inaccurate information, or imperfect transcriptions. You alone remain responsible for the content and the use you make of the generated suggestions.
12+
13+
## Limitations
14+
15+
- Notes Tech is **in no case** a substitute for medical, legal, financial or professional advice.
16+
- The Gemma model may **hallucinate** (invent facts with confidence); always verify critical answers.
17+
- Whisper may transcribe incorrectly, especially in noisy environments or with specialized technical terms.
18+
- Performance depends on your hardware and the loaded model.
19+
20+
## Panic mode and data loss
21+
22+
The **panic mode** permanently and irreversibly wipes your notes, encryption key, and models. **No recovery is possible** — it is by design. Before using it, export what you want to keep via `Settings → Export`.
23+
24+
Likewise, **forgetting a vault passphrase makes its notes unreadable forever**: the passphrase is never stored, it only derives the key via Argon2id. No recovery procedure exists.
25+
26+
For **PIN** vaults, **5 successive failures trigger an auto-wipe** (Keystore key deletion). Aligned with the standard Android lock screen behaviour.
27+
28+
## AI models
29+
30+
The app is compatible with:
31+
- **Gemma 3 1B int4** in MediaPipe `.task` format — Google's Gemma license: https://ai.google.dev/gemma/terms
32+
- **Whisper** GGML `.bin` models — MIT license, source `ggerganov/whisper.cpp`
33+
34+
You are responsible for complying with those licenses.
35+
36+
## Data
37+
38+
All your notes are stored **locally and encrypted** on your phone (see the **Privacy policy**). Notes Tech sends nothing over the Internet and has no technical permission to do so (no Android `INTERNET` permission).
39+
40+
## Updates
41+
42+
Updates are distributed via the official GitHub repository. No auto-update: it is up to you to install the new version.
43+
44+
## Liability
45+
46+
The publisher cannot be held liable for any direct or indirect damage resulting from the use of the app, within the limits authorized by French law. In particular, **any data loss consequent to a panic mode, a forgotten passphrase, a PIN auto-wipe or an uninstall is the sole responsibility of the user**.
47+
48+
## Governing law
49+
50+
Terms governed by **French law**. French courts have jurisdiction in case of dispute.
51+
52+
## Contact
53+
54+
**contact@files-tech.com**
55+
56+
---
57+
58+
Notes Tech is part of the **Files Tech** suite, published by a French sole proprietorship.

assets/legal/TERMS.fr.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Conditions d'utilisation — Notes Tech
2+
3+
**Version 1.0.0 — Mai 2026**
4+
5+
## Licence
6+
7+
Notes Tech est un logiciel libre publié sous **licence Apache 2.0**. Vous pouvez l'utiliser, le modifier et le redistribuer dans les conditions de cette licence. Le texte complet est disponible dans le fichier `LICENSE` du dépôt source (https://github.com/gitubpatrice/notes_tech).
8+
9+
## Usage
10+
11+
L'application est fournie **telle quelle, sans garantie d'aucune sorte**. Les fonctionnalités IA (questions/réponses sur vos notes via Gemma, dictée Whisper) sont assistées par intelligence artificielle qui peut produire des erreurs, des informations inexactes, ou des transcriptions imparfaites. Vous restez seul responsable du contenu et de l'usage que vous faites des suggestions générées.
12+
13+
## Limitations
14+
15+
- Notes Tech ne se substitue **en aucun cas** à un avis médical, juridique, financier ou professionnel.
16+
- Le modèle Gemma peut **halluciner** (inventer des faits avec assurance) ; vérifiez toujours les réponses critiques.
17+
- Whisper peut transcrire incorrectement, surtout en environnement bruyant ou avec des termes techniques spécialisés.
18+
- Les performances dépendent de votre matériel et du modèle chargé.
19+
20+
## Mode panique et perte de données
21+
22+
Le **mode panique** efface définitivement et irréversiblement vos notes, votre clé de chiffrement et vos modèles. **Aucune récupération n'est possible** — c'est par conception. Avant de l'utiliser, exportez ce que vous voulez conserver via `Réglages → Exporter`.
23+
24+
De même, **oublier la passphrase d'un coffre rend ses notes illisibles à jamais** : la passphrase n'est jamais stockée, elle ne sert qu'à dériver la clé via Argon2id. Aucune procédure de récupération n'existe.
25+
26+
Pour les coffres en mode **PIN**, **5 échecs successifs déclenchent un auto-wipe** (suppression de la clé Keystore). Aligné sur le comportement standard d'un écran de verrouillage Android.
27+
28+
## Modèles d'IA
29+
30+
L'application est compatible avec :
31+
- **Gemma 3 1B int4** au format MediaPipe `.task` — licence Gemma de Google : https://ai.google.dev/gemma/terms
32+
- **Whisper** modèles GGML `.bin` — licence MIT, source `ggerganov/whisper.cpp`
33+
34+
Vous êtes responsable du respect de ces licences.
35+
36+
## Données
37+
38+
Toutes vos notes sont stockées **localement et chiffrées** sur votre téléphone (voir la **Politique de confidentialité**). Notes Tech n'envoie rien sur Internet et n'a pas la permission technique de le faire (pas de permission `INTERNET` Android).
39+
40+
## Mises à jour
41+
42+
Les mises à jour sont distribuées via le dépôt GitHub officiel. Aucune mise à jour automatique : c'est à vous d'installer la nouvelle version.
43+
44+
## Responsabilité
45+
46+
L'éditeur ne pourra être tenu responsable de tout dommage direct ou indirect résultant de l'utilisation de l'application, dans la limite autorisée par la loi française. En particulier, **toute perte de données consécutive à un mode panique, à un oubli de passphrase, à un auto-wipe PIN ou à une désinstallation de l'application est de la seule responsabilité de l'utilisateur**.
47+
48+
## Loi applicable
49+
50+
Conditions soumises au **droit français**. Tribunaux français compétents en cas de litige.
51+
52+
## Contact
53+
54+
**contact@files-tech.com**
55+
56+
---
57+
58+
Notes Tech fait partie de la suite **Files Tech**, éditée par une micro-entreprise française.

l10n.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
arb-dir: lib/l10n
2+
template-arb-file: app_en.arb
3+
output-localization-file: app_localizations.dart
4+
output-class: AppLocalizations
5+
nullable-getter: false

lib/app.dart

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
/// Widget racine — branchement thème + écran d'accueil + lifecycle.
1+
/// Widget racine — branchement thème + locale + écran d'accueil + lifecycle.
22
library;
33

44
import 'package:flutter/material.dart';
55
import 'package:provider/provider.dart';
66

77
import 'core/constants.dart';
88
import 'core/theme.dart';
9+
import 'l10n/app_localizations.dart';
910
import 'services/security/folder_vault_service.dart';
1011
import 'services/settings_service.dart';
1112
import 'ui/screens/home_screen.dart';
@@ -47,14 +48,41 @@ class _NotesTechAppState extends State<NotesTechApp>
4748

4849
@override
4950
Widget build(BuildContext context) {
50-
final settings = context.watch<SettingsService>();
51-
return MaterialApp(
52-
title: AppConstants.appName,
53-
debugShowCheckedModeBanner: false,
54-
themeMode: settings.themeMode,
55-
theme: AppTheme.light,
56-
darkTheme: AppTheme.dark,
57-
home: const HomeScreen(),
51+
// Selector ciblé : ne reconstruit que sur changement themeMode/locale,
52+
// pas sur sortMode/semanticSearchEnabled/secureWindowEnabled/auto-lock
53+
// (audit perf P1-3 : context.watch<SettingsService>() reconstruisait
54+
// tout le MaterialApp à chaque changement, même non-théme/non-locale).
55+
return Selector<SettingsService, _AppSettingsTuple>(
56+
selector: (_, s) => _AppSettingsTuple(s.themeMode, s.locale),
57+
builder: (_, settings, _) {
58+
return MaterialApp(
59+
title: AppConstants.appName,
60+
debugShowCheckedModeBanner: false,
61+
themeMode: settings.themeMode,
62+
theme: AppTheme.light,
63+
darkTheme: AppTheme.dark,
64+
locale: settings.locale,
65+
localizationsDelegates: AppLocalizations.localizationsDelegates,
66+
supportedLocales: AppLocalizations.supportedLocales,
67+
home: const HomeScreen(),
68+
);
69+
},
5870
);
5971
}
6072
}
73+
74+
@immutable
75+
class _AppSettingsTuple {
76+
const _AppSettingsTuple(this.themeMode, this.locale);
77+
final ThemeMode themeMode;
78+
final Locale? locale;
79+
80+
@override
81+
bool operator ==(Object other) =>
82+
other is _AppSettingsTuple &&
83+
other.themeMode == themeMode &&
84+
other.locale?.languageCode == locale?.languageCode;
85+
86+
@override
87+
int get hashCode => Object.hash(themeMode, locale?.languageCode);
88+
}

0 commit comments

Comments
 (0)