import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; // SystemChrome, DeviceOrientation 사용을 위해 import import 'package:youtube_player_flutter/youtube_player_flutter.dart'; class YoutubePlayerPage extends StatefulWidget { final String lessonUrl; // final int currentBottomNavIndex; const YoutubePlayerPage({ super.key, required this.lessonUrl, // this.currentBottomNavIndex = 0, }); @override State createState() => _YoutubePlayerPageState(); } class _YoutubePlayerPageState extends State { YoutubePlayerController? _controller; String? _videoId; bool _isPlayerReady = false; String _videoTitle = 'YouTube Video'; final int _currentBottomNavIndex = 0; bool _isSystemUiVisible = true; @override void initState() { super.initState(); // 페이지 진입 시 기본 화면 방향 설정 (선택적) // SystemChrome.setPreferredOrientations([ // DeviceOrientation.portraitUp, // DeviceOrientation.portraitDown, // ]); _videoId = YoutubePlayer.convertUrlToId(widget.lessonUrl); WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { final String snackBarMessage; if (_videoId != null) { snackBarMessage = 'Video ID: $_videoId'; } else { snackBarMessage = 'Could not extract Video ID from URL: ${widget.lessonUrl}'; } ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(snackBarMessage), duration: const Duration(seconds: 3), ), ); } }); if (_videoId != null) { _controller = YoutubePlayerController( initialVideoId: _videoId!, flags: const YoutubePlayerFlags( autoPlay: true, mute: false, // <<< 동영상 재생이 끝나면 컨트롤러를 자동으로 숨기지 않도록 설정 (선택적) >>> // hideControls: false, // 기본값은 true ), )..addListener(_playerListener); } else { print("Error: Could not extract video ID from URL: ${widget.lessonUrl}"); _videoTitle = 'Video Error'; } } void _playerListener() { if (_controller == null || !mounted) return; // <<< 재생 상태 감지 >>> if (_controller!.value.playerState == PlayerState.ended) { // 동영상 재생이 완료되었을 때 처리할 로직 print("Video has ended."); if (mounted) { // 전체 화면 모드였다면 해제 if (_controller!.value.isFullScreen) { _controller!.toggleFullScreenMode(); } // 시스템 UI를 다시 보이도록 설정 (toggleFullScreenMode가 자동으로 처리할 수도 있음) _showSystemUi(); // 세로 화면으로 복귀 (toggleFullScreenMode가 자동으로 처리할 수도 있음) SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, ]); // 필요하다면 페이지를 pop 하거나 다른 동작 수행 // 예: Navigator.of(context).pop(); // 또는 사용자에게 알림 표시 // ScaffoldMessenger.of(context).showSnackBar( // const SnackBar(content: Text('동영상 재생이 완료되었습니다.')), // ); } return; // ended 상태 처리 후 리스너의 나머지 로직은 건너뛸 수 있음 } if (_controller!.value.isFullScreen) { _hideSystemUi(); // 플레이어가 전체 화면으로 진입하면 가로 방향으로 설정 (선택적, 플레이어가 자동으로 할 수 있음) // SystemChrome.setPreferredOrientations([ // DeviceOrientation.landscapeLeft, // DeviceOrientation.landscapeRight, // ]); } else { _showSystemUi(); // <<< 전체 화면이 해제되면 화면 방향을 세로로 복구 >>> SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, ]); } if (_isPlayerReady) { if (_controller!.metadata.title.isNotEmpty && _videoTitle != _controller!.metadata.title) { if (mounted) { setState(() { _videoTitle = _controller!.metadata.title; }); } } } } void _hideSystemUi() { if (!_isSystemUiVisible) return; SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); if (mounted) { setState(() { _isSystemUiVisible = false; }); } } void _showSystemUi() { if (_isSystemUiVisible) return; SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); if (mounted) { setState(() { _isSystemUiVisible = true; }); } } void _onNotificationTapped() { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('알림 아이콘 클릭됨')), ); } void _onProfileTapped() { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('프로필 아이콘 클릭됨')), ); } void _onBottomNavItemTapped(int index) { if (_currentBottomNavIndex == index && index != 0) return; if (index == 0) { Navigator.of(context).popUntil((route) => route.isFirst); } else { String tabName = ''; switch (index) { case 1: tabName = 'Plan'; break; case 2: tabName = 'Statistics'; break; case 3: tabName = 'More'; break; } ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('$tabName 탭으로 이동 (구현 필요)')), ); } } // <<< 뒤로가기 버튼 처리 로직 >>> Future _onWillPop() async { if (_controller != null && _controller!.value.isFullScreen) { _controller!.toggleFullScreenMode(); // 전체 화면 해제 // _playerListener에서 화면 방향은 자동으로 세로로 설정될 것임 // _showSystemUi(); // 시스템 UI도 _playerListener에서 처리 return false; // 뒤로가기(페이지 pop) 막음 } // 전체 화면이 아닐 때는 일반적인 뒤로 가기 동작 허용 return true; } @override Widget build(BuildContext context) { final bool isFullScreen = _controller?.value.isFullScreen ?? false; // <<< WillPopScope로 Scaffold를 감싸서 뒤로가기 이벤트 가로채기 >>> return WillPopScope( onWillPop: _onWillPop, child: Scaffold( extendBodyBehindAppBar: isFullScreen, appBar: isFullScreen ? null : AppBar( leading: Navigator.canPop(context) ? IconButton( icon: const Icon(Icons.arrow_back_ios_new), // <<< AppBar의 뒤로가기 버튼도 _onWillPop 로직을 따르도록 수정 >>> onPressed: () async { if (await _onWillPop()) { // true를 반환하면 (즉, 전체화면이 아니면) 페이지 pop Navigator.pop(context); } }, ) : null, title: Text(_videoTitle), actions: [ IconButton( icon: const Icon(Icons.notifications_none), tooltip: '알림', onPressed: _onNotificationTapped, ), Padding( padding: const EdgeInsets.only(right: 16.0, left: 8.0), child: InkWell( onTap: _onProfileTapped, customBorder: const CircleBorder(), child: const CircleAvatar( radius: 16, backgroundColor: Colors.grey, child: Icon( Icons.person, size: 20, color: Colors.white, ), ), ), ), ], ), body: Center( child: _controller == null ? Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error_outline, color: Colors.red, size: 50), const SizedBox(height: 10), Padding( padding: const EdgeInsets.symmetric(horizontal: 20.0), child: Text( '비디오를 로드할 수 없습니다.\nURL: ${widget.lessonUrl}', textAlign: TextAlign.center, style: const TextStyle(fontSize: 16), ), ), ], ) : YoutubePlayer( controller: _controller!, showVideoProgressIndicator: true, progressIndicatorColor: Colors.amber, progressColors: const ProgressBarColors( playedColor: Colors.amber, handleColor: Colors.amberAccent, ), onReady: () { if (mounted) { setState(() { _isPlayerReady = true; if (_controller!.metadata.title.isNotEmpty) { _videoTitle = _controller!.metadata.title; } }); } print('Player is ready.'); }, // <<< 필요하다면 onEnded 콜백 직접 사용 가능 (addListener와 중복될 수 있으니 주의) >>> // onEnded: (metadata) { // print("Video has ended (onEnded callback)."); // if (mounted) { // if (_controller!.value.isFullScreen) { // _controller!.toggleFullScreenMode(); // } // _showSystemUi(); // SystemChrome.setPreferredOrientations([ // DeviceOrientation.portraitUp, // DeviceOrientation.portraitDown, // ]); // } // }, ), ), bottomNavigationBar: isFullScreen ? null : BottomNavigationBar( items: const [ BottomNavigationBarItem( icon: Icon(Icons.home_filled), label: 'Home', ), BottomNavigationBarItem( icon: Icon(Icons.calendar_today_outlined), label: 'Plan', ), BottomNavigationBarItem( icon: Icon(Icons.bar_chart_outlined), label: 'Statistics', ), BottomNavigationBarItem( icon: Icon(Icons.more_horiz_outlined), label: 'More', ), ], currentIndex: _currentBottomNavIndex, selectedItemColor: Colors.amber[800], unselectedItemColor: Colors.blue, onTap: _onBottomNavItemTapped, type: BottomNavigationBarType.fixed, ), ), ); } @override void deactivate() { // 페이지가 비활성화될 때 (예: 다른 화면으로 전환될 때) 플레이어 일시 중지 if (_controller != null && _isPlayerReady && _controller!.value.isPlaying) { _controller!.pause(); } super.deactivate(); } @override void dispose() { // <<< 페이지 종료 시 기본 화면 방향으로 복구 또는 모든 방향 허용 >>> SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, // DeviceOrientation.landscapeLeft, // 필요에 따라 가로모드도 허용 // DeviceOrientation.landscapeRight, ]); // 또는: SystemChrome.setPreferredOrientations(DeviceOrientation.values); // 모든 방향 허용 // 시스템 UI 복구 (이미 _showSystemUi()가 있지만, 명시적으로 호출) // _showSystemUi(); // 이 로직은 _isSystemUiVisible 상태에 따라 실행되므로 안전 // 또는 강제로 UI를 보이게 하려면: SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); _controller?.removeListener(_playerListener); _controller?.dispose(); super.dispose(); } }