Flutter中YoutubePlayer全屏未占满屏幕及方向错误问题求助
点击YoutubePlayer全屏按钮后,设备仅切换为横屏,但播放器无法占满整个屏幕,控制台输出以下错误:
Failed to change device orientation: Error Domain=UISceneErrorDomain Code=101 “None of the requested orientations are supported by the view controller. Requested: portrait; Supported: landscapeLeft, landscapeRight “ UserInfo={NSLocalizedDescription=None of the requested orientations are supported by the view controller. Requested: portrait; Supported: landscapeLeft, landscapeRight}
相关代码
Widget _buildRegularCard() { return Scaffold( appBar: AppBar( centerTitle: true, backgroundColor: Colors.black, leading: IconButton( onPressed: () => Navigator.pop(context, true), icon: const Icon( Icons.arrow_back, color: Colors.white, ), ), ), body: StreamBuilder( stream: FirebaseFirestore.instance .collection(widget.collectionName) .doc(widget.documentId) .snapshots(), builder: (context, snapshot) { if (snapshot.hasError) { return const Text('Something went wrong'); } if (snapshot.connectionState == ConnectionState.waiting) { return const Center( child: CircularProgressIndicator( color: Colors.black, strokeWidth: 15, semanticsLabel: "Chargement", ) ); } String? videoId = YoutubePlayer.convertUrlToId(snapshot.data?["video"]); YoutubePlayerController controller = YoutubePlayerController( initialVideoId: videoId!, flags: const YoutubePlayerFlags( autoPlay: false, mute: false, showLiveFullscreenButton: true ), ); controller.addListener(() { if (controller.value.isFullScreen) { SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); } else { SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); } }); return SingleChildScrollView( scrollDirection: Axis.vertical, child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Stack( children: [ CachedNetworkImage( height: 400, width: double.infinity, fit: BoxFit.cover, imageUrl: snapshot.data?["image"], ), LikeButton( mainAxisAlignment: MainAxisAlignment.end, isLiked: isInFavorites, size: 60, onTap: (isLiked) async { String uid = FirebaseAuth.instance.currentUser?.uid ?? ""; if (uid.isNotEmpty) { if (isInFavorites == true) { await FirebaseFirestore.instance .collection('Utilisateurs') .doc(uid) .collection('favoris') .doc(widget.documentId) .delete(); print("******* Suppresion du jeux"); } else { await FirebaseFirestore.instance .collection('Utilisateurs') .doc(uid) .collection('favoris') .doc(widget.documentId) .set({ 'nom': snapshot.data?["nom"], 'image': snapshot.data?['image'], 'age': snapshot.data?['age'], 'but': snapshot.data?['but'], 'materiel': snapshot.data?['materiel'], 'nombreJoueurs': snapshot.data?['nombreJoueurs'], 'rating': snapshot.data?['rating'], 'type de jeux': snapshot.data?['type de jeux'], 'video': snapshot.data?['video'], }); print("Ajout du jeux"); } setState(() { if (isInFavorites == true) { isInFavorites = false; } else { isInFavorites = true; } }); } }, bubblesColor: BubblesColor( dotPrimaryColor: Theme.of(context).primaryColor, dotSecondaryColor: Theme.of(context).secondaryHeaderColor, ), circleColor: const CircleColor( start: Colors.yellow, end: Colors.green ), likeBuilder: (test) { isInFavorites = test; test == false ? Image.asset("assets/icons/unlike.png") : Image.asset("assets/icons/like.png"); }, ), ], ), RatingStarFix( size: 40, collectionName: widget.collectionName, documentId: widget.documentId, ), TextActivitesDescriptionTitle( text: snapshot.data?["nom"], color: const Color(0xffe2324e), size: 50, ), CardActivitiesDescription( text: snapshot.data?["nom"], padding: 0, color: Colors.black, subtext: "Nom : ", ), CardActivitiesDescription( text: "age", padding: 0, color: Colors.black, subtext: 'Age : ', ), CardActivitiesDescription( text: snapshot.data?["but"], padding: 0, color: Colors.black, subtext: "But : ", ), CardActivitiesDescription( text: snapshot.data?["materiel"], padding: 0, color: Colors.black, subtext: "Materiel : ", ), CardActivitiesDescription( text: snapshot.data?["nombreJoueurs"], padding: 0, color: Colors.black, subtext: 'Nombres Joueurs : ', ), CardActivitiesDescription( text: snapshot.data?["deroulement"], padding: 0, color: Colors.black, subtext: 'Deroullement : ', ), snapshot.data?["video"] == "" ? Container( height: 200, width: 200, child: const Center( child: Text("Video En cour de fabrication "), ), ) : Padding( padding: const EdgeInsets.all(15), child: YoutubePlayerBuilder( player: YoutubePlayer( controller: controller, aspectRatio: 16 / 7, showVideoProgressIndicator: true, progressIndicatorColor: Colors.amber, progressColors: const ProgressBarColors( playedColor: Colors.amber, handleColor: Colors.amberAccent, ), onReady: () { controller.addListener(() {}); }, ), builder: (BuildContext, player) { return Column( children: [ player, ], ); }, ), ), const Divider(height: 40, thickness: 2), const Text( "Commentaires", style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), ), StreamBuilder( stream: FirebaseFirestore.instance .collection(widget.collectionName) .doc(widget.documentId) .collection('comments') .snapshots(), builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) { if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator()); } var comments = snapshot.data!.docs; return ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: comments.length, itemBuilder: (context, index) { var comment = comments[index]; return ListTile( title: Text(comment['username']), subtitle: Text(comment['content']), trailing: RatingStarDisplay( size: 20, rating: comment['rating'], ), ); }, ); }, ), ElevatedButton( onPressed: () { Navigator.push( context, MaterialPageRoute( builder: (context) => AddCommentPage( documentId: widget.documentId, collectionName: widget.collectionName, ), ), ); }, child: const Text("Ajouter un commentaire"), ), ], ), ); }, ), ); }
问题原因
错误提示明确:退出全屏时,播放器请求切换回竖屏,但当前ViewController不支持竖屏方向,导致方向切换失败,进而破坏了全屏状态的正常流转,最终出现播放器无法占满屏幕的异常。另外,每次StreamBuilder重建都会重新创建YoutubePlayerController,导致状态丢失,也是问题的诱因之一。
修复方案
1. 允许页面支持所有所需方向
在当前页面的State类中添加方向控制逻辑:
@override void initState() { super.initState(); // 初始化时允许所有必要方向 SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight, ]); } @override void dispose() { // 页面销毁后恢复默认竖屏(根据APP需求调整) SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); super.dispose(); }
2. 避免重复创建YoutubePlayerController
将Controller移到State类中初始化,防止StreamBuilder重建时丢失状态:
late YoutubePlayerController _controller; @override void didChangeDependencies() { super.didChangeDependencies(); // 从Stream数据中安全获取videoUrl并初始化Controller final videoUrl = snapshot.data?["video"]; String? videoId = YoutubePlayer.convertUrlToId(videoUrl ?? ""); _controller = YoutubePlayerController( initialVideoId: videoId!, flags: const YoutubePlayerFlags( autoPlay: false, mute: false, showLiveFullscreenButton: true ), ); _controller.addListener(() { if (_controller.value.isFullScreen) { SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); } else { SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); // 确保退出全屏时方向可切换回竖屏 SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight, ]); } }); } @override void dispose() { _controller.dispose(); super.dispose(); }
3. 优化YoutubePlayerBuilder布局
全屏模式下让播放器直接占满屏幕,避免被Column约束:
YoutubePlayerBuilder( player: YoutubePlayer( controller: _controller, aspectRatio: 16 / 7, showVideoProgressIndicator: true, progressIndicatorColor: Colors.amber, progressColors: const ProgressBarColors( playedColor: Colors.amber, handleColor: Colors.amberAccent, ), ), builder: (context, player) { return _controller.value.isFullScreen ? player : Column(children: [player]); }, )
4. 检查全局方向设置
在main.dart中确保APP没有全局锁定方向:
void main() { WidgetsFlutterBinding.ensureInitialized(); SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight, ]); runApp(const MyApp()); }
内容的提问来源于stack exchange,提问作者nabil bouzid

