Flutter故事页全屏背景适配问题:iOS两侧出现白边
iOS设备Flutter沉浸式页面背景出现白边的解决方法
问题场景
开发故事页面时,背景使用图片或视频,通过SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive)实现全屏覆盖(包含状态栏),该方案在Android上表现正常,但iOS设备背景两侧出现白边。
问题截图:

原页面代码:
class StoriesPage extends StatefulWidget { final String title; final String text; final String backgroundMedia; final String? promoCode; final MediaTypes mediaType; const StoriesPage({ Key? key, required this.title, required this.text, required this.backgroundMedia, this.promoCode, required this.mediaType, }) : super(key: key); @override State<StoriesPage> createState() => _StoriesPageState(); } class _StoriesPageState extends State<StoriesPage> { late VideoPlayerController _videoController; @override void initState() { SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); if (widget.mediaType == MediaTypes.video) { _videoController = VideoPlayerController.networkUrl(Uri.parse(widget.backgroundMedia)) ..initialize().then((value) { _videoController.play(); _videoController.setLooping(true); setState(() {}); }); } super.initState(); } @override void dispose() { widget.mediaType == MediaTypes.video ? _videoController.dispose() : null; SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); super.dispose(); } @override Widget build(BuildContext context) { return Stack(children: [ widget.mediaType == MediaTypes.image ? Container( decoration: BoxDecoration( color: Colors.transparent, image: DecorationImage( image: NetworkImage(widget.backgroundMedia), fit: BoxFit.cover, )), ) : _videoController.value.isInitialized ? SizedBox.expand( child: FittedBox( fit: BoxFit.cover, child: SizedBox( width: _videoController.value.size.width, height: _videoController.value.size.height, child: VideoPlayer(_videoController), ), ), ) : Center( child: CircularProgressIndicator( color: context.colors.primary, ), ), Container( padding: const EdgeInsets.all(16), width: double.infinity, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ const Color.fromARGB(255, 6, 11, 17).withOpacity(0), const Color.fromARGB(60, 21, 44, 67).withOpacity(0.5), const Color.fromARGB(60, 10, 20, 31).withOpacity(1) ]), ), child: Column( mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[ Text( widget.title, style: context.text.headline1.copyWith( height: 1.21, color: Colors.white, fontWeight: FontWeight.w500, fontSize: 34), ), const SizedBox(height: 16), Text( widget.text, style: context.text.body3.copyWith(color: Colors.white), ), widget.promoCode != null ? Column( children: [ const SizedBox(height: 32), SizedBox( width: double.infinity, child: ElevatedButton( style: const ButtonStyle( backgroundColor: MaterialStatePropertyAll( Colors.transparent), elevation: MaterialStatePropertyAll(0), side: MaterialStatePropertyAll( BorderSide(color: Colors.white))), onPressed: () async { await Clipboard.setData( ClipboardData(text: widget.promoCode!)) .then((_) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Скопировано'))); }); }, child: Text(widget.promoCode.toString())), ), const SizedBox(height: 10), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () async { await Clipboard.setData(ClipboardData( text: widget.promoCode.toString())) .then((_) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Скопировано'))); }); }, child: const Text('Скопировать')), ), ], ) : const SizedBox(), const SizedBox(height: 20), ], ), ), Padding( padding: const EdgeInsets.all(20), child: Align( alignment: Alignment.topRight, child: GestureDetector( onTap: () => Navigator.of(context).pop(), child: Container( height: 48, width: 48, decoration: const ShapeDecoration( shape: CircleBorder(), color: Colors.white), child: const Padding( padding: EdgeInsets.all(8.0), child: Icon( Icons.close, size: 24, ), ), ), ), ), ), ]); } }
问题原因
iOS的安全区域机制与Flutter页面默认布局约束冲突,导致背景无法完全延伸至屏幕边缘;同时SystemUiMode.immersive在iOS上需要配合额外的系统UI样式配置,才能彻底消除留白。
解决步骤及修改代码
1. 让Stack强制占满屏幕
给外层Stack添加fit: StackFit.expand属性,确保所有子Widget填充整个屏幕空间。
2. 优化iOS系统UI配置
在initState中添加SystemChrome.setSystemUIOverlayStyle,设置状态栏和导航栏为透明,让内容延伸至安全区域外。
3. 调整背景容器约束
给图片背景的Container设置宽高为double.infinity,并添加黑色 fallback 颜色,避免加载前出现白边。
修改后的关键代码片段:
@override void initState() { super.initState(); // 配置沉浸式模式+透明状态栏/导航栏,适配iOS SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( statusBarColor: Colors.transparent, navigationBarColor: Colors.transparent, statusBarIconBrightness: Brightness.light, navigationBarIconBrightness: Brightness.light, )); if (widget.mediaType == MediaTypes.video) { _videoController = VideoPlayerController.networkUrl(Uri.parse(widget.backgroundMedia)) ..initialize().then((value) { _videoController.play(); _videoController.setLooping(true); setState(() {}); }); } } @override Widget build(BuildContext context) { return Stack( fit: StackFit.expand, // 关键:让Stack占满整个屏幕 children: [ widget.mediaType == MediaTypes.image ? Container( width: double.infinity, height: double.infinity, decoration: BoxDecoration( color: Colors.black, // 加载前 fallback 颜色 image: DecorationImage( image: NetworkImage(widget.backgroundMedia), fit: BoxFit.cover, ), ), ) : _videoController.value.isInitialized ? SizedBox.expand( child: FittedBox( fit: BoxFit.cover, child: SizedBox( width: _videoController.value.size.width, height: _videoController.value.size.height, child: VideoPlayer(_videoController), ), ), ) : Container( color: Colors.black, // 加载时背景设为黑色 child: Center( child: CircularProgressIndicator( color: context.colors.primary, ), ), ), // 其余代码保持不变 ], ); }
额外说明
- 设置黑色 fallback 颜色是为了避免图片/视频加载完成前出现白边;
SystemUiOverlayStyle中的图标亮度可根据背景颜色调整,保证可读性;dispose中恢复系统UI模式的逻辑无需修改。
内容的提问来源于stack exchange,提问作者lenol
相关产品推荐
相关产品推荐

