Flutter中‘Floating SnackBar presented off screen’异常修复问询
问题:Floating SnackBar 在部分设备上触发"显示在屏幕外"断言异常
错误日志
The following assertion was thrown during performLayout(): Floating SnackBar presented off screen. A SnackBar with behavior property set to SnackBarBehavior.floating is fully or partially off screen because some or all the widgets provided to Scaffold.floatingActionButton, Scaffold.persistentFooterButtons and Scaffold.bottomNavigationBar take up too much vertical space. Consider constraining the size of these widgets to allow room for the SnackBar to be visible. The relevant error-causing widget was Scaffold dashboard.dart:69 When the exception was thrown, this was the stack #0 _ScaffoldLayout.performLayout.<anonymous closure> #0 _ScaffoldLayout.performLayout.<anonymous closure> scaffold.dart:1189 #1 _ScaffoldLayout.performLayout scaffold.dart:1204 #2 MultiChildLayoutDelegate._callPerformLayout custom_layout.dart:240 #3 RenderCustomMultiChildLayoutBox.performLayout custom_layout.dart:410 #4 RenderObject._layoutWithoutResize object.dart:2027 #5 PipelineOwner.flushLayout object.dart:1020 #6 RendererBinding.drawFrame binding.dart:516 #7 WidgetsBinding.drawFrame binding.dart:865 #8 RendererBinding._handlePersistentFrameCallback binding.dart:381 #9 SchedulerBinding._invokeFrameCallback binding.dart:1289 #10 SchedulerBinding.handleDrawFrame binding.dart:1218 #11 SchedulerBinding._handleDrawFrame binding.dart:1076 #12 _invoke (dart:ui/hooks.dart:145:13) #13 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:338:5) #14 _drawFrame (dart:ui/hooks.dart:112:31) The following RenderObject was being processed when the exception was fired: RenderCustomMultiChildLayoutBox#3f6d8 NEEDS-LAYOUT NEEDS-COMPOSITING-BITS-UPDATE RenderObject: RenderCustomMultiChildLayoutBox#3f6d8 NEEDS-LAYOUT NEEDS-COMPOSITING-BITS-UPDATE needs compositing parentData: <none> (can use size) constraints: BoxConstraints(w=411.4, h=867.4) size: Size(411.4, 867.4) child 1: RenderRepaintBoundary#ba78b relayoutBoundary=up1 needs compositing
相关代码
SnackBar构建逻辑
SnackBar getSnackbar({ required String message, required Color backgroundColor, }) { return SnackBar( elevation: 8, backgroundColor: backgroundColor, behavior: SnackBarBehavior.floating, content: Row(children: [ Flexible( child: Text( message, style: const TextStyle( color: Colors.white, ), ), ) ]), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)), ); } // 触发SnackBar的代码 ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar(getSnackbar( backgroundColor: Colors.green, message: 'File saved to $filename', ));
页面Scaffold代码
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('SomeScreen'), ), body: Padding( padding: const EdgeInsets.all(8.0), child: _isLoading ? const Center(child: CircularProgressIndicator()) : Column(children: [ _displaySomething(), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ _displayAnotherThing(), ], )) ]), ), ); }
更新说明
经调试发现,该问题并非仅出现在Android 13高分辨率模拟器,旧版Android系统也会触发;仅当设置behavior: SnackBarBehavior.floating时出现异常,即使移除Scaffold内所有内容,仅保留触发SnackBar的TextButton,仍可复现问题,推测可能与Flutter近期更新有关。
解决办法
- 添加适配屏幕的边距:给floating类型的SnackBar设置
margin,利用MediaQuery获取系统底部安全区域的padding,避免被系统UI遮挡:
SnackBar getSnackbar({ required String message, required Color backgroundColor, required BuildContext context, // 新增context参数获取屏幕信息 }) { final bottomPadding = MediaQuery.of(context).padding.bottom; return SnackBar( elevation: 8, backgroundColor: backgroundColor, behavior: SnackBarBehavior.floating, // 设置边距,避开底部安全区域 margin: EdgeInsets.only( left: 16, right: 16, bottom: bottomPadding + 16, ), content: Row(children: [ Flexible( child: Text( message, style: const TextStyle( color: Colors.white, ), ), ) ]), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)), ); } // 调用时传入context ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar(getSnackbar( backgroundColor: Colors.green, message: 'File saved to $filename', context: context, ));
- 全局配置SnackBar主题:在应用的
ThemeData中统一设置floating SnackBar的边距,无需在每个SnackBar中单独配置:
MaterialApp( theme: ThemeData( snackBarTheme: SnackBarThemeData( behavior: SnackBarBehavior.floating, margin: EdgeInsets.only( left: 16, right: 16, bottom: MediaQuery.of(context).padding.bottom + 16, ), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)), elevation: 8, ), ), // ...其他配置 )
- 临时降级Flutter版本:如果确定是Flutter新版本引入的bug,可以降级到之前能正常运行的版本,例如执行命令:
flutter downgrade 3.10.6 # 替换为你之前正常的版本号
内容的提问来源于stack exchange,提问作者nck
相关产品推荐
相关产品推荐

