merge develop: Flutter 3.29 web, nginx, index bootstrap (build Docker)
This commit is contained in:
@@ -1,14 +1,24 @@
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
|
||||
class Env {
|
||||
// Base URL de l'API, surchargeable à la compilation via --dart-define=API_BASE_URL
|
||||
static const String apiBaseUrl = String.fromEnvironment(
|
||||
'API_BASE_URL',
|
||||
defaultValue: 'https://app.ptits-pas.fr',
|
||||
);
|
||||
/// Base URL de l’API (sans `/api/v1`).
|
||||
///
|
||||
/// - **Web prod** : par défaut on utilise [Uri.base.origin] (même schéma + hôte que la page),
|
||||
/// ce qui évite les blocages « mixed content » (ex. page en `http://` vs API en `https://`)
|
||||
/// et les builds où `--dart-define=API_BASE_URL` ne serait pas passé.
|
||||
/// - **Web dev** : surcharger avec `--dart-define=API_BASE_URL=http://localhost:3000` (ou l’URL du back).
|
||||
/// - **Mobile** : défaut `https://app.ptits-pas.fr` ou `API_BASE_URL` en `--dart-define`.
|
||||
static String get apiBaseUrl {
|
||||
const fromEnv = String.fromEnvironment('API_BASE_URL');
|
||||
if (fromEnv.isNotEmpty) {
|
||||
return fromEnv;
|
||||
}
|
||||
if (kIsWeb) {
|
||||
return Uri.base.origin;
|
||||
}
|
||||
return 'https://app.ptits-pas.fr';
|
||||
}
|
||||
|
||||
// Construit une URL vers l'API v1 à partir d'un chemin (commençant par '/')
|
||||
static String apiV1(String path) => '$apiBaseUrl/api/v1$path';
|
||||
/// Construit une URL vers l'API v1 à partir d'un chemin (commençant par '/')
|
||||
static String apiV1(String path) => '${apiBaseUrl}/api/v1$path';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -69,7 +69,10 @@ class AuthService {
|
||||
}
|
||||
} catch (e) {
|
||||
if (e is Exception) rethrow;
|
||||
throw Exception('Erreur réseau: impossible de se connecter au serveur');
|
||||
// Erreurs non-Exception (ex. certains cas côté web) : afficher le détail pour le diagnostic
|
||||
throw Exception(
|
||||
'Erreur réseau: impossible de se connecter au serveur (${e.runtimeType}: $e)',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ class _CguPrivacyValidationDialogState extends State<CguPrivacyValidationDialog>
|
||||
child: Material(
|
||||
color: _ModaleCouleur.ivoire,
|
||||
elevation: 6,
|
||||
shadowColor: _ModaleCouleur.encre.withValues(alpha: 0.12),
|
||||
shadowColor: _ModaleCouleur.encre.withOpacity(0.12),
|
||||
child: SizedBox(
|
||||
width: dialogW,
|
||||
height: dialogH,
|
||||
@@ -179,9 +179,7 @@ class _CguPrivacyValidationDialogState extends State<CguPrivacyValidationDialog>
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: _ModaleCouleur.turquoiseFonce.withValues(
|
||||
alpha: 0.35,
|
||||
),
|
||||
color: _ModaleCouleur.turquoiseFonce.withOpacity(0.35),
|
||||
),
|
||||
),
|
||||
child: ClipRRect(
|
||||
@@ -200,7 +198,7 @@ class _CguPrivacyValidationDialogState extends State<CguPrivacyValidationDialog>
|
||||
'Document non disponible.',
|
||||
style: GoogleFonts.merienda(
|
||||
color: _ModaleCouleur.encre
|
||||
.withValues(alpha: 0.75),
|
||||
.withOpacity(0.75),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -221,9 +219,9 @@ class _CguPrivacyValidationDialogState extends State<CguPrivacyValidationDialog>
|
||||
backgroundColor: _ModaleCouleur.turquoiseFonce,
|
||||
foregroundColor: Colors.white,
|
||||
disabledBackgroundColor:
|
||||
_ModaleCouleur.mentheClair.withValues(alpha: 0.7),
|
||||
_ModaleCouleur.mentheClair.withOpacity(0.7),
|
||||
disabledForegroundColor:
|
||||
_ModaleCouleur.encre.withValues(alpha: 0.45),
|
||||
_ModaleCouleur.encre.withOpacity(0.45),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
vertical: 14,
|
||||
@@ -286,7 +284,7 @@ class _CguPrivacyValidationDialogState extends State<CguPrivacyValidationDialog>
|
||||
tooltip: 'Fermer',
|
||||
icon: Icon(
|
||||
Icons.close,
|
||||
color: _ModaleCouleur.encre.withValues(alpha: 0.55),
|
||||
color: _ModaleCouleur.encre.withOpacity(0.55),
|
||||
),
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
),
|
||||
@@ -311,7 +309,7 @@ class _CguPrivacyValidationDialogState extends State<CguPrivacyValidationDialog>
|
||||
? _ModaleCouleur.turquoise
|
||||
: _ModaleCouleur.mentheTresClair,
|
||||
side: BorderSide(
|
||||
color: _ModaleCouleur.turquoiseFonce.withValues(alpha: 0.55),
|
||||
color: _ModaleCouleur.turquoiseFonce.withOpacity(0.55),
|
||||
width: 1.5,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 10),
|
||||
@@ -327,10 +325,8 @@ class _CguPrivacyValidationDialogState extends State<CguPrivacyValidationDialog>
|
||||
}
|
||||
}
|
||||
|
||||
/// [PdfViewPinch] s’appuie sur un [InteractiveViewer], sans vrai [Scrollable] :
|
||||
/// barre verticale reliée à [PdfControllerPinch.documentProgress] pour que le
|
||||
/// « chariot » reste visible (surtout sur le web où la barre OS est souvent
|
||||
/// masquée).
|
||||
/// [PdfViewPinch] pour le web / mobile avec `pdfx` 2.6 (pas de
|
||||
/// `documentProgress` / `minScale` dans cette API).
|
||||
class _PdfPaneWithScrollbar extends StatefulWidget {
|
||||
const _PdfPaneWithScrollbar({
|
||||
super.key,
|
||||
@@ -344,106 +340,27 @@ class _PdfPaneWithScrollbar extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _PdfPaneWithScrollbarState extends State<_PdfPaneWithScrollbar> {
|
||||
static const double _barW = 14;
|
||||
static const double _thumbW = 9;
|
||||
static const double _minThumbH = 44;
|
||||
|
||||
late final PdfControllerPinch _ctrl;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_ctrl = PdfControllerPinch(document: widget.documentFuture);
|
||||
_ctrl.addListener(_onCtrl);
|
||||
}
|
||||
|
||||
void _onCtrl() {
|
||||
// [PdfViewPinch] met à jour documentProgress dans son propre listener :
|
||||
// replanifier pour lire la valeur après ce tour de notifications.
|
||||
Future.microtask(() {
|
||||
if (mounted) setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_ctrl.removeListener(_onCtrl);
|
||||
_ctrl.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final raw = _ctrl.documentProgress;
|
||||
final safeP = (!raw.isFinite || raw.isNaN) ? 0.0 : raw.clamp(0.0, 1.0);
|
||||
|
||||
return Stack(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: _barW),
|
||||
child: PdfViewPinch(
|
||||
padding: 10,
|
||||
scrollDirection: Axis.vertical,
|
||||
minScale: 1,
|
||||
maxScale: 4,
|
||||
controller: _ctrl,
|
||||
backgroundDecoration: const BoxDecoration(color: Colors.white),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 6,
|
||||
right: 1,
|
||||
bottom: 6,
|
||||
width: _barW,
|
||||
child: LayoutBuilder(
|
||||
builder: (context, c) {
|
||||
final trackH = c.maxHeight;
|
||||
final thumbH = (trackH * 0.2).clamp(_minThumbH, trackH * 0.5);
|
||||
final maxTop = (trackH - thumbH).clamp(0.0, double.infinity);
|
||||
final top = safeP * maxTop;
|
||||
|
||||
return Semantics(
|
||||
label: 'Position dans le document',
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: _ModaleCouleur.turquoise.withValues(
|
||||
alpha: 0.35,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: top,
|
||||
left: (_barW - _thumbW) / 2,
|
||||
child: Container(
|
||||
width: _thumbW,
|
||||
height: thumbH,
|
||||
decoration: BoxDecoration(
|
||||
color: _ModaleCouleur.turquoiseFonce,
|
||||
borderRadius: BorderRadius.circular(_thumbW / 2),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Color(0x40000000),
|
||||
blurRadius: 3,
|
||||
offset: Offset(0, 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
return PdfViewPinch(
|
||||
padding: 10,
|
||||
scrollDirection: Axis.vertical,
|
||||
controller: _ctrl,
|
||||
backgroundDecoration: const BoxDecoration(color: Colors.white),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user