Phase 1 du MVP : schéma SQLite porté depuis le prototype web, seed des règles/récompenses, flux d'onboarding en 4 étapes et écran d'accueil minimal. Co-authored-by: Cursor <cursoragent@cursor.com>
118 lines
3.9 KiB
Dart
118 lines
3.9 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../../../data/seed/default_rewards.dart';
|
|
import '../../../data/seed/default_rules.dart';
|
|
import '../onboarding_state.dart';
|
|
import '../widgets/onboarding_scaffold.dart';
|
|
|
|
class RulesStep extends ConsumerWidget {
|
|
const RulesStep({
|
|
super.key,
|
|
required this.step,
|
|
required this.isSaving,
|
|
required this.onBack,
|
|
required this.onFinish,
|
|
});
|
|
|
|
final int step;
|
|
final bool isSaving;
|
|
final VoidCallback onBack;
|
|
final VoidCallback onFinish;
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final draft = ref.watch(onboardingDraftProvider);
|
|
final notifier = ref.read(onboardingDraftProvider.notifier);
|
|
|
|
return OnboardingScaffold(
|
|
step: step,
|
|
title: 'Règles par défaut',
|
|
subtitle: 'Pack « Maison & école » prêt à l\'emploi',
|
|
child: Card(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(24),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
SwitchListTile(
|
|
contentPadding: EdgeInsets.zero,
|
|
title: const Text('Importer le pack par défaut'),
|
|
subtitle: Text(
|
|
'${defaultRules.length} règles et '
|
|
'${defaultRewards.length} récompenses',
|
|
),
|
|
value: draft.importDefaultRules,
|
|
onChanged: isSaving ? null : notifier.setImportDefaultRules,
|
|
),
|
|
const Divider(),
|
|
Expanded(
|
|
child: draft.importDefaultRules
|
|
? ListView(
|
|
children: [
|
|
Text(
|
|
'Familles de règles incluses :',
|
|
style: Theme.of(context).textTheme.titleSmall,
|
|
),
|
|
const SizedBox(height: 8),
|
|
..._ruleFamilies.map(
|
|
(f) => Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
|
child: Text('• $f'),
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
'Vous pourrez modifier les règles plus tard '
|
|
'depuis l\'espace parent (Premium pour '
|
|
'l\'édition complète).',
|
|
style: Theme.of(context).textTheme.bodySmall
|
|
?.copyWith(color: Colors.grey.shade600),
|
|
),
|
|
],
|
|
)
|
|
: Center(
|
|
child: Text(
|
|
'Vous partirez sans règles ni récompenses. '
|
|
'Vous pourrez les ajouter manuellement plus tard.',
|
|
textAlign: TextAlign.center,
|
|
style: Theme.of(context).textTheme.bodyMedium,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
bottom: Row(
|
|
children: [
|
|
TextButton(
|
|
onPressed: isSaving ? null : onBack,
|
|
child: const Text('Retour'),
|
|
),
|
|
const Spacer(),
|
|
FilledButton(
|
|
onPressed: isSaving ? null : onFinish,
|
|
child: isSaving
|
|
? const SizedBox(
|
|
width: 22,
|
|
height: 22,
|
|
child: CircularProgressIndicator(strokeWidth: 2),
|
|
)
|
|
: const Text('C\'est parti !'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
const _ruleFamilies = [
|
|
RuleFamilies.maison,
|
|
RuleFamilies.routine,
|
|
RuleFamilies.fratrie,
|
|
RuleFamilies.respect,
|
|
RuleFamilies.ecrans,
|
|
RuleFamilies.bonus,
|
|
];
|