import 'dart:async'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:flutter/foundation.dart' show kIsWeb; class LoginPage extends StatelessWidget { const LoginPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.transparent, body: LayoutBuilder( builder: (context, constraints) { // Version desktop (web) if (kIsWeb) { final w = constraints.maxWidth; final h = constraints.maxHeight; return FutureBuilder( future: _getImageDimensions(), builder: (context, snapshot) { if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator()); } final imageDimensions = snapshot.data!; final imageHeight = h; final imageWidth = imageHeight * (imageDimensions.width / imageDimensions.height); final remainingWidth = w - imageWidth; final leftMargin = remainingWidth / 4; return Stack( children: [ // Fond en papier Positioned.fill( child: Image.asset( 'assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeat, ), ), // Image principale Positioned( left: leftMargin, top: 0, height: imageHeight, width: imageWidth, child: Image.asset( 'assets/images/river_logo_desktop.png', fit: BoxFit.contain, ), ), // Formulaire dans le cadran en bas à droite Positioned( right: 0, bottom: 0, width: w * 0.5, // Moitié droite de l'écran height: h * 0.5, // Moitié basse de l'écran child: Padding( padding: EdgeInsets.all(w * 0.02), // 2% de padding child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Labels au-dessus des champs Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Text( 'Email', style: GoogleFonts.merienda( fontSize: 20, color: Colors.black87, fontWeight: FontWeight.w600, ), ), ), Expanded( child: Padding( padding: const EdgeInsets.only(left: 20), child: Text( 'Mot de passe', style: GoogleFonts.merienda( fontSize: 20, color: Colors.black87, fontWeight: FontWeight.w600, ), ), ), ), ], ), const SizedBox(height: 10), // Champs côte à côte Row( children: [ Expanded( child: _ImageTextField( bg: 'assets/images/field_email.png', width: 400, height: 80, hint: 'Email', ), ), const SizedBox(width: 20), Expanded( child: _ImageTextField( bg: 'assets/images/field_password.png', width: 400, height: 80, hint: 'Mot de passe', obscure: true, ), ), ], ), const Spacer(), // Bouton centré Center( child: _ImageButton( bg: 'assets/images/btn_green.png', width: 300, height: 60, text: 'Se connecter', textColor: const Color(0xFF8AD0C8), // Vert harmonieux onPressed: () { // TODO: Implémenter la logique de connexion }, ), ), const SizedBox(height: 40), ], ), ), ), ], ); }, ); } // Version mobile (à implémenter) return const Center( child: Text('Version mobile à implémenter'), ); }, ), ); } Future _getImageDimensions() async { final image = Image.asset('assets/images/river_logo_desktop.png'); final completer = Completer(); image.image.resolve(const ImageConfiguration()).addListener( ImageStreamListener((info, _) { completer.complete(ImageDimensions( width: info.image.width.toDouble(), height: info.image.height.toDouble(), )); }), ); return completer.future; } } class ImageDimensions { final double width; final double height; ImageDimensions({required this.width, required this.height}); } // ─────────────────────────────────────────────────────────────── // Champ texte avec fond image // ─────────────────────────────────────────────────────────────── class _ImageTextField extends StatelessWidget { final String bg; final double width; final double height; final String hint; final bool obscure; const _ImageTextField({ required this.bg, required this.width, required this.height, required this.hint, this.obscure = false, }); @override Widget build(BuildContext context) { return Container( width: width, height: height, decoration: BoxDecoration( image: DecorationImage( image: AssetImage(bg), fit: BoxFit.fill, ), ), child: TextField( obscureText: obscure, textAlign: TextAlign.left, style: GoogleFonts.merienda( fontSize: height * 0.25, // Réduction de la taille de la police color: Colors.black87, ), decoration: InputDecoration( border: InputBorder.none, hintText: hint, hintStyle: GoogleFonts.merienda( fontSize: height * 0.25, // Même taille pour le placeholder color: Colors.black38, ), contentPadding: EdgeInsets.symmetric( horizontal: width * 0.1, vertical: height * 0.3, ), ), ), ); } } // ─────────────────────────────────────────────────────────────── // Bouton avec fond image // ─────────────────────────────────────────────────────────────── class _ImageButton extends StatelessWidget { final String bg; final double width; final double height; final String text; final Color textColor; final VoidCallback onPressed; const _ImageButton({ required this.bg, required this.width, required this.height, required this.text, required this.textColor, required this.onPressed, }); @override Widget build(BuildContext context) { return Container( width: width, height: height, decoration: BoxDecoration( image: DecorationImage( image: AssetImage(bg), fit: BoxFit.fill, ), ), child: TextButton( onPressed: onPressed, child: Text( text, style: GoogleFonts.merienda( fontSize: height * 0.3, color: textColor, fontWeight: FontWeight.w600, ), ), ), ); } }