diff --git a/mobile/.gitignore b/mobile/.gitignore
new file mode 100644
index 0000000..3820a95
--- /dev/null
+++ b/mobile/.gitignore
@@ -0,0 +1,45 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.build/
+.buildlog/
+.history
+.svn/
+.swiftpm/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins-dependencies
+.pub-cache/
+.pub/
+/build/
+/coverage/
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/mobile/.metadata b/mobile/.metadata
new file mode 100644
index 0000000..fdcdbd8
--- /dev/null
+++ b/mobile/.metadata
@@ -0,0 +1,30 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+ revision: "c9a6c484230f8b5e408ec57be1ef71dee1e77020"
+ channel: "stable"
+
+project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020
+ base_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020
+ - platform: android
+ create_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020
+ base_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020
+
+ # User provided section
+
+ # List of Local paths (relative to this file) that should be
+ # ignored by the migrate tool.
+ #
+ # Files that are not part of the templates will be ignored by default.
+ unmanaged_files:
+ - 'lib/main.dart'
+ - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/mobile/README.md b/mobile/README.md
index 7090e52..5221d2e 100644
--- a/mobile/README.md
+++ b/mobile/README.md
@@ -1,7 +1,54 @@
-# Application mobile Android
+# Bons Points — Application Android (Flutter)
-Emplacement prévu pour l'app **Flutter** (Bons Points — produit commercial Play Store).
+App familiale de bons points, **100 % locale** (SQLite, pas de compte cloud).
-Voir le cahier des charges : [../docs/CAHIER_DES_CHARGES_APP_ANDROID.md](../docs/CAHIER_DES_CHARGES_APP_ANDROID.md).
+## Stack (choix par défaut — voir CDC §14)
-**Statut :** non initialisé — à développer dans un workspace Cursor dédié.
+| Couche | Choix |
+|--------|--------|
+| UI | Flutter + Material 3 |
+| État | **Riverpod** |
+| BDD | **Drift** (SQLite) |
+| PIN | bcrypt |
+
+## Démarrage
+
+```bash
+cd mobile
+flutter pub get
+dart run build_runner build # après modification des tables Drift
+flutter run
+```
+
+## Structure
+
+```
+lib/
+├── main.dart / app.dart
+├── core/theme/ # charte visuelle (prototype web)
+├── data/
+│ ├── database/ # schéma SQLite (port de web/src/db.js)
+│ ├── seed/ # règles & récompenses par défaut
+│ └── repositories/
+├── providers/
+└── features/
+ ├── onboarding/ # bienvenue → enfant → PIN → règles
+ └── dashboard/ # tableau de bord MVP
+```
+
+## État actuel (phase 1)
+
+- [x] Projet Flutter (`fr.ptitspas.bonpoint`, Android seul)
+- [x] Schéma SQLite + seed règles/récompenses
+- [x] Logique score (plancher 0, delta réel)
+- [x] Onboarding sans compte (4 étapes)
+- [x] Tableau de bord minimal
+- [ ] Espace parent (PIN, grille règles)
+- [ ] Fiche enfant + boutique
+- [ ] Export/import, IAP, AdMob
+
+## Tests
+
+```bash
+flutter test
+```
diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml
new file mode 100644
index 0000000..0d29021
--- /dev/null
+++ b/mobile/analysis_options.yaml
@@ -0,0 +1,28 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+ # The lint rules applied to this project can be customized in the
+ # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+ # included above or to enable additional rules. A list of all available lints
+ # and their documentation is published at https://dart.dev/lints.
+ #
+ # Instead of disabling a lint rule for the entire project in the
+ # section below, it can also be suppressed for a single line of code
+ # or a specific dart file by using the `// ignore: name_of_lint` and
+ # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+ # producing the lint.
+ rules:
+ # avoid_print: false # Uncomment to disable the `avoid_print` rule
+ # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/mobile/android/.gitignore b/mobile/android/.gitignore
new file mode 100644
index 0000000..be3943c
--- /dev/null
+++ b/mobile/android/.gitignore
@@ -0,0 +1,14 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+.cxx/
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/to/reference-keystore
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/mobile/android/app/build.gradle.kts b/mobile/android/app/build.gradle.kts
new file mode 100644
index 0000000..1466d79
--- /dev/null
+++ b/mobile/android/app/build.gradle.kts
@@ -0,0 +1,45 @@
+plugins {
+ id("com.android.application")
+ // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
+ id("dev.flutter.flutter-gradle-plugin")
+}
+
+android {
+ namespace = "fr.ptitspas.bonpoint.bonpoint"
+ compileSdk = flutter.compileSdkVersion
+ ndkVersion = flutter.ndkVersion
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId = "fr.ptitspas.bonpoint.bonpoint"
+ // You can update the following values to match your application needs.
+ // For more information, see: https://flutter.dev/to/review-gradle-config.
+ minSdk = flutter.minSdkVersion
+ targetSdk = flutter.targetSdkVersion
+ versionCode = flutter.versionCode
+ versionName = flutter.versionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig = signingConfigs.getByName("debug")
+ }
+ }
+}
+
+kotlin {
+ compilerOptions {
+ jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
+ }
+}
+
+flutter {
+ source = "../.."
+}
diff --git a/mobile/android/app/src/debug/AndroidManifest.xml b/mobile/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000..399f698
--- /dev/null
+++ b/mobile/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..06c1e3a
--- /dev/null
+++ b/mobile/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/android/app/src/main/kotlin/fr/ptitspas/bonpoint/bonpoint/MainActivity.kt b/mobile/android/app/src/main/kotlin/fr/ptitspas/bonpoint/bonpoint/MainActivity.kt
new file mode 100644
index 0000000..82756e3
--- /dev/null
+++ b/mobile/android/app/src/main/kotlin/fr/ptitspas/bonpoint/bonpoint/MainActivity.kt
@@ -0,0 +1,5 @@
+package fr.ptitspas.bonpoint.bonpoint
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity : FlutterActivity()
diff --git a/mobile/android/app/src/main/res/drawable-v21/launch_background.xml b/mobile/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 0000000..f74085f
--- /dev/null
+++ b/mobile/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/mobile/android/app/src/main/res/drawable/launch_background.xml b/mobile/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 0000000..304732f
--- /dev/null
+++ b/mobile/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..db77bb4
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..17987b7
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..09d4391
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d5f1c8d
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4d6372e
Binary files /dev/null and b/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/mobile/android/app/src/main/res/values-night/styles.xml b/mobile/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 0000000..06952be
--- /dev/null
+++ b/mobile/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/mobile/android/app/src/main/res/values/styles.xml b/mobile/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..cb1ef88
--- /dev/null
+++ b/mobile/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/mobile/android/app/src/profile/AndroidManifest.xml b/mobile/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 0000000..399f698
--- /dev/null
+++ b/mobile/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/mobile/android/build.gradle.kts b/mobile/android/build.gradle.kts
new file mode 100644
index 0000000..dbee657
--- /dev/null
+++ b/mobile/android/build.gradle.kts
@@ -0,0 +1,24 @@
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+val newBuildDir: Directory =
+ rootProject.layout.buildDirectory
+ .dir("../../build")
+ .get()
+rootProject.layout.buildDirectory.value(newBuildDir)
+
+subprojects {
+ val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
+ project.layout.buildDirectory.value(newSubprojectBuildDir)
+}
+subprojects {
+ project.evaluationDependsOn(":app")
+}
+
+tasks.register("clean") {
+ delete(rootProject.layout.buildDirectory)
+}
diff --git a/mobile/android/gradle.properties b/mobile/android/gradle.properties
new file mode 100644
index 0000000..e96108c
--- /dev/null
+++ b/mobile/android/gradle.properties
@@ -0,0 +1,6 @@
+org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
+android.useAndroidX=true
+# This newDsl flag was added by the Flutter template
+android.newDsl=false
+# This builtInKotlin flag was added by the Flutter template
+android.builtInKotlin=false
diff --git a/mobile/android/gradle/wrapper/gradle-wrapper.properties b/mobile/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..2d428bf
--- /dev/null
+++ b/mobile/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip
diff --git a/mobile/android/settings.gradle.kts b/mobile/android/settings.gradle.kts
new file mode 100644
index 0000000..c21f0c5
--- /dev/null
+++ b/mobile/android/settings.gradle.kts
@@ -0,0 +1,26 @@
+pluginManagement {
+ val flutterSdkPath =
+ run {
+ val properties = java.util.Properties()
+ file("local.properties").inputStream().use { properties.load(it) }
+ val flutterSdkPath = properties.getProperty("flutter.sdk")
+ require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
+ flutterSdkPath
+ }
+
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
+
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+plugins {
+ id("dev.flutter.flutter-plugin-loader") version "1.0.0"
+ id("com.android.application") version "9.0.1" apply false
+ id("org.jetbrains.kotlin.android") version "2.3.20" apply false
+}
+
+include(":app")
diff --git a/mobile/lib/app.dart b/mobile/lib/app.dart
new file mode 100644
index 0000000..3ca3eb8
--- /dev/null
+++ b/mobile/lib/app.dart
@@ -0,0 +1,71 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+import 'core/theme/app_theme.dart';
+import 'features/dashboard/dashboard_screen.dart';
+import 'features/onboarding/onboarding_flow.dart';
+import 'providers/database_provider.dart';
+
+class BonpointApp extends ConsumerStatefulWidget {
+ const BonpointApp({super.key});
+
+ @override
+ ConsumerState createState() => _BonpointAppState();
+}
+
+class _BonpointAppState extends ConsumerState {
+ @override
+ void initState() {
+ super.initState();
+ Future.microtask(() => ref.read(databaseProvider).initialize());
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final onboardingAsync = ref.watch(onboardingCompleteProvider);
+
+ return MaterialApp(
+ title: 'Bons Points',
+ debugShowCheckedModeBanner: false,
+ theme: AppTheme.light(),
+ home: onboardingAsync.when(
+ loading: () => const _SplashScreen(),
+ error: (_, _) => const OnboardingFlow(),
+ data: (complete) =>
+ complete ? const DashboardScreen() : const OnboardingFlow(),
+ ),
+ );
+ }
+}
+
+class _SplashScreen extends StatelessWidget {
+ const _SplashScreen();
+
+ @override
+ Widget build(BuildContext context) {
+ return DecoratedBox(
+ decoration: const BoxDecoration(gradient: AppTheme.primaryGradient),
+ child: Scaffold(
+ backgroundColor: Colors.transparent,
+ body: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const Text('⭐', style: TextStyle(fontSize: 64)),
+ const SizedBox(height: 16),
+ Text(
+ 'Bons Points',
+ style: Theme.of(context).textTheme.headlineMedium?.copyWith(
+ color: Colors.white,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ const SizedBox(height: 32),
+ const CircularProgressIndicator(color: Colors.white),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/mobile/lib/core/theme/app_theme.dart b/mobile/lib/core/theme/app_theme.dart
new file mode 100644
index 0000000..eaa314d
--- /dev/null
+++ b/mobile/lib/core/theme/app_theme.dart
@@ -0,0 +1,73 @@
+import 'package:flutter/material.dart';
+
+/// Charte inspirée du prototype web (dégradé violet, cartes blanches).
+abstract final class AppTheme {
+ static const primaryGradient = LinearGradient(
+ begin: Alignment.topLeft,
+ end: Alignment.bottomRight,
+ colors: [Color(0xFF667EEA), Color(0xFF764BA2)],
+ );
+
+ static const childColors = [
+ '#6C63FF',
+ '#E91E8C',
+ '#2196F3',
+ '#FF9800',
+ '#4CAF50',
+ '#9C27B0',
+ '#00BCD4',
+ '#F44336',
+ ];
+
+ static Color parseColor(String hex) {
+ final value = hex.replaceFirst('#', '');
+ return Color(int.parse('FF$value', radix: 16));
+ }
+
+ static ThemeData light() {
+ final seed = parseColor('#667EEA');
+ return ThemeData(
+ useMaterial3: true,
+ colorScheme: ColorScheme.fromSeed(
+ seedColor: seed,
+ brightness: Brightness.light,
+ ),
+ scaffoldBackgroundColor: seed,
+ appBarTheme: const AppBarTheme(
+ backgroundColor: Colors.transparent,
+ foregroundColor: Colors.white,
+ elevation: 0,
+ centerTitle: true,
+ ),
+ inputDecorationTheme: InputDecorationTheme(
+ filled: true,
+ fillColor: Colors.white,
+ border: OutlineInputBorder(borderRadius: BorderRadius.circular(16)),
+ enabledBorder: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(16),
+ borderSide: BorderSide(color: Colors.white.withValues(alpha: 0.3)),
+ ),
+ ),
+ cardTheme: CardThemeData(
+ color: Colors.white,
+ elevation: 4,
+ shadowColor: Colors.black26,
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
+ ),
+ filledButtonTheme: FilledButtonThemeData(
+ style: FilledButton.styleFrom(
+ backgroundColor: Colors.white,
+ foregroundColor: seed,
+ padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(24),
+ ),
+ textStyle: const TextStyle(fontWeight: FontWeight.w700, fontSize: 16),
+ ),
+ ),
+ textButtonTheme: TextButtonThemeData(
+ style: TextButton.styleFrom(foregroundColor: Colors.white),
+ ),
+ );
+ }
+}
diff --git a/mobile/lib/data/database/app_database.dart b/mobile/lib/data/database/app_database.dart
new file mode 100644
index 0000000..fbed6a5
--- /dev/null
+++ b/mobile/lib/data/database/app_database.dart
@@ -0,0 +1,178 @@
+import 'dart:io';
+
+import 'package:drift/drift.dart';
+import 'package:drift/native.dart';
+import 'package:path/path.dart' as p;
+import 'package:path_provider/path_provider.dart';
+
+import '../seed/default_rewards.dart';
+import '../seed/default_rules.dart';
+import 'tables.dart';
+
+part 'app_database.g.dart';
+
+@DriftDatabase(tables: [Enfants, Regles, Mouvements, Recompenses, Config])
+class AppDatabase extends _$AppDatabase {
+ AppDatabase() : super(_openConnection());
+
+ AppDatabase.forTesting(super.executor);
+
+ @override
+ int get schemaVersion => 1;
+
+ @override
+ MigrationStrategy get migration => MigrationStrategy(
+ onCreate: (m) async {
+ await m.createAll();
+ },
+ onUpgrade: (m, from, to) async {},
+ );
+
+ /// Met à jour les packs par défaut si l'utilisateur les a déjà importés.
+ Future initialize() async {
+ if (await getConfig('regles_version') != null) {
+ await _syncDefaultRules();
+ }
+ if (await getConfig('recompenses_version') != null) {
+ await _syncDefaultRewards();
+ }
+ }
+
+ Future importDefaultPack() async {
+ await _syncDefaultRules();
+ await _syncDefaultRewards();
+ }
+
+ Future _syncDefaultRules() async {
+ final version = await getConfig('regles_version');
+ if (version == rulesVersion) return;
+
+ await (update(regles)..where((r) => r.actif.equals(true)))
+ .write(const ReglesCompanion(actif: Value(false)));
+
+ for (final rule in defaultRules) {
+ await into(regles).insert(
+ ReglesCompanion.insert(
+ libelle: rule.libelle,
+ points: rule.points,
+ icone: Value(rule.icone),
+ ordre: Value(rule.ordre),
+ famille: Value(rule.famille),
+ familleOrdre: Value(rule.familleOrdre),
+ ),
+ );
+ }
+
+ await setConfig('regles_version', rulesVersion);
+ }
+
+ Future _syncDefaultRewards() async {
+ final version = await getConfig('recompenses_version');
+ if (version == rewardsVersion) return;
+
+ await (update(recompenses)..where((r) => r.actif.equals(true)))
+ .write(const RecompensesCompanion(actif: Value(false)));
+
+ for (final reward in defaultRewards) {
+ await into(recompenses).insert(
+ RecompensesCompanion.insert(
+ libelle: reward.libelle,
+ coutPoints: reward.coutPoints,
+ icone: Value(reward.icone),
+ ordre: Value(reward.ordre),
+ ),
+ );
+ }
+
+ await setConfig('recompenses_version', rewardsVersion);
+ }
+
+ Future getConfig(String key) async {
+ final row = await (select(config)..where((c) => c.cle.equals(key)))
+ .getSingleOrNull();
+ return row?.valeur;
+ }
+
+ Future setConfig(String key, String value) async {
+ await into(config).insertOnConflictUpdate(
+ ConfigCompanion.insert(cle: key, valeur: value),
+ );
+ }
+
+ Future isOnboardingComplete() async {
+ return await getConfig('onboarding_complete') == 'true';
+ }
+
+ Future completeOnboarding() async {
+ await setConfig('onboarding_complete', 'true');
+ }
+
+ Future> listChildren() {
+ return (select(enfants)..orderBy([(e) => OrderingTerm.asc(e.ordre)])).get();
+ }
+
+ Future countChildren() async {
+ final count = await enfants.count().getSingle();
+ return count;
+ }
+
+ Future insertChild({
+ required String prenom,
+ required String couleur,
+ String? dateNaissance,
+ }) {
+ return into(enfants).insert(
+ EnfantsCompanion.insert(
+ prenom: prenom,
+ couleur: Value(couleur),
+ dateNaissance: Value(dateNaissance),
+ ordre: const Value(1),
+ ),
+ );
+ }
+
+ Future setPinHash(String hash) async {
+ await setConfig('pin_hash', hash);
+ }
+
+ Future getPinHash() => getConfig('pin_hash');
+
+ /// Applique une règle : plancher score à 0, delta réel enregistré.
+ Future applyRule(int childId, int ruleId, {String? note}) async {
+ return transaction(() async {
+ final child = await (select(enfants)..where((e) => e.id.equals(childId)))
+ .getSingleOrNull();
+ final rule = await (select(regles)
+ ..where((r) => r.id.equals(ruleId) & r.actif.equals(true)))
+ .getSingleOrNull();
+
+ if (child == null || rule == null) return null;
+
+ final newScore = (child.score + rule.points).clamp(0, 1 << 30);
+ final realDelta = newScore - child.score;
+
+ await into(mouvements).insert(
+ MouvementsCompanion.insert(
+ enfantId: childId,
+ regleId: Value(ruleId),
+ delta: realDelta,
+ note: Value(note),
+ ),
+ );
+
+ await (update(enfants)..where((e) => e.id.equals(childId)))
+ .write(EnfantsCompanion(score: Value(newScore)));
+
+ return (select(enfants)..where((e) => e.id.equals(childId)))
+ .getSingleOrNull();
+ });
+ }
+}
+
+LazyDatabase _openConnection() {
+ return LazyDatabase(() async {
+ final dir = await getApplicationDocumentsDirectory();
+ final file = File(p.join(dir.path, 'bonpoint.db'));
+ return NativeDatabase.createInBackground(file);
+ });
+}
diff --git a/mobile/lib/data/database/app_database.g.dart b/mobile/lib/data/database/app_database.g.dart
new file mode 100644
index 0000000..ba56db4
--- /dev/null
+++ b/mobile/lib/data/database/app_database.g.dart
@@ -0,0 +1,3692 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'app_database.dart';
+
+// ignore_for_file: type=lint
+class $EnfantsTable extends Enfants with TableInfo<$EnfantsTable, Enfant> {
+ @override
+ final GeneratedDatabase attachedDatabase;
+ final String? _alias;
+ $EnfantsTable(this.attachedDatabase, [this._alias]);
+ static const VerificationMeta _idMeta = const VerificationMeta('id');
+ @override
+ late final GeneratedColumn id = GeneratedColumn(
+ 'id',
+ aliasedName,
+ false,
+ hasAutoIncrement: true,
+ type: DriftSqlType.int,
+ requiredDuringInsert: false,
+ defaultConstraints: GeneratedColumn.constraintIsAlways(
+ 'PRIMARY KEY AUTOINCREMENT',
+ ),
+ );
+ static const VerificationMeta _prenomMeta = const VerificationMeta('prenom');
+ @override
+ late final GeneratedColumn prenom = GeneratedColumn(
+ 'prenom',
+ aliasedName,
+ false,
+ type: DriftSqlType.string,
+ requiredDuringInsert: true,
+ );
+ static const VerificationMeta _dateNaissanceMeta = const VerificationMeta(
+ 'dateNaissance',
+ );
+ @override
+ late final GeneratedColumn dateNaissance = GeneratedColumn(
+ 'date_naissance',
+ aliasedName,
+ true,
+ type: DriftSqlType.string,
+ requiredDuringInsert: false,
+ );
+ static const VerificationMeta _scoreMeta = const VerificationMeta('score');
+ @override
+ late final GeneratedColumn score = GeneratedColumn(
+ 'score',
+ aliasedName,
+ false,
+ type: DriftSqlType.int,
+ requiredDuringInsert: false,
+ defaultValue: const Constant(0),
+ );
+ static const VerificationMeta _couleurMeta = const VerificationMeta(
+ 'couleur',
+ );
+ @override
+ late final GeneratedColumn couleur = GeneratedColumn(
+ 'couleur',
+ aliasedName,
+ false,
+ type: DriftSqlType.string,
+ requiredDuringInsert: false,
+ defaultValue: const Constant('#6C63FF'),
+ );
+ static const VerificationMeta _ordreMeta = const VerificationMeta('ordre');
+ @override
+ late final GeneratedColumn ordre = GeneratedColumn(
+ 'ordre',
+ aliasedName,
+ false,
+ type: DriftSqlType.int,
+ requiredDuringInsert: false,
+ defaultValue: const Constant(0),
+ );
+ @override
+ List get $columns => [
+ id,
+ prenom,
+ dateNaissance,
+ score,
+ couleur,
+ ordre,
+ ];
+ @override
+ String get aliasedName => _alias ?? actualTableName;
+ @override
+ String get actualTableName => $name;
+ static const String $name = 'enfants';
+ @override
+ VerificationContext validateIntegrity(
+ Insertable instance, {
+ bool isInserting = false,
+ }) {
+ final context = VerificationContext();
+ final data = instance.toColumns(true);
+ if (data.containsKey('id')) {
+ context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
+ }
+ if (data.containsKey('prenom')) {
+ context.handle(
+ _prenomMeta,
+ prenom.isAcceptableOrUnknown(data['prenom']!, _prenomMeta),
+ );
+ } else if (isInserting) {
+ context.missing(_prenomMeta);
+ }
+ if (data.containsKey('date_naissance')) {
+ context.handle(
+ _dateNaissanceMeta,
+ dateNaissance.isAcceptableOrUnknown(
+ data['date_naissance']!,
+ _dateNaissanceMeta,
+ ),
+ );
+ }
+ if (data.containsKey('score')) {
+ context.handle(
+ _scoreMeta,
+ score.isAcceptableOrUnknown(data['score']!, _scoreMeta),
+ );
+ }
+ if (data.containsKey('couleur')) {
+ context.handle(
+ _couleurMeta,
+ couleur.isAcceptableOrUnknown(data['couleur']!, _couleurMeta),
+ );
+ }
+ if (data.containsKey('ordre')) {
+ context.handle(
+ _ordreMeta,
+ ordre.isAcceptableOrUnknown(data['ordre']!, _ordreMeta),
+ );
+ }
+ return context;
+ }
+
+ @override
+ Set get $primaryKey => {id};
+ @override
+ List> get uniqueKeys => [
+ {prenom},
+ ];
+ @override
+ Enfant map(Map data, {String? tablePrefix}) {
+ final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
+ return Enfant(
+ id: attachedDatabase.typeMapping.read(
+ DriftSqlType.int,
+ data['${effectivePrefix}id'],
+ )!,
+ prenom: attachedDatabase.typeMapping.read(
+ DriftSqlType.string,
+ data['${effectivePrefix}prenom'],
+ )!,
+ dateNaissance: attachedDatabase.typeMapping.read(
+ DriftSqlType.string,
+ data['${effectivePrefix}date_naissance'],
+ ),
+ score: attachedDatabase.typeMapping.read(
+ DriftSqlType.int,
+ data['${effectivePrefix}score'],
+ )!,
+ couleur: attachedDatabase.typeMapping.read(
+ DriftSqlType.string,
+ data['${effectivePrefix}couleur'],
+ )!,
+ ordre: attachedDatabase.typeMapping.read(
+ DriftSqlType.int,
+ data['${effectivePrefix}ordre'],
+ )!,
+ );
+ }
+
+ @override
+ $EnfantsTable createAlias(String alias) {
+ return $EnfantsTable(attachedDatabase, alias);
+ }
+}
+
+class Enfant extends DataClass implements Insertable {
+ final int id;
+ final String prenom;
+ final String? dateNaissance;
+ final int score;
+ final String couleur;
+ final int ordre;
+ const Enfant({
+ required this.id,
+ required this.prenom,
+ this.dateNaissance,
+ required this.score,
+ required this.couleur,
+ required this.ordre,
+ });
+ @override
+ Map toColumns(bool nullToAbsent) {
+ final map = {};
+ map['id'] = Variable(id);
+ map['prenom'] = Variable(prenom);
+ if (!nullToAbsent || dateNaissance != null) {
+ map['date_naissance'] = Variable(dateNaissance);
+ }
+ map['score'] = Variable(score);
+ map['couleur'] = Variable(couleur);
+ map['ordre'] = Variable(ordre);
+ return map;
+ }
+
+ EnfantsCompanion toCompanion(bool nullToAbsent) {
+ return EnfantsCompanion(
+ id: Value(id),
+ prenom: Value(prenom),
+ dateNaissance: dateNaissance == null && nullToAbsent
+ ? const Value.absent()
+ : Value(dateNaissance),
+ score: Value(score),
+ couleur: Value(couleur),
+ ordre: Value(ordre),
+ );
+ }
+
+ factory Enfant.fromJson(
+ Map json, {
+ ValueSerializer? serializer,
+ }) {
+ serializer ??= driftRuntimeOptions.defaultSerializer;
+ return Enfant(
+ id: serializer.fromJson(json['id']),
+ prenom: serializer.fromJson(json['prenom']),
+ dateNaissance: serializer.fromJson(json['dateNaissance']),
+ score: serializer.fromJson(json['score']),
+ couleur: serializer.fromJson(json['couleur']),
+ ordre: serializer.fromJson(json['ordre']),
+ );
+ }
+ @override
+ Map toJson({ValueSerializer? serializer}) {
+ serializer ??= driftRuntimeOptions.defaultSerializer;
+ return {
+ 'id': serializer.toJson(id),
+ 'prenom': serializer.toJson(prenom),
+ 'dateNaissance': serializer.toJson(dateNaissance),
+ 'score': serializer.toJson(score),
+ 'couleur': serializer.toJson(couleur),
+ 'ordre': serializer.toJson(ordre),
+ };
+ }
+
+ Enfant copyWith({
+ int? id,
+ String? prenom,
+ Value dateNaissance = const Value.absent(),
+ int? score,
+ String? couleur,
+ int? ordre,
+ }) => Enfant(
+ id: id ?? this.id,
+ prenom: prenom ?? this.prenom,
+ dateNaissance: dateNaissance.present
+ ? dateNaissance.value
+ : this.dateNaissance,
+ score: score ?? this.score,
+ couleur: couleur ?? this.couleur,
+ ordre: ordre ?? this.ordre,
+ );
+ Enfant copyWithCompanion(EnfantsCompanion data) {
+ return Enfant(
+ id: data.id.present ? data.id.value : this.id,
+ prenom: data.prenom.present ? data.prenom.value : this.prenom,
+ dateNaissance: data.dateNaissance.present
+ ? data.dateNaissance.value
+ : this.dateNaissance,
+ score: data.score.present ? data.score.value : this.score,
+ couleur: data.couleur.present ? data.couleur.value : this.couleur,
+ ordre: data.ordre.present ? data.ordre.value : this.ordre,
+ );
+ }
+
+ @override
+ String toString() {
+ return (StringBuffer('Enfant(')
+ ..write('id: $id, ')
+ ..write('prenom: $prenom, ')
+ ..write('dateNaissance: $dateNaissance, ')
+ ..write('score: $score, ')
+ ..write('couleur: $couleur, ')
+ ..write('ordre: $ordre')
+ ..write(')'))
+ .toString();
+ }
+
+ @override
+ int get hashCode =>
+ Object.hash(id, prenom, dateNaissance, score, couleur, ordre);
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ (other is Enfant &&
+ other.id == this.id &&
+ other.prenom == this.prenom &&
+ other.dateNaissance == this.dateNaissance &&
+ other.score == this.score &&
+ other.couleur == this.couleur &&
+ other.ordre == this.ordre);
+}
+
+class EnfantsCompanion extends UpdateCompanion {
+ final Value id;
+ final Value prenom;
+ final Value dateNaissance;
+ final Value score;
+ final Value couleur;
+ final Value ordre;
+ const EnfantsCompanion({
+ this.id = const Value.absent(),
+ this.prenom = const Value.absent(),
+ this.dateNaissance = const Value.absent(),
+ this.score = const Value.absent(),
+ this.couleur = const Value.absent(),
+ this.ordre = const Value.absent(),
+ });
+ EnfantsCompanion.insert({
+ this.id = const Value.absent(),
+ required String prenom,
+ this.dateNaissance = const Value.absent(),
+ this.score = const Value.absent(),
+ this.couleur = const Value.absent(),
+ this.ordre = const Value.absent(),
+ }) : prenom = Value(prenom);
+ static Insertable custom({
+ Expression? id,
+ Expression? prenom,
+ Expression? dateNaissance,
+ Expression? score,
+ Expression? couleur,
+ Expression? ordre,
+ }) {
+ return RawValuesInsertable({
+ if (id != null) 'id': id,
+ if (prenom != null) 'prenom': prenom,
+ if (dateNaissance != null) 'date_naissance': dateNaissance,
+ if (score != null) 'score': score,
+ if (couleur != null) 'couleur': couleur,
+ if (ordre != null) 'ordre': ordre,
+ });
+ }
+
+ EnfantsCompanion copyWith({
+ Value? id,
+ Value? prenom,
+ Value? dateNaissance,
+ Value? score,
+ Value? couleur,
+ Value? ordre,
+ }) {
+ return EnfantsCompanion(
+ id: id ?? this.id,
+ prenom: prenom ?? this.prenom,
+ dateNaissance: dateNaissance ?? this.dateNaissance,
+ score: score ?? this.score,
+ couleur: couleur ?? this.couleur,
+ ordre: ordre ?? this.ordre,
+ );
+ }
+
+ @override
+ Map toColumns(bool nullToAbsent) {
+ final map = {};
+ if (id.present) {
+ map['id'] = Variable(id.value);
+ }
+ if (prenom.present) {
+ map['prenom'] = Variable(prenom.value);
+ }
+ if (dateNaissance.present) {
+ map['date_naissance'] = Variable(dateNaissance.value);
+ }
+ if (score.present) {
+ map['score'] = Variable(score.value);
+ }
+ if (couleur.present) {
+ map['couleur'] = Variable(couleur.value);
+ }
+ if (ordre.present) {
+ map['ordre'] = Variable(ordre.value);
+ }
+ return map;
+ }
+
+ @override
+ String toString() {
+ return (StringBuffer('EnfantsCompanion(')
+ ..write('id: $id, ')
+ ..write('prenom: $prenom, ')
+ ..write('dateNaissance: $dateNaissance, ')
+ ..write('score: $score, ')
+ ..write('couleur: $couleur, ')
+ ..write('ordre: $ordre')
+ ..write(')'))
+ .toString();
+ }
+}
+
+class $ReglesTable extends Regles with TableInfo<$ReglesTable, Regle> {
+ @override
+ final GeneratedDatabase attachedDatabase;
+ final String? _alias;
+ $ReglesTable(this.attachedDatabase, [this._alias]);
+ static const VerificationMeta _idMeta = const VerificationMeta('id');
+ @override
+ late final GeneratedColumn id = GeneratedColumn(
+ 'id',
+ aliasedName,
+ false,
+ hasAutoIncrement: true,
+ type: DriftSqlType.int,
+ requiredDuringInsert: false,
+ defaultConstraints: GeneratedColumn.constraintIsAlways(
+ 'PRIMARY KEY AUTOINCREMENT',
+ ),
+ );
+ static const VerificationMeta _libelleMeta = const VerificationMeta(
+ 'libelle',
+ );
+ @override
+ late final GeneratedColumn libelle = GeneratedColumn(
+ 'libelle',
+ aliasedName,
+ false,
+ type: DriftSqlType.string,
+ requiredDuringInsert: true,
+ );
+ static const VerificationMeta _pointsMeta = const VerificationMeta('points');
+ @override
+ late final GeneratedColumn points = GeneratedColumn(
+ 'points',
+ aliasedName,
+ false,
+ type: DriftSqlType.int,
+ requiredDuringInsert: true,
+ );
+ static const VerificationMeta _iconeMeta = const VerificationMeta('icone');
+ @override
+ late final GeneratedColumn icone = GeneratedColumn(
+ 'icone',
+ aliasedName,
+ false,
+ type: DriftSqlType.string,
+ requiredDuringInsert: false,
+ defaultValue: const Constant('⭐'),
+ );
+ static const VerificationMeta _actifMeta = const VerificationMeta('actif');
+ @override
+ late final GeneratedColumn actif = GeneratedColumn(
+ 'actif',
+ aliasedName,
+ false,
+ type: DriftSqlType.bool,
+ requiredDuringInsert: false,
+ defaultConstraints: GeneratedColumn.constraintIsAlways(
+ 'CHECK ("actif" IN (0, 1))',
+ ),
+ defaultValue: const Constant(true),
+ );
+ static const VerificationMeta _ordreMeta = const VerificationMeta('ordre');
+ @override
+ late final GeneratedColumn ordre = GeneratedColumn(
+ 'ordre',
+ aliasedName,
+ false,
+ type: DriftSqlType.int,
+ requiredDuringInsert: false,
+ defaultValue: const Constant(0),
+ );
+ static const VerificationMeta _familleMeta = const VerificationMeta(
+ 'famille',
+ );
+ @override
+ late final GeneratedColumn famille = GeneratedColumn(
+ 'famille',
+ aliasedName,
+ false,
+ type: DriftSqlType.string,
+ requiredDuringInsert: false,
+ defaultValue: const Constant('📋 Autre'),
+ );
+ static const VerificationMeta _familleOrdreMeta = const VerificationMeta(
+ 'familleOrdre',
+ );
+ @override
+ late final GeneratedColumn familleOrdre = GeneratedColumn(
+ 'famille_ordre',
+ aliasedName,
+ false,
+ type: DriftSqlType.int,
+ requiredDuringInsert: false,
+ defaultValue: const Constant(99),
+ );
+ @override
+ List get $columns => [
+ id,
+ libelle,
+ points,
+ icone,
+ actif,
+ ordre,
+ famille,
+ familleOrdre,
+ ];
+ @override
+ String get aliasedName => _alias ?? actualTableName;
+ @override
+ String get actualTableName => $name;
+ static const String $name = 'regles';
+ @override
+ VerificationContext validateIntegrity(
+ Insertable instance, {
+ bool isInserting = false,
+ }) {
+ final context = VerificationContext();
+ final data = instance.toColumns(true);
+ if (data.containsKey('id')) {
+ context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
+ }
+ if (data.containsKey('libelle')) {
+ context.handle(
+ _libelleMeta,
+ libelle.isAcceptableOrUnknown(data['libelle']!, _libelleMeta),
+ );
+ } else if (isInserting) {
+ context.missing(_libelleMeta);
+ }
+ if (data.containsKey('points')) {
+ context.handle(
+ _pointsMeta,
+ points.isAcceptableOrUnknown(data['points']!, _pointsMeta),
+ );
+ } else if (isInserting) {
+ context.missing(_pointsMeta);
+ }
+ if (data.containsKey('icone')) {
+ context.handle(
+ _iconeMeta,
+ icone.isAcceptableOrUnknown(data['icone']!, _iconeMeta),
+ );
+ }
+ if (data.containsKey('actif')) {
+ context.handle(
+ _actifMeta,
+ actif.isAcceptableOrUnknown(data['actif']!, _actifMeta),
+ );
+ }
+ if (data.containsKey('ordre')) {
+ context.handle(
+ _ordreMeta,
+ ordre.isAcceptableOrUnknown(data['ordre']!, _ordreMeta),
+ );
+ }
+ if (data.containsKey('famille')) {
+ context.handle(
+ _familleMeta,
+ famille.isAcceptableOrUnknown(data['famille']!, _familleMeta),
+ );
+ }
+ if (data.containsKey('famille_ordre')) {
+ context.handle(
+ _familleOrdreMeta,
+ familleOrdre.isAcceptableOrUnknown(
+ data['famille_ordre']!,
+ _familleOrdreMeta,
+ ),
+ );
+ }
+ return context;
+ }
+
+ @override
+ Set get $primaryKey => {id};
+ @override
+ Regle map(Map data, {String? tablePrefix}) {
+ final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
+ return Regle(
+ id: attachedDatabase.typeMapping.read(
+ DriftSqlType.int,
+ data['${effectivePrefix}id'],
+ )!,
+ libelle: attachedDatabase.typeMapping.read(
+ DriftSqlType.string,
+ data['${effectivePrefix}libelle'],
+ )!,
+ points: attachedDatabase.typeMapping.read(
+ DriftSqlType.int,
+ data['${effectivePrefix}points'],
+ )!,
+ icone: attachedDatabase.typeMapping.read(
+ DriftSqlType.string,
+ data['${effectivePrefix}icone'],
+ )!,
+ actif: attachedDatabase.typeMapping.read(
+ DriftSqlType.bool,
+ data['${effectivePrefix}actif'],
+ )!,
+ ordre: attachedDatabase.typeMapping.read(
+ DriftSqlType.int,
+ data['${effectivePrefix}ordre'],
+ )!,
+ famille: attachedDatabase.typeMapping.read(
+ DriftSqlType.string,
+ data['${effectivePrefix}famille'],
+ )!,
+ familleOrdre: attachedDatabase.typeMapping.read(
+ DriftSqlType.int,
+ data['${effectivePrefix}famille_ordre'],
+ )!,
+ );
+ }
+
+ @override
+ $ReglesTable createAlias(String alias) {
+ return $ReglesTable(attachedDatabase, alias);
+ }
+}
+
+class Regle extends DataClass implements Insertable {
+ final int id;
+ final String libelle;
+ final int points;
+ final String icone;
+ final bool actif;
+ final int ordre;
+ final String famille;
+ final int familleOrdre;
+ const Regle({
+ required this.id,
+ required this.libelle,
+ required this.points,
+ required this.icone,
+ required this.actif,
+ required this.ordre,
+ required this.famille,
+ required this.familleOrdre,
+ });
+ @override
+ Map toColumns(bool nullToAbsent) {
+ final map = {};
+ map['id'] = Variable(id);
+ map['libelle'] = Variable(libelle);
+ map['points'] = Variable(points);
+ map['icone'] = Variable(icone);
+ map['actif'] = Variable(actif);
+ map['ordre'] = Variable(ordre);
+ map['famille'] = Variable(famille);
+ map['famille_ordre'] = Variable(familleOrdre);
+ return map;
+ }
+
+ ReglesCompanion toCompanion(bool nullToAbsent) {
+ return ReglesCompanion(
+ id: Value(id),
+ libelle: Value(libelle),
+ points: Value(points),
+ icone: Value(icone),
+ actif: Value(actif),
+ ordre: Value(ordre),
+ famille: Value(famille),
+ familleOrdre: Value(familleOrdre),
+ );
+ }
+
+ factory Regle.fromJson(
+ Map json, {
+ ValueSerializer? serializer,
+ }) {
+ serializer ??= driftRuntimeOptions.defaultSerializer;
+ return Regle(
+ id: serializer.fromJson(json['id']),
+ libelle: serializer.fromJson(json['libelle']),
+ points: serializer.fromJson(json['points']),
+ icone: serializer.fromJson(json['icone']),
+ actif: serializer.fromJson(json['actif']),
+ ordre: serializer.fromJson(json['ordre']),
+ famille: serializer.fromJson(json['famille']),
+ familleOrdre: serializer.fromJson(json['familleOrdre']),
+ );
+ }
+ @override
+ Map toJson({ValueSerializer? serializer}) {
+ serializer ??= driftRuntimeOptions.defaultSerializer;
+ return {
+ 'id': serializer.toJson(id),
+ 'libelle': serializer.toJson(libelle),
+ 'points': serializer.toJson(points),
+ 'icone': serializer.toJson(icone),
+ 'actif': serializer.toJson(actif),
+ 'ordre': serializer.toJson(ordre),
+ 'famille': serializer.toJson(famille),
+ 'familleOrdre': serializer.toJson(familleOrdre),
+ };
+ }
+
+ Regle copyWith({
+ int? id,
+ String? libelle,
+ int? points,
+ String? icone,
+ bool? actif,
+ int? ordre,
+ String? famille,
+ int? familleOrdre,
+ }) => Regle(
+ id: id ?? this.id,
+ libelle: libelle ?? this.libelle,
+ points: points ?? this.points,
+ icone: icone ?? this.icone,
+ actif: actif ?? this.actif,
+ ordre: ordre ?? this.ordre,
+ famille: famille ?? this.famille,
+ familleOrdre: familleOrdre ?? this.familleOrdre,
+ );
+ Regle copyWithCompanion(ReglesCompanion data) {
+ return Regle(
+ id: data.id.present ? data.id.value : this.id,
+ libelle: data.libelle.present ? data.libelle.value : this.libelle,
+ points: data.points.present ? data.points.value : this.points,
+ icone: data.icone.present ? data.icone.value : this.icone,
+ actif: data.actif.present ? data.actif.value : this.actif,
+ ordre: data.ordre.present ? data.ordre.value : this.ordre,
+ famille: data.famille.present ? data.famille.value : this.famille,
+ familleOrdre: data.familleOrdre.present
+ ? data.familleOrdre.value
+ : this.familleOrdre,
+ );
+ }
+
+ @override
+ String toString() {
+ return (StringBuffer('Regle(')
+ ..write('id: $id, ')
+ ..write('libelle: $libelle, ')
+ ..write('points: $points, ')
+ ..write('icone: $icone, ')
+ ..write('actif: $actif, ')
+ ..write('ordre: $ordre, ')
+ ..write('famille: $famille, ')
+ ..write('familleOrdre: $familleOrdre')
+ ..write(')'))
+ .toString();
+ }
+
+ @override
+ int get hashCode => Object.hash(
+ id,
+ libelle,
+ points,
+ icone,
+ actif,
+ ordre,
+ famille,
+ familleOrdre,
+ );
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ (other is Regle &&
+ other.id == this.id &&
+ other.libelle == this.libelle &&
+ other.points == this.points &&
+ other.icone == this.icone &&
+ other.actif == this.actif &&
+ other.ordre == this.ordre &&
+ other.famille == this.famille &&
+ other.familleOrdre == this.familleOrdre);
+}
+
+class ReglesCompanion extends UpdateCompanion {
+ final Value id;
+ final Value libelle;
+ final Value points;
+ final Value icone;
+ final Value actif;
+ final Value ordre;
+ final Value famille;
+ final Value familleOrdre;
+ const ReglesCompanion({
+ this.id = const Value.absent(),
+ this.libelle = const Value.absent(),
+ this.points = const Value.absent(),
+ this.icone = const Value.absent(),
+ this.actif = const Value.absent(),
+ this.ordre = const Value.absent(),
+ this.famille = const Value.absent(),
+ this.familleOrdre = const Value.absent(),
+ });
+ ReglesCompanion.insert({
+ this.id = const Value.absent(),
+ required String libelle,
+ required int points,
+ this.icone = const Value.absent(),
+ this.actif = const Value.absent(),
+ this.ordre = const Value.absent(),
+ this.famille = const Value.absent(),
+ this.familleOrdre = const Value.absent(),
+ }) : libelle = Value(libelle),
+ points = Value(points);
+ static Insertable custom({
+ Expression? id,
+ Expression? libelle,
+ Expression? points,
+ Expression? icone,
+ Expression? actif,
+ Expression? ordre,
+ Expression? famille,
+ Expression? familleOrdre,
+ }) {
+ return RawValuesInsertable({
+ if (id != null) 'id': id,
+ if (libelle != null) 'libelle': libelle,
+ if (points != null) 'points': points,
+ if (icone != null) 'icone': icone,
+ if (actif != null) 'actif': actif,
+ if (ordre != null) 'ordre': ordre,
+ if (famille != null) 'famille': famille,
+ if (familleOrdre != null) 'famille_ordre': familleOrdre,
+ });
+ }
+
+ ReglesCompanion copyWith({
+ Value? id,
+ Value? libelle,
+ Value? points,
+ Value? icone,
+ Value? actif,
+ Value? ordre,
+ Value? famille,
+ Value? familleOrdre,
+ }) {
+ return ReglesCompanion(
+ id: id ?? this.id,
+ libelle: libelle ?? this.libelle,
+ points: points ?? this.points,
+ icone: icone ?? this.icone,
+ actif: actif ?? this.actif,
+ ordre: ordre ?? this.ordre,
+ famille: famille ?? this.famille,
+ familleOrdre: familleOrdre ?? this.familleOrdre,
+ );
+ }
+
+ @override
+ Map toColumns(bool nullToAbsent) {
+ final map = {};
+ if (id.present) {
+ map['id'] = Variable(id.value);
+ }
+ if (libelle.present) {
+ map['libelle'] = Variable(libelle.value);
+ }
+ if (points.present) {
+ map['points'] = Variable(points.value);
+ }
+ if (icone.present) {
+ map['icone'] = Variable(icone.value);
+ }
+ if (actif.present) {
+ map['actif'] = Variable(actif.value);
+ }
+ if (ordre.present) {
+ map['ordre'] = Variable(ordre.value);
+ }
+ if (famille.present) {
+ map['famille'] = Variable(famille.value);
+ }
+ if (familleOrdre.present) {
+ map['famille_ordre'] = Variable(familleOrdre.value);
+ }
+ return map;
+ }
+
+ @override
+ String toString() {
+ return (StringBuffer('ReglesCompanion(')
+ ..write('id: $id, ')
+ ..write('libelle: $libelle, ')
+ ..write('points: $points, ')
+ ..write('icone: $icone, ')
+ ..write('actif: $actif, ')
+ ..write('ordre: $ordre, ')
+ ..write('famille: $famille, ')
+ ..write('familleOrdre: $familleOrdre')
+ ..write(')'))
+ .toString();
+ }
+}
+
+class $RecompensesTable extends Recompenses
+ with TableInfo<$RecompensesTable, Recompense> {
+ @override
+ final GeneratedDatabase attachedDatabase;
+ final String? _alias;
+ $RecompensesTable(this.attachedDatabase, [this._alias]);
+ static const VerificationMeta _idMeta = const VerificationMeta('id');
+ @override
+ late final GeneratedColumn id = GeneratedColumn(
+ 'id',
+ aliasedName,
+ false,
+ hasAutoIncrement: true,
+ type: DriftSqlType.int,
+ requiredDuringInsert: false,
+ defaultConstraints: GeneratedColumn.constraintIsAlways(
+ 'PRIMARY KEY AUTOINCREMENT',
+ ),
+ );
+ static const VerificationMeta _libelleMeta = const VerificationMeta(
+ 'libelle',
+ );
+ @override
+ late final GeneratedColumn libelle = GeneratedColumn(
+ 'libelle',
+ aliasedName,
+ false,
+ type: DriftSqlType.string,
+ requiredDuringInsert: true,
+ );
+ static const VerificationMeta _coutPointsMeta = const VerificationMeta(
+ 'coutPoints',
+ );
+ @override
+ late final GeneratedColumn coutPoints = GeneratedColumn(
+ 'cout_points',
+ aliasedName,
+ false,
+ type: DriftSqlType.int,
+ requiredDuringInsert: true,
+ );
+ static const VerificationMeta _iconeMeta = const VerificationMeta('icone');
+ @override
+ late final GeneratedColumn icone = GeneratedColumn(
+ 'icone',
+ aliasedName,
+ false,
+ type: DriftSqlType.string,
+ requiredDuringInsert: false,
+ defaultValue: const Constant('🎁'),
+ );
+ static const VerificationMeta _actifMeta = const VerificationMeta('actif');
+ @override
+ late final GeneratedColumn actif = GeneratedColumn(
+ 'actif',
+ aliasedName,
+ false,
+ type: DriftSqlType.bool,
+ requiredDuringInsert: false,
+ defaultConstraints: GeneratedColumn.constraintIsAlways(
+ 'CHECK ("actif" IN (0, 1))',
+ ),
+ defaultValue: const Constant(true),
+ );
+ static const VerificationMeta _ordreMeta = const VerificationMeta('ordre');
+ @override
+ late final GeneratedColumn ordre = GeneratedColumn(
+ 'ordre',
+ aliasedName,
+ false,
+ type: DriftSqlType.int,
+ requiredDuringInsert: false,
+ defaultValue: const Constant(0),
+ );
+ @override
+ List get $columns => [
+ id,
+ libelle,
+ coutPoints,
+ icone,
+ actif,
+ ordre,
+ ];
+ @override
+ String get aliasedName => _alias ?? actualTableName;
+ @override
+ String get actualTableName => $name;
+ static const String $name = 'recompenses';
+ @override
+ VerificationContext validateIntegrity(
+ Insertable instance, {
+ bool isInserting = false,
+ }) {
+ final context = VerificationContext();
+ final data = instance.toColumns(true);
+ if (data.containsKey('id')) {
+ context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
+ }
+ if (data.containsKey('libelle')) {
+ context.handle(
+ _libelleMeta,
+ libelle.isAcceptableOrUnknown(data['libelle']!, _libelleMeta),
+ );
+ } else if (isInserting) {
+ context.missing(_libelleMeta);
+ }
+ if (data.containsKey('cout_points')) {
+ context.handle(
+ _coutPointsMeta,
+ coutPoints.isAcceptableOrUnknown(data['cout_points']!, _coutPointsMeta),
+ );
+ } else if (isInserting) {
+ context.missing(_coutPointsMeta);
+ }
+ if (data.containsKey('icone')) {
+ context.handle(
+ _iconeMeta,
+ icone.isAcceptableOrUnknown(data['icone']!, _iconeMeta),
+ );
+ }
+ if (data.containsKey('actif')) {
+ context.handle(
+ _actifMeta,
+ actif.isAcceptableOrUnknown(data['actif']!, _actifMeta),
+ );
+ }
+ if (data.containsKey('ordre')) {
+ context.handle(
+ _ordreMeta,
+ ordre.isAcceptableOrUnknown(data['ordre']!, _ordreMeta),
+ );
+ }
+ return context;
+ }
+
+ @override
+ Set get $primaryKey => {id};
+ @override
+ Recompense map(Map data, {String? tablePrefix}) {
+ final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
+ return Recompense(
+ id: attachedDatabase.typeMapping.read(
+ DriftSqlType.int,
+ data['${effectivePrefix}id'],
+ )!,
+ libelle: attachedDatabase.typeMapping.read(
+ DriftSqlType.string,
+ data['${effectivePrefix}libelle'],
+ )!,
+ coutPoints: attachedDatabase.typeMapping.read(
+ DriftSqlType.int,
+ data['${effectivePrefix}cout_points'],
+ )!,
+ icone: attachedDatabase.typeMapping.read(
+ DriftSqlType.string,
+ data['${effectivePrefix}icone'],
+ )!,
+ actif: attachedDatabase.typeMapping.read(
+ DriftSqlType.bool,
+ data['${effectivePrefix}actif'],
+ )!,
+ ordre: attachedDatabase.typeMapping.read(
+ DriftSqlType.int,
+ data['${effectivePrefix}ordre'],
+ )!,
+ );
+ }
+
+ @override
+ $RecompensesTable createAlias(String alias) {
+ return $RecompensesTable(attachedDatabase, alias);
+ }
+}
+
+class Recompense extends DataClass implements Insertable {
+ final int id;
+ final String libelle;
+ final int coutPoints;
+ final String icone;
+ final bool actif;
+ final int ordre;
+ const Recompense({
+ required this.id,
+ required this.libelle,
+ required this.coutPoints,
+ required this.icone,
+ required this.actif,
+ required this.ordre,
+ });
+ @override
+ Map toColumns(bool nullToAbsent) {
+ final map = {};
+ map['id'] = Variable(id);
+ map['libelle'] = Variable(libelle);
+ map['cout_points'] = Variable(coutPoints);
+ map['icone'] = Variable(icone);
+ map['actif'] = Variable(actif);
+ map['ordre'] = Variable(ordre);
+ return map;
+ }
+
+ RecompensesCompanion toCompanion(bool nullToAbsent) {
+ return RecompensesCompanion(
+ id: Value(id),
+ libelle: Value(libelle),
+ coutPoints: Value(coutPoints),
+ icone: Value(icone),
+ actif: Value(actif),
+ ordre: Value(ordre),
+ );
+ }
+
+ factory Recompense.fromJson(
+ Map json, {
+ ValueSerializer? serializer,
+ }) {
+ serializer ??= driftRuntimeOptions.defaultSerializer;
+ return Recompense(
+ id: serializer.fromJson(json['id']),
+ libelle: serializer.fromJson(json['libelle']),
+ coutPoints: serializer.fromJson(json['coutPoints']),
+ icone: serializer.fromJson(json['icone']),
+ actif: serializer.fromJson(json['actif']),
+ ordre: serializer.fromJson(json['ordre']),
+ );
+ }
+ @override
+ Map toJson({ValueSerializer? serializer}) {
+ serializer ??= driftRuntimeOptions.defaultSerializer;
+ return {
+ 'id': serializer.toJson(id),
+ 'libelle': serializer.toJson(libelle),
+ 'coutPoints': serializer.toJson(coutPoints),
+ 'icone': serializer.toJson(icone),
+ 'actif': serializer.toJson(actif),
+ 'ordre': serializer.toJson(ordre),
+ };
+ }
+
+ Recompense copyWith({
+ int? id,
+ String? libelle,
+ int? coutPoints,
+ String? icone,
+ bool? actif,
+ int? ordre,
+ }) => Recompense(
+ id: id ?? this.id,
+ libelle: libelle ?? this.libelle,
+ coutPoints: coutPoints ?? this.coutPoints,
+ icone: icone ?? this.icone,
+ actif: actif ?? this.actif,
+ ordre: ordre ?? this.ordre,
+ );
+ Recompense copyWithCompanion(RecompensesCompanion data) {
+ return Recompense(
+ id: data.id.present ? data.id.value : this.id,
+ libelle: data.libelle.present ? data.libelle.value : this.libelle,
+ coutPoints: data.coutPoints.present
+ ? data.coutPoints.value
+ : this.coutPoints,
+ icone: data.icone.present ? data.icone.value : this.icone,
+ actif: data.actif.present ? data.actif.value : this.actif,
+ ordre: data.ordre.present ? data.ordre.value : this.ordre,
+ );
+ }
+
+ @override
+ String toString() {
+ return (StringBuffer('Recompense(')
+ ..write('id: $id, ')
+ ..write('libelle: $libelle, ')
+ ..write('coutPoints: $coutPoints, ')
+ ..write('icone: $icone, ')
+ ..write('actif: $actif, ')
+ ..write('ordre: $ordre')
+ ..write(')'))
+ .toString();
+ }
+
+ @override
+ int get hashCode => Object.hash(id, libelle, coutPoints, icone, actif, ordre);
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ (other is Recompense &&
+ other.id == this.id &&
+ other.libelle == this.libelle &&
+ other.coutPoints == this.coutPoints &&
+ other.icone == this.icone &&
+ other.actif == this.actif &&
+ other.ordre == this.ordre);
+}
+
+class RecompensesCompanion extends UpdateCompanion {
+ final Value id;
+ final Value libelle;
+ final Value coutPoints;
+ final Value icone;
+ final Value actif;
+ final Value ordre;
+ const RecompensesCompanion({
+ this.id = const Value.absent(),
+ this.libelle = const Value.absent(),
+ this.coutPoints = const Value.absent(),
+ this.icone = const Value.absent(),
+ this.actif = const Value.absent(),
+ this.ordre = const Value.absent(),
+ });
+ RecompensesCompanion.insert({
+ this.id = const Value.absent(),
+ required String libelle,
+ required int coutPoints,
+ this.icone = const Value.absent(),
+ this.actif = const Value.absent(),
+ this.ordre = const Value.absent(),
+ }) : libelle = Value(libelle),
+ coutPoints = Value(coutPoints);
+ static Insertable custom({
+ Expression? id,
+ Expression? libelle,
+ Expression? coutPoints,
+ Expression? icone,
+ Expression? actif,
+ Expression? ordre,
+ }) {
+ return RawValuesInsertable({
+ if (id != null) 'id': id,
+ if (libelle != null) 'libelle': libelle,
+ if (coutPoints != null) 'cout_points': coutPoints,
+ if (icone != null) 'icone': icone,
+ if (actif != null) 'actif': actif,
+ if (ordre != null) 'ordre': ordre,
+ });
+ }
+
+ RecompensesCompanion copyWith({
+ Value? id,
+ Value? libelle,
+ Value? coutPoints,
+ Value? icone,
+ Value? actif,
+ Value? ordre,
+ }) {
+ return RecompensesCompanion(
+ id: id ?? this.id,
+ libelle: libelle ?? this.libelle,
+ coutPoints: coutPoints ?? this.coutPoints,
+ icone: icone ?? this.icone,
+ actif: actif ?? this.actif,
+ ordre: ordre ?? this.ordre,
+ );
+ }
+
+ @override
+ Map toColumns(bool nullToAbsent) {
+ final map = {};
+ if (id.present) {
+ map['id'] = Variable(id.value);
+ }
+ if (libelle.present) {
+ map['libelle'] = Variable(libelle.value);
+ }
+ if (coutPoints.present) {
+ map['cout_points'] = Variable(coutPoints.value);
+ }
+ if (icone.present) {
+ map['icone'] = Variable(icone.value);
+ }
+ if (actif.present) {
+ map['actif'] = Variable(actif.value);
+ }
+ if (ordre.present) {
+ map['ordre'] = Variable(ordre.value);
+ }
+ return map;
+ }
+
+ @override
+ String toString() {
+ return (StringBuffer('RecompensesCompanion(')
+ ..write('id: $id, ')
+ ..write('libelle: $libelle, ')
+ ..write('coutPoints: $coutPoints, ')
+ ..write('icone: $icone, ')
+ ..write('actif: $actif, ')
+ ..write('ordre: $ordre')
+ ..write(')'))
+ .toString();
+ }
+}
+
+class $MouvementsTable extends Mouvements
+ with TableInfo<$MouvementsTable, Mouvement> {
+ @override
+ final GeneratedDatabase attachedDatabase;
+ final String? _alias;
+ $MouvementsTable(this.attachedDatabase, [this._alias]);
+ static const VerificationMeta _idMeta = const VerificationMeta('id');
+ @override
+ late final GeneratedColumn id = GeneratedColumn(
+ 'id',
+ aliasedName,
+ false,
+ hasAutoIncrement: true,
+ type: DriftSqlType.int,
+ requiredDuringInsert: false,
+ defaultConstraints: GeneratedColumn.constraintIsAlways(
+ 'PRIMARY KEY AUTOINCREMENT',
+ ),
+ );
+ static const VerificationMeta _enfantIdMeta = const VerificationMeta(
+ 'enfantId',
+ );
+ @override
+ late final GeneratedColumn enfantId = GeneratedColumn(
+ 'enfant_id',
+ aliasedName,
+ false,
+ type: DriftSqlType.int,
+ requiredDuringInsert: true,
+ defaultConstraints: GeneratedColumn.constraintIsAlways(
+ 'REFERENCES enfants (id)',
+ ),
+ );
+ static const VerificationMeta _regleIdMeta = const VerificationMeta(
+ 'regleId',
+ );
+ @override
+ late final GeneratedColumn regleId = GeneratedColumn(
+ 'regle_id',
+ aliasedName,
+ true,
+ type: DriftSqlType.int,
+ requiredDuringInsert: false,
+ defaultConstraints: GeneratedColumn.constraintIsAlways(
+ 'REFERENCES regles (id)',
+ ),
+ );
+ static const VerificationMeta _recompenseIdMeta = const VerificationMeta(
+ 'recompenseId',
+ );
+ @override
+ late final GeneratedColumn recompenseId = GeneratedColumn(
+ 'recompense_id',
+ aliasedName,
+ true,
+ type: DriftSqlType.int,
+ requiredDuringInsert: false,
+ defaultConstraints: GeneratedColumn.constraintIsAlways(
+ 'REFERENCES recompenses (id)',
+ ),
+ );
+ static const VerificationMeta _deltaMeta = const VerificationMeta('delta');
+ @override
+ late final GeneratedColumn delta = GeneratedColumn(
+ 'delta',
+ aliasedName,
+ false,
+ type: DriftSqlType.int,
+ requiredDuringInsert: true,
+ );
+ static const VerificationMeta _noteMeta = const VerificationMeta('note');
+ @override
+ late final GeneratedColumn note = GeneratedColumn(
+ 'note',
+ aliasedName,
+ true,
+ type: DriftSqlType.string,
+ requiredDuringInsert: false,
+ );
+ static const VerificationMeta _creeLeMeta = const VerificationMeta('creeLe');
+ @override
+ late final GeneratedColumn creeLe = GeneratedColumn