diff --git a/b0esche_cloud/lib/pages/home_page.dart b/b0esche_cloud/lib/pages/home_page.dart index 125b207..31a3781 100644 --- a/b0esche_cloud/lib/pages/home_page.dart +++ b/b0esche_cloud/lib/pages/home_page.dart @@ -417,47 +417,38 @@ class _HomePageState extends State with TickerProviderStateMixin { right: 32, ), child: Row( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, children: [ // Title - Expanded( - flex: 3, - child: Align( - alignment: Alignment.centerLeft, - child: Builder( - builder: (context) { - final screenWidth = MediaQuery.of( - context, - ).size.width; - final fontSize = screenWidth < 600 ? 24.0 : 48.0; - return Text( - 'b0esche.cloud', - style: TextStyle( - fontFamily: 'PixelatedElegance', - fontSize: fontSize, - color: AppTheme.primaryText, - decoration: TextDecoration.underline, - decorationColor: AppTheme.primaryText, - fontFeatures: const [FontFeature.slashedZero()], - ), - ); - }, - ), - ), + Builder( + builder: (context) { + final screenWidth = MediaQuery.of(context).size.width; + final fontSize = screenWidth < 600 ? 24.0 : 48.0; + return Text( + 'b0esche.cloud', + style: TextStyle( + fontFamily: 'PixelatedElegance', + fontSize: fontSize, + color: AppTheme.primaryText, + decoration: TextDecoration.underline, + decorationColor: AppTheme.primaryText, + fontFeatures: const [FontFeature.slashedZero()], + ), + ); + }, ), - // Audio bar (max 1/4 width, right-aligned) + const SizedBox(width: 32), + // Audio bar (centered between title and nav buttons) Expanded( - flex: 1, child: AnimatedBuilder( animation: _audioBarController, builder: (context, child) { return (_showAudioBar && _audioFileName != null && _audioFileUrl != null) - ? Align( - alignment: Alignment.topRight, + ? Center( child: FractionallySizedBox( - widthFactor: 1.0, + widthFactor: 0.7, child: SlideTransition( position: _audioBarOffset, child: AudioPlayerBar( @@ -475,6 +466,32 @@ class _HomePageState extends State with TickerProviderStateMixin { }, ), ), + const SizedBox(width: 32), + // Navigation buttons (Drive, Mail, Add, Profile) + BlocBuilder( + builder: (context, state) { + final isLoggedIn = state is AuthAuthenticated; + if (!isLoggedIn) { + return const SizedBox.shrink(); + } + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + _buildNavButton('Drive', Icons.cloud), + const SizedBox(width: 16), + _buildNavButton('Mail', Icons.mail), + const SizedBox(width: 16), + _buildNavButton('Add', Icons.add), + const SizedBox(width: 16), + _buildNavButton( + 'Profile', + Icons.person, + isAvatar: true, + ), + ], + ); + }, + ), ], ), ), @@ -729,30 +746,7 @@ class _HomePageState extends State with TickerProviderStateMixin { }, ), ), - Positioned( - top: 0, - left: 0, - right: 0, - child: Center( - child: Builder( - builder: (context) { - final screenWidth = MediaQuery.of(context).size.width; - final fontSize = screenWidth < 600 ? 24.0 : 48.0; - return Text( - 'b0esche.cloud', - style: TextStyle( - fontFamily: 'PixelatedElegance', - fontSize: fontSize, - color: AppTheme.primaryText, - decoration: TextDecoration.underline, - decorationColor: AppTheme.primaryText, - fontFeatures: const [FontFeature.slashedZero()], - ), - ); - }, - ), - ), - ), + // Removed duplicate title Positioned widget Positioned( top: MediaQuery.of(context).size.width < 600 ? 40 : 10, right: 20, diff --git a/b0esche_cloud/lib/widgets/audio_player_bar.dart b/b0esche_cloud/lib/widgets/audio_player_bar.dart index 2d61472..6a85fdd 100644 --- a/b0esche_cloud/lib/widgets/audio_player_bar.dart +++ b/b0esche_cloud/lib/widgets/audio_player_bar.dart @@ -20,8 +20,10 @@ class AudioPlayerBar extends StatefulWidget { State createState() => _AudioPlayerBarState(); } -class _AudioPlayerBarState extends State { +class _AudioPlayerBarState extends State + with SingleTickerProviderStateMixin { late AudioPlayer _audioPlayer; + late AnimationController _iconController; Duration _duration = Duration.zero; Duration _position = Duration.zero; bool _isPlaying = false; @@ -31,9 +33,20 @@ class _AudioPlayerBarState extends State { void initState() { super.initState(); _audioPlayer = AudioPlayer(); + _iconController = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 250), + ); _initAudio(); } + @override + void dispose() { + _iconController.dispose(); + _audioPlayer.dispose(); + super.dispose(); + } + Future _initAudio() async { try { await _audioPlayer.setUrl(widget.fileUrl); @@ -55,58 +68,61 @@ class _AudioPlayerBarState extends State { setState(() { _isLoading = false; }); - // Optionally show error + } + } + + void _handlePlayPause() { + if (_isPlaying) { + _audioPlayer.pause(); + _iconController.reverse(); + } else { + _audioPlayer.play(); + _iconController.forward(); } } @override Widget build(BuildContext context) { + // Sync icon animation with state + if (_isPlaying && + _iconController.status != AnimationStatus.forward && + _iconController.value == 0.0) { + _iconController.forward(); + } else if (!_isPlaying && + _iconController.status != AnimationStatus.reverse && + _iconController.value == 1.0) { + _iconController.reverse(); + } return AnimatedContainer( duration: const Duration(milliseconds: 300), - height: 64, + height: 48, + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: AppTheme.glassDecoration.copyWith( - borderRadius: BorderRadius.circular(16), + borderRadius: BorderRadius.circular(24), boxShadow: [ BoxShadow( - color: AppTheme.accentColor.withOpacity(0.18), - blurRadius: 16, - offset: const Offset(0, 4), + color: AppTheme.accentColor.withValues(alpha: 0.15), + blurRadius: 12, + offset: const Offset(0, 2), ), ], ), child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - // Animated play/pause button - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: ModernGlassButton( - onPressed: _isLoading - ? () {} // no-op when loading - : () { - if (_isPlaying) { - _audioPlayer.pause(); - } else { - _audioPlayer.play(); - } - }, - child: AnimatedSwitcher( - duration: const Duration(milliseconds: 250), - transitionBuilder: (child, anim) => - ScaleTransition(scale: anim, child: child), - child: _isPlaying - ? const Icon( - Icons.pause, - key: ValueKey('pause'), - color: AppTheme.primaryText, - ) - : const Icon( - Icons.play_arrow, - key: ValueKey('play'), - color: AppTheme.primaryText, - ), - ), + // Play/Pause button (AnimatedIcon) + ModernGlassButton( + onPressed: _isLoading ? () {} : _handlePlayPause, + + child: AnimatedIcon( + icon: AnimatedIcons.play_pause, + progress: _iconController, + color: AppTheme.primaryText, + size: 22, ), ), + const SizedBox(width: 10), // File name and slider Expanded( child: Column( @@ -118,62 +134,68 @@ class _AudioPlayerBarState extends State { style: const TextStyle( color: AppTheme.primaryText, fontWeight: FontWeight.w600, - fontSize: 15, + fontSize: 14, ), overflow: TextOverflow.ellipsis, ), - SliderTheme( - data: SliderTheme.of(context).copyWith( - trackHeight: 3, - thumbShape: const RoundSliderThumbShape( - enabledThumbRadius: 7, + SizedBox( + height: 18, + child: SliderTheme( + data: SliderTheme.of(context).copyWith( + trackHeight: 2.2, + thumbShape: const RoundSliderThumbShape( + enabledThumbRadius: 6, + ), + overlayShape: SliderComponentShape.noOverlay, + activeTrackColor: AppTheme.accentColor, + inactiveTrackColor: AppTheme.accentColor.withValues( + alpha: 0.18, + ), + ), + child: Slider( + min: 0, + max: _duration.inMilliseconds.toDouble(), + value: _position.inMilliseconds + .clamp(0, _duration.inMilliseconds) + .toDouble(), + onChanged: _isLoading + ? null + : (value) { + _audioPlayer.seek( + Duration(milliseconds: value.toInt()), + ); + }, + activeColor: AppTheme.accentColor, + inactiveColor: AppTheme.accentColor.withValues( + alpha: 0.18, + ), ), - overlayShape: SliderComponentShape.noOverlay, - activeTrackColor: AppTheme.accentColor, - inactiveTrackColor: AppTheme.accentColor.withOpacity(0.2), - ), - child: Slider( - min: 0, - max: _duration.inMilliseconds.toDouble(), - value: _position.inMilliseconds - .clamp(0, _duration.inMilliseconds) - .toDouble(), - onChanged: _isLoading - ? null - : (value) { - _audioPlayer.seek( - Duration(milliseconds: value.toInt()), - ); - }, - activeColor: AppTheme.accentColor, - inactiveColor: AppTheme.accentColor.withOpacity(0.2), ), ), ], ), ), + const SizedBox(width: 10), // Time - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Text( - '${_formatDuration(_position)} / ${_formatDuration(_duration)}', - style: const TextStyle( - color: AppTheme.secondaryText, - fontSize: 13, - fontWeight: FontWeight.w500, - ), + Text( + '${_formatDuration(_position)} / ${_formatDuration(_duration)}', + style: const TextStyle( + color: AppTheme.secondaryText, + fontSize: 12, + fontWeight: FontWeight.w500, ), ), - // Close button + // Close button (smaller) if (widget.onClose != null) Padding( - padding: const EdgeInsets.only(right: 8.0), + padding: const EdgeInsets.only(left: 8.0), child: ModernGlassButton( onPressed: widget.onClose!, + child: const Icon( Icons.close, color: AppTheme.primaryText, - size: 20, + size: 18, ), ), ),