diff --git a/lib/features/analysis/analysis_screen.dart b/lib/features/analysis/analysis_screen.dart index 9ee20dd..d5b69fb 100644 --- a/lib/features/analysis/analysis_screen.dart +++ b/lib/features/analysis/analysis_screen.dart @@ -56,6 +56,8 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> { bool _isCalibrating = false; bool _isSelectingReferences = false; bool _isFullscreenEditMode = false; + bool _isAtBottom = false; + final ScrollController _scrollController = ScrollController(); final TransformationController _transformationController = TransformationController(); final GlobalKey _imageKey = GlobalKey(); double _currentZoomScale = 1.0; @@ -64,12 +66,25 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> { void initState() { super.initState(); _transformationController.addListener(_onTransformChanged); + _scrollController.addListener(_onScroll); + } + + void _onScroll() { + if (!_scrollController.hasClients) return; + // Detect if we are near the bottom (within 20 pixels of the specific spacing we added) + final isBottom = _scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 20; + if (isBottom != _isAtBottom) { + setState(() { + _isAtBottom = isBottom; + }); + } } @override void dispose() { _transformationController.removeListener(_onTransformChanged); _transformationController.dispose(); + _scrollController.dispose(); super.dispose(); } @@ -160,26 +175,6 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> { } }, ), - floatingActionButton: Consumer( - builder: (context, provider, _) { - if (provider.state != AnalysisState.success) return const SizedBox.shrink(); - if (_isCalibrating) { - return FloatingActionButton.extended( - onPressed: () { - setState(() => _isCalibrating = false); - }, - backgroundColor: AppTheme.successColor, - icon: const Icon(Icons.check), - label: const Text('Valider'), - ); - } - return FloatingActionButton.extended( - onPressed: () => _saveSession(context, provider), - icon: const Icon(Icons.save), - label: const Text('Sauvegarder'), - ); - }, - ), ); } @@ -189,8 +184,11 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> { return _buildFullscreenEditContent(context, provider); } - return SingleChildScrollView( - child: Column( + return Stack( + children: [ + SingleChildScrollView( + controller: _scrollController, + child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Calibration mode indicator @@ -442,9 +440,13 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> { // Action buttons _buildActionButtons(context, provider), + + // Large spacing at the bottom to trigger the state change + const SizedBox(height: 100), ], ), ) + else // Calibration info Padding( @@ -498,7 +500,74 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> { ), ], ), - ); + ), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Align( + alignment: _isAtBottom ? Alignment.bottomCenter : Alignment.bottomRight, + child: Padding( + padding: _isAtBottom ? EdgeInsets.zero : const EdgeInsets.all(16.0), + child: _isCalibrating + ? FloatingActionButton.extended( + onPressed: () => setState(() => _isCalibrating = false), + backgroundColor: AppTheme.successColor, + icon: const Icon(Icons.check), + label: const Text('Valider'), + ) + : AnimatedContainer( + duration: const Duration(milliseconds: 260), + curve: Curves.easeInOut, + width: _isAtBottom ? MediaQuery.of(context).size.width : 180, + height: 56, + decoration: BoxDecoration( + color: AppTheme.primaryColor, + borderRadius: BorderRadius.circular(_isAtBottom ? 0 : 16), + boxShadow: [ + if (!_isAtBottom) + BoxShadow( + color: Colors.black.withOpacity(0.2), + blurRadius: 6, + offset: const Offset(0, 3), + ), + ], + ), + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: () => _saveSession(context, provider), + borderRadius: BorderRadius.circular(_isAtBottom ? 0 : 16), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), + child: FittedBox( + fit: BoxFit.scaleDown, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.save, color: Colors.white), + const SizedBox(width: 8), + Text( + _isAtBottom ? 'SAUVEGARDER LA SESSION' : 'Sauvegarder', + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16 + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ), + ), + ], + ); } Widget _buildZoomableImageWithOverlay(BuildContext context, AnalysisProvider provider) {