Julien Martin aac3a729a2 Initialise l'app Flutter mobile : BDD Drift, onboarding sans compte et tableau de bord.
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>
2026-06-12 12:12:01 +02:00

116 lines
3.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../onboarding_state.dart';
import '../widgets/onboarding_scaffold.dart';
class PinStep extends ConsumerStatefulWidget {
const PinStep({
super.key,
required this.step,
required this.onBack,
required this.onNext,
});
final int step;
final VoidCallback onBack;
final VoidCallback onNext;
@override
ConsumerState<PinStep> createState() => _PinStepState();
}
class _PinStepState extends ConsumerState<PinStep> {
final _pinController = TextEditingController();
final _confirmController = TextEditingController();
bool _obscurePin = true;
bool _obscureConfirm = true;
@override
void dispose() {
_pinController.dispose();
_confirmController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final notifier = ref.read(onboardingDraftProvider.notifier);
return OnboardingScaffold(
step: widget.step,
title: 'PIN parent',
subtitle: 'Protège l\'espace parent (4 chiffres minimum)',
child: Card(
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(
controller: _pinController,
obscureText: _obscurePin,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(8),
],
decoration: InputDecoration(
labelText: 'Choisir un PIN',
suffixIcon: IconButton(
icon: Icon(
_obscurePin ? Icons.visibility : Icons.visibility_off,
),
onPressed: () =>
setState(() => _obscurePin = !_obscurePin),
),
),
onChanged: notifier.setPin,
),
const SizedBox(height: 16),
TextField(
controller: _confirmController,
obscureText: _obscureConfirm,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(8),
],
decoration: InputDecoration(
labelText: 'Confirmer le PIN',
suffixIcon: IconButton(
icon: Icon(
_obscureConfirm
? Icons.visibility
: Icons.visibility_off,
),
onPressed: () =>
setState(() => _obscureConfirm = !_obscureConfirm),
),
),
onChanged: notifier.setPinConfirm,
),
const SizedBox(height: 20),
Text(
'Ce PIN sera demandé pour accéder à l\'espace parent '
'(appliquer des règles, modifier les réglages).',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Colors.grey.shade600,
),
),
],
),
),
),
bottom: Row(
children: [
TextButton(onPressed: widget.onBack, child: const Text('Retour')),
const Spacer(),
FilledButton(onPressed: widget.onNext, child: const Text('Suivant')),
],
),
);
}
}