Flutter中Youtube播放器全屏时底部溢出问题求助
问题解决:Flutter YoutubePlayer全屏切换时底部Widget溢出
核心问题分析
- 控制器初始化位置错误:你在
build方法里每次重建都会重新创建YoutubePlayerController,导致播放器状态不稳定,全屏切换时布局异常。 - 布局适配不足:横屏全屏时屏幕高度大幅缩小,原布局的
Column + Flexible无法适配高度变化,导致底部内容溢出。 - SingleChildScrollView嵌套错误:之前直接包裹整个body导致内容不显示,正确的做法是只包裹底部的可变内容区域。
具体修复步骤
1. 修正控制器初始化逻辑
把控制器初始化移到didChangeDependencies(而非build),保证只在视频ID变化时重建,同时添加状态监听:
late YoutubePlayerController _controller; String? _videoID; @override void didChangeDependencies() { super.didChangeDependencies(); final productId = ModalRoute.of(context)!.settings.arguments as String; final productProvider = Provider.of<ProductsProvider>(context); final getCurrProduct = productProvider.findProdById(productId); final videoURL = getCurrProduct.youtube; final videoID = YoutubePlayer.convertUrlToId(videoURL); // 仅当视频ID变化时重建控制器 if (_videoID != videoID) { _videoID = videoID; _controller = YoutubePlayerController( initialVideoId: videoID!, flags: const YoutubePlayerFlags(autoPlay: false), )..addListener(() { setState(() {}); // 监听状态变化,触发布局更新 }); } } @override void dispose() { _quantityTextController.dispose(); _controller.dispose(); // 新增:释放控制器资源 super.dispose(); }
2. 适配全屏状态的布局调整
根据播放器的全屏状态动态切换布局,非全屏时用SingleChildScrollView包裹底部内容避免溢出:
@override Widget build(BuildContext context) { Size size = Utils(context).getScreenSize; final Color color = Utils(context).color; final cartProvider = Provider.of<CartProvider>(context); final productId = ModalRoute.of(context)!.settings.arguments as String; final productProvider = Provider.of<ProductsProvider>(context); final getCurrProduct = productProvider.findProdById(productId); double usedPrice = getCurrProduct.isOnSale ? getCurrProduct.salePrice : getCurrProduct.price; double totalPrice = usedPrice * int.parse(_quantityTextController.text); bool? _isInCart = cartProvider.getCartItems.containsKey(getCurrProduct.id); final wishlistProvider = Provider.of<WishlistProvider>(context); bool? _isInWishlist = wishlistProvider.getWishlistItems.containsKey(getCurrProduct.id); final viewedProdProvider = Provider.of<ViewedProdProvider>(context); bool _isFullScreen = _controller.value.isFullScreen; return WillPopScope( onWillPop: () async { // 全屏状态下先退出全屏再返回 if (_isFullScreen) { _controller.toggleFullScreenMode(); return false; } await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); return true; }, child: Scaffold( resizeToAvoidBottomInset: false, appBar: _isFullScreen ? null : AppBar( leading: InkWell( borderRadius: BorderRadius.circular(12), onTap: () { SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, ]); Navigator.canPop(context) ? Navigator.pop(context) : null; }, child: Icon( IconlyLight.arrowLeft2, color: color, size: 24, ), ), elevation: 0, backgroundColor: Theme.of(context).scaffoldBackgroundColor), body: _isFullScreen ? YoutubePlayer( controller: _controller, showVideoProgressIndicator: true, onReady: () => debugPrint('Ready'), bottomActions: [ CurrentPosition(), ProgressBar( isExpanded: true, colors: const ProgressBarColors( playedColor: Colors.green, handleColor: Colors.greenAccent), ), const PlaybackSpeedButton(), FullScreenButton(), ], ) : Column( children: [ YoutubePlayer( controller: _controller, showVideoProgressIndicator: true, onReady: () => debugPrint('Ready'), bottomActions: [ CurrentPosition(), ProgressBar( isExpanded: true, colors: const ProgressBarColors( playedColor: Colors.green, handleColor: Colors.greenAccent), ), const PlaybackSpeedButton(), FullScreenButton(), ], ), Expanded( child: SingleChildScrollView( physics: const BouncingScrollPhysics(), child: Container( decoration: BoxDecoration( color: Theme.of(context).cardColor, borderRadius: const BorderRadius.only( topLeft: Radius.circular(40), topRight: Radius.circular(40), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only( top: 20, left: 30, right: 30, bottom: 0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Flexible( child: TextWidget( text: getCurrProduct.title, color: color, textSize: 25, isTitle: true, ), ), ], ), ), Padding( padding: const EdgeInsets.only(top: 20, left: 30, right: 30), child: Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox( width: 10, ), const Spacer(), ], ), ), const SizedBox( height: 5, ), Container( padding: const EdgeInsets.all(16.0), child: TextWidget( text: getCurrProduct.description, color: Colors.black, textSize: 18, isTitle: true, maxLines: 4, ), ), const SizedBox( height: 50, ), ], ), ), ), ), ], ), ), ); }
3. 其他细节优化
- 全屏时隐藏AppBar,避免播放器被遮挡;
- 返回键逻辑优化:全屏状态下先退出全屏,再执行页面返回;
- 用
Expanded代替Flexible,确保底部内容占据剩余空间,同时SingleChildScrollView允许内容滚动,彻底解决溢出问题。
内容的提问来源于stack exchange,提问作者Raphael Elimimian
相关产品推荐
相关产品推荐

