feat: Adapter RegisterChoiceScreen pour mobile (Closes #83)

- Implémentation responsive avec LayoutBuilder pour détecter mobile/desktop
- Mode mobile : titre au-dessus, carte pleine largeur avec ratio 2/3, boutons verticaux
- Mode desktop : chevron en haut à gauche, layout texte/carte côte à côte
- Extraction de la logique de carte dans ChoiceCardWidget réutilisable
- Bouton "Précédent" stylisé avec CustomNavigationButton et HoverReliefWidget
- Tailles d'icônes augmentées (140px mobile, 170px desktop)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
MARTIN Julien 2026-02-08 12:34:50 +01:00
parent 9b007fe490
commit fd97e68dd9
4 changed files with 246 additions and 156 deletions

View File

@ -1,2 +1,2 @@
flutter.sdk=/home/deploy/snap/flutter/common/flutter
flutter.sdk=C:\\Users\\marti\\dev\\flutter
sdk.dir=C:\\Users\\myhan\\AppData\\Local\\Android\\Sdk

View File

@ -1,8 +1,10 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'dart:math' as math; // Pour la rotation du chevron
import '../../widgets/hover_relief_widget.dart'; // Import du widget générique
import '../../models/card_assets.dart'; // Import des enums de cartes
import 'dart:math' as math;
import '../../widgets/choice_card_widget.dart';
import '../../widgets/hover_relief_widget.dart';
import '../../widgets/custom_navigation_button.dart';
import '../../models/card_assets.dart';
import 'package:go_router/go_router.dart';
class RegisterChoiceScreen extends StatelessWidget {
@ -10,10 +12,15 @@ class RegisterChoiceScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final screenSize = MediaQuery.of(context).size;
return Scaffold(
body: Stack(
body: LayoutBuilder(
builder: (context, constraints) {
final width = constraints.maxWidth;
final height = constraints.maxHeight;
final screenSize = Size(width, height);
final isMobile = width < 900;
return Stack(
children: [
// Fond papier
Positioned.fill(
@ -24,12 +31,16 @@ class RegisterChoiceScreen extends StatelessWidget {
),
),
// Bouton Retour (chevron gauche)
// Bouton Retour (chevron gauche) - Desktop uniquement
if (!isMobile)
Positioned(
top: 40,
left: 40,
child: IconButton(
icon: Transform.flip(flipX: true, child: Image.asset('assets/images/chevron_right.png', height: 40)),
icon: Transform.flip(
flipX: true,
child: Image.asset('assets/images/chevron_right.png', height: 40),
),
onPressed: () {
if (context.canPop()) {
context.pop();
@ -41,8 +52,19 @@ class RegisterChoiceScreen extends StatelessWidget {
),
),
// Contenu principal en Row (Gauche / Droite)
Padding(
// Contenu principal
isMobile
? _buildMobileLayout(context, screenSize)
: _buildDesktopLayout(context, screenSize),
],
);
},
),
);
}
Widget _buildDesktopLayout(BuildContext context, Size screenSize) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: screenSize.width * 0.05),
child: Row(
children: [
@ -71,94 +93,75 @@ class RegisterChoiceScreen extends StatelessWidget {
child: Center(
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: screenSize.height * 0.78, // Augmenté pour éviter l'overflow
maxHeight: screenSize.height * 0.78,
),
child: AspectRatio(
aspectRatio: 2 / 3,
child: Container(
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(CardColorVertical.pink.path),
fit: BoxFit.fill,
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
// Bouton "Parents" avec HoverReliefWidget appliqué uniquement à l'image
_buildChoiceButton(
context: context,
iconPath: 'assets/images/icon_parents.png',
label: 'Parents',
onPressed: () {
context.go('/parent-register-step1');
},
),
// Bouton "Assistante Maternelle" avec HoverReliefWidget appliqué uniquement à l'image
_buildChoiceButton(
context: context,
iconPath: 'assets/images/icon_assmat.png',
label: 'Assistante Maternelle',
onPressed: () {
context.go('/am-register-step1');
},
),
],
child: ChoiceCardWidget(
isMobile: false,
onParentSelected: () => context.go('/parent-register-step1'),
onAmSelected: () => context.go('/am-register-step1'),
),
),
),
),
),
),
],
),
),
],
),
);
}
}
// Nouvelle méthode helper pour construire les boutons de choix
Widget _buildChoiceButton({
required BuildContext context,
required String iconPath,
required String label,
required VoidCallback onPressed,
}) {
// TODO: Déterminer la couleur de base de card_rose.png et ajuster ces couleurs d'ombre
final Color baseRoseColor = Colors.pink.shade300; // Placeholder
final Color initialShadow = baseRoseColor.withAlpha(90); // Rose plus foncé et transparent pour l'ombre initiale
final Color hoverShadow = baseRoseColor.withAlpha(130); // Rose encore plus foncé pour l'ombre au survol
return Column(
mainAxisSize: MainAxisSize.min,
Widget _buildMobileLayout(BuildContext context, Size screenSize) {
return Center(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
HoverReliefWidget(
onPressed: onPressed,
borderRadius: BorderRadius.circular(15.0),
initialShadowColor: initialShadow, // Ombre rose initiale
hoverShadowColor: hoverShadow, // Ombre rose au survol
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Image.asset(iconPath, height: 140),
),
),
const SizedBox(height: 15),
Text(
label,
'Veuillez choisir votre\ntype de compte :',
style: GoogleFonts.merienda(
fontSize: 26,
fontWeight: FontWeight.w600,
color: Colors.black.withOpacity(0.85),
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.black87,
height: 1.3,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 30),
// Carte rose verticale
SizedBox(
width: double.infinity,
child: AspectRatio(
aspectRatio: 2 / 3,
child: ChoiceCardWidget(
isMobile: true,
onParentSelected: () => context.go('/parent-register-step1'),
onAmSelected: () => context.go('/am-register-step1'),
),
),
),
const SizedBox(height: 30),
// Bouton Précédent
HoverReliefWidget(
child: CustomNavigationButton(
text: 'Précédent',
style: NavigationButtonStyle.purple,
width: double.infinity,
height: 50,
fontSize: 16,
onPressed: () {
if (context.canPop()) {
context.pop();
} else {
context.go('/login');
}
},
),
),
],
),
),
);
}
// --- La classe HoverChoiceButton peut maintenant être supprimée si elle n'est plus utilisée ailleurs ---
// class HoverChoiceButton extends StatefulWidget { ... }
// class _HoverChoiceButtonState extends State<HoverChoiceButton> { ... }
}

View File

@ -0,0 +1,87 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import '../models/card_assets.dart';
import 'hover_relief_widget.dart';
/// Widget réutilisable pour la carte de choix Parent/AM
class ChoiceCardWidget extends StatelessWidget {
final VoidCallback onParentSelected;
final VoidCallback onAmSelected;
final bool isMobile;
const ChoiceCardWidget({
super.key,
required this.onParentSelected,
required this.onAmSelected,
this.isMobile = false,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(CardColorVertical.pink.path),
fit: BoxFit.fill,
),
borderRadius: BorderRadius.circular(15),
),
child: Column(
mainAxisAlignment: isMobile ? MainAxisAlignment.center : MainAxisAlignment.spaceEvenly,
children: [
_buildChoiceButton(
iconPath: 'assets/images/icon_parents.png',
label: 'Parents',
onPressed: onParentSelected,
isMobile: isMobile,
),
SizedBox(height: isMobile ? 30 : 0),
_buildChoiceButton(
iconPath: 'assets/images/icon_assmat.png',
label: 'Assistante Maternelle',
onPressed: onAmSelected,
isMobile: isMobile,
),
],
),
);
}
Widget _buildChoiceButton({
required String iconPath,
required String label,
required VoidCallback onPressed,
required bool isMobile,
}) {
final Color baseRoseColor = Colors.pink.shade300;
final Color initialShadow = baseRoseColor.withAlpha(90);
final Color hoverShadow = baseRoseColor.withAlpha(130);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
HoverReliefWidget(
onPressed: onPressed,
borderRadius: BorderRadius.circular(15.0),
initialShadowColor: initialShadow,
hoverShadowColor: hoverShadow,
child: Padding(
padding: EdgeInsets.all(isMobile ? 6.0 : 8.0),
child: Image.asset(iconPath, height: isMobile ? 140 : 170),
),
),
SizedBox(height: isMobile ? 10 : 15),
Text(
label,
style: GoogleFonts.merienda(
fontSize: isMobile ? 20 : 26,
fontWeight: FontWeight.w600,
color: Colors.black.withOpacity(0.85),
),
textAlign: TextAlign.center,
),
],
);
}
}

View File

@ -61,10 +61,10 @@ packages:
dependency: transitive
description:
name: fake_async
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
url: "https://pub.dev"
source: hosted
version: "1.3.3"
version: "1.3.2"
ffi:
dependency: transitive
description:
@ -249,10 +249,10 @@ packages:
dependency: transitive
description:
name: intl
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev"
source: hosted
version: "0.20.2"
version: "0.19.0"
js:
dependency: "direct main"
description:
@ -265,26 +265,26 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
url: "https://pub.dev"
source: hosted
version: "11.0.2"
version: "10.0.8"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
url: "https://pub.dev"
source: hosted
version: "3.0.10"
version: "3.0.9"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
version: "3.0.1"
lints:
dependency: transitive
description:
@ -321,10 +321,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
url: "https://pub.dev"
source: hosted
version: "1.17.0"
version: "1.16.0"
mime:
dependency: transitive
description:
@ -526,10 +526,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
url: "https://pub.dev"
source: hosted
version: "0.7.7"
version: "0.7.4"
typed_data:
dependency: transitive
description:
@ -606,10 +606,10 @@ packages:
dependency: transitive
description:
name: vector_math
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "2.1.4"
vm_service:
dependency: transitive
description:
@ -635,5 +635,5 @@ packages:
source: hosted
version: "1.1.0"
sdks:
dart: ">=3.8.0-0 <4.0.0"
dart: ">=3.7.0 <4.0.0"
flutter: ">=3.27.0"