Android调整显示大小时Flutter AlertDialog崩溃问题求助
Flutter AlertDialog 系统显示大小调整后布局崩溃修复方案
问题场景
实现的AlertDialog在Android系统设置中调整显示大小(缩放级别)时出现布局崩溃,不同缩放级别下弹窗布局异常,伴随渲染报错。
实现代码
showDialog<void>( context: context, barrierDismissible: false, builder: (BuildContext context) { return StatefulBuilder(builder: (context, setState) { return DynamicColorBuilder(builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) { return AlertDialog( title: Text(AppLocalizations.of(context)!.accentColor), content: Column( mainAxisSize: MainAxisSize.min, children: [ Consumer<ThemeProvider>( builder: (c, themeProvider, _) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text("${AppLocalizations.of(context)!.dynamicColor}:"), const SizedBox(width: 10), Switch( value: dynamicColor, onChanged: (bool value) { setState(() { dynamicColor = value; themeProvider.setDynamicColor(value); }); }, ), ], ); }, ), const SizedBox(height: 10,), Stack( alignment: Alignment.center, children: [ Consumer<ThemeProvider>( builder: (c, themeProvider, _) => GridView.count( shrinkWrap: true, crossAxisCount: 4, physics: const NeverScrollableScrollPhysics(), crossAxisSpacing: 10, mainAxisSpacing: 10, children: List.generate(AppColors.primaryColors.length, (i) { bool isSelectedColor = AppColors.primaryColors[i] == themeProvider.selectedPrimaryColor; return GestureDetector( onTap: isSelectedColor ? null : () => { color = i, themeProvider.setSelectedPrimaryColor(AppColors.primaryColors[i]), }, child: Container( height: 300, decoration: BoxDecoration( color: AppColors.primaryColors[i], borderRadius: BorderRadius.circular(10), ), child: AnimatedOpacity( duration: const Duration(milliseconds: 200), opacity: isSelectedColor ? 1 : 0, child: Center( child: ClipRRect( borderRadius: BorderRadius.circular(8), child: AnimatedScale( duration: const Duration(milliseconds: 150), scale: dynamicColor && lightDynamic != null ? 0 : 1, child: Container( padding: const EdgeInsets.all(5), decoration: BoxDecoration( borderRadius: BorderRadius.circular(3), color: Theme.of(context).cardColor.withOpacity(0.5), ), child: const Icon(Icons.check,size: 20,), ), ), ), ), ), ), ); }, ), ), ), AnimatedScale( duration: const Duration(milliseconds: 150), scale: dynamicColor && lightDynamic != null ? 1 : 0, child: Container( width: MediaQuery.of(context).size.width, height: 150, decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: Theme.of(context).colorScheme.surface.withOpacity(0.9) ), child: Padding( padding: const EdgeInsets.all(16.0), child: Center( child: Text( AppLocalizations.of(context)!.accentNotAvailable, textAlign: TextAlign.center, style: TextStyle(color: Theme.of(context).colorScheme.onSurface), ), ), ), ), ), ], ), ], ), // Action Buttons actions: <Widget>[ TextButton( child: Text(AppLocalizations.of(context)!.cancel), onPressed: () { Navigator.of(context).pop(); color = StorageManager.getColor()!; dynamicColor = StorageManager.getDynamicColor()!; Provider.of<ThemeProvider>(context, listen: false).setSelectedPrimaryColor(AppColors.primaryColors[color]); Provider.of<ThemeProvider>(context, listen: false).setDynamicColor(dynamicColor); getSettings(); }, ), TextButton( child: Text(AppLocalizations.of(context)!.apply), onPressed: () { Navigator.of(context).pop(); StorageManager.setColor(color); StorageManager.setDynamicColor(dynamicColor); }, ), ], ); }); }); }, );
显示异常情况
- 显示大小调至最大时:弹窗布局超出屏幕,元素挤压错乱
- 显示大小调至最小时:弹窗布局收缩异常,元素排版混乱
报错信息
════════ Exception caught by rendering library ═════════════════════════════════ RenderShrinkWrappingViewport does not support returning intrinsic dimensions. The relevant error-causing widget was AlertDialog appbar.dart:288 ════════════════════════════════════════════════════════════════════════════════ ════════ Exception caught by rendering library ═════════════════════════════════ RenderSemanticsAnnotations does not meet its constraints. The relevant error-causing widget was AlertDialog appbar.dart:288 ════════════════════════════════════════════════════════════════════════════════ ════════ Exception caught by rendering library ═════════════════════════════════ _RenderInkFeatures does not meet its constraints. The relevant error-causing widget was AlertDialog appbar.dart:288 ════════════════════════════════════════════════════════════════════════════════ ════════ Exception caught by rendering library ═════════════════════════════════ RenderCustomPaint does not meet its constraints. The relevant error-causing widget was AlertDialog appbar.dart:288 ════════════════════════════════════════════════════════════════════════════════ ════════ Exception caught by rendering library ═════════════════════════════════ RenderPhysicalShape does not meet its constraints. The relevant error-causing widget was AlertDialog appbar.dart:288 ════════════════════════════════════════════════════════════════════════════════ ════════ Exception caught by widgets library ═══════════════════════════════════ 'package:flutter/src/rendering/object.dart': Failed assertion: line 1896 pos 14: '_debugSubtreeRelayoutRootAlreadyMarkedNeedsLayout()': is not true. object.dart:1896 The relevant error-causing widget was GridView appbar.dart:316 ════════════════════════════════════════════════════════════════════════════════ ════════ Exception caught by widgets library ═══════════════════════════════════ 'package:flutter/src/rendering/object.dart': Failed assertion: line 1896 pos 14: '_debugSubtreeRelayoutRootAlreadyMarkedNeedsLayout()': is not true. object.dart:1896 The relevant error-causing widget was GridView appbar.dart:316 ════════════════════════════════════════════════════════════════════════════════ ════════ Exception caught by widgets library ═══════════════════════════════════ 'package:flutter/src/foundation/node.dart': Failed assertion: line 112 pos 12: '_owner != null': is not true. node.dart:112 The relevant error-causing widget was GridView appbar.dart:316 ════════════════════════════════════════════════════════════════════════════════ ════════ Exception caught by widgets library ═══════════════════════════════════ 'package:flutter/src/foundation/node.dart': Failed assertion: line 112 pos 12: '_owner != null': is not true. node.dart:112 The relevant error-causing widget was GridView appbar.dart:316 ════════════════════════════════════════════════════════════════════════════════ ════════ Exception caught by widgets library ═══════════════════════════════════ 'package:flutter/src/rendering/object.dart': Failed assertion: line 1896 pos 14: '_debugSubtreeRelayoutRootAlreadyMarkedNeedsLayout()': is not true. object.dart:1896 The relevant error-causing widget was GridView appbar.dart:316 ════════════════════════════════════════════════════════════════════════════════ ════════ Exception caught by rendering library ═════════════════════════════════ RenderBox.size accessed beyond the scope of resize, layout, or permitted parent access. RenderBox can always access its own size, otherwise, the only object that is allowed to read RenderBox.size is its parent, if they have said they will. It you hit this assert trying to access a child's size, pass "parentUsesSize: true" to that child's layout(). 'package:flutter/src/rendering/box.dart': box.dart:1 Failed assertion: line 2017 pos 13: 'debugDoingThisResize || debugDoingThisLayout || _computingThisDryLayout ||(RenderObject.debugActiveLayout == parent && size._canBeUsedByParent)' The relevant error-causing widget was AlertDialog appbar.dart:288 ════════════════════════════════════════════════════════════════════════════════ ════════ Exception caught by rendering library ═════════════════════════════════ RenderBox.size accessed beyond the scope of resize, layout, or permitted parent access. RenderBox can always access its own size, otherwise, the only object that is allowed to read RenderBox.size is its parent, if they have said they will. It you hit this assert trying to access a child's size, pass "parentUsesSize: true" to that child's layout(). 'package:flutter/src/rendering/box.dart': box.dart:1 Failed assertion: line 2017 pos 13: 'debugDoingThisResize || debugDoingThisLayout || _computingThisDryLayout ||(RenderObject.debugActiveLayout == parent && size._canBeUsedByParent)' The relevant error-causing widget was AlertDialog appbar.dart:288 ════════════════════════════════════════════════════════════════════════════════ ════════ Exception caught by rendering library ═════════════════════════════════ RenderBox.size accessed beyond the scope of resize, layout, or permitted parent access. RenderBox can always access its own size, otherwise, the only object that is allowed to read RenderBox.size is its parent, if they have said they will. It you hit this assert trying to access a child's size, pass "parentUsesSize: true" to that child's layout(). 'package:flutter/src/rendering/box.dart': box.dart:1 Failed assertion: line 2017 pos 13: 'debugDoingThisResize || debugDoingThisLayout || _computingThisDryLayout ||(RenderObject.debugActiveLayout == parent && size._canBeUsedByParent)' The relevant error-causing widget was AlertDialog appbar.dart:288 ════════════════════════════════════════════════════════════════════════════════ ════════ Exception caught by rendering library ═════════════════════════════════ RenderBox.size accessed beyond the scope of resize, layout, or permitted parent access. RenderBox can always access its own size, otherwise, the only object that is allowed to read RenderBox.size is its parent, if they have said they will. It you hit this assert trying to access a child's size, pass "parentUsesSize: true" to that child's layout(). 'package:flutter/src/rendering/box.dart': box.dart:1 Failed assertion: line 2017 pos 13: 'debugDoingThisResize || debugDoingThisLayout || _computingThisDryLayout ||(RenderObject.debugActiveLayout == parent && size._canBeUsedByParent)'
已尝试无效方案
- 用SizedBox限制GridView和Column尺寸:低显示缩放时有效,高缩放时失效
- 使用
MediaQuery.of(context).copyWith(textScaleFactor: 1.0)固定文本缩放:未解决布局崩溃问题
修复方案
核心问题分析
报错指向RenderShrinkWrappingViewport不支持固有尺寸,以及GridView在嵌套布局中约束冲突。AlertDialog的content区域本身有最大宽度限制,嵌套的shrinkWrap: true的GridView在极端缩放时无法正确计算尺寸,导致布局溢出或约束不满足。
具体修复步骤
- 为AlertDialog的content添加滚动容器:用
SingleChildScrollView包裹Column,避免高缩放时内容超出弹窗高度 - 限制GridView的最大高度:通过计算行数和间距固定GridView高度,避免无限制扩展
- 移除GridView中固定的Container高度:改用
AspectRatio保持色块为正方形,适配不同缩放级别 - 调整AnimatedScale的容器宽度:用
double.infinity自适应弹窗内部宽度,避免超出边界
修复后的关键代码片段
content: LayoutBuilder( builder: (context, constraints) { return SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ // Switch部分代码保持不变 const SizedBox(height: 10), Stack( alignment: Alignment.center, children: [ Consumer<ThemeProvider>( builder: (c, themeProvider, _) { final itemCount = AppColors.primaryColors.length; final rowCount = (itemCount / 4).ceil(); // 计算GridView高度:每行高度50 + 行间距10 final gridHeight = (rowCount * 50) + (rowCount - 1) * 10; return SizedBox( height: gridHeight, child: GridView.count( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), crossAxisCount: 4, crossAxisSpacing: 10, mainAxisSpacing: 10, children: List.generate(itemCount, (i) { bool isSelectedColor = AppColors.primaryColors[i] == themeProvider.selectedPrimaryColor; return GestureDetector( onTap: isSelectedColor ? null : () { setState(() => color = i); themeProvider.setSelectedPrimaryColor(AppColors.primaryColors[i]); }, child: AspectRatio( aspectRatio: 1, child: Container( decoration: BoxDecoration( color: AppColors.primaryColors[i], borderRadius: BorderRadius.circular(10), ), child: AnimatedOpacity( duration: const Duration(milliseconds: 200), opacity: isSelectedColor ? 1 : 0, child: Center( child: ClipRRect( borderRadius: BorderRadius.circular(8), child: AnimatedScale( duration: const Duration(milliseconds: 150), scale: dynamicColor && lightDynamic != null ? 0 : 1, child: Container( padding: const EdgeInsets.all(5), decoration: BoxDecoration( borderRadius: BorderRadius.circular(3), color: Theme.of(context).cardColor.withOpacity(0.5), ), child: const Icon(Icons.check, size: 20), ), ), ), ), ), ), ), ); }), ), ); }, ), AnimatedScale( duration: const Duration(milliseconds: 150), scale: dynamicColor && lightDynamic != null ? 1 : 0, child: Container( width: double.infinity, height: 150, decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: Theme.of(context).colorScheme.surface.withOpacity(0.9) ), child: Padding( padding: const EdgeInsets.all(16.0), child: Center( child: Text( AppLocalizations.of(context)!.accentNotAvailable, textAlign: TextAlign.center, style: TextStyle(color: Theme.of(context).colorScheme.onSurface), ), ), ), ), ), ], ), ], ), ); }, ),
额外优化建议
- 为AlertDialog添加
scrollable: true属性(Flutter 2.5+支持),简化滚动布局实现 - 测试不同缩放级别下的布局,确保色块和文本不会挤压或溢出
- 动态颜色提示容器可根据弹窗高度调整最大高度,避免遮挡底部按钮
内容的提问来源于stack exchange,提问作者Yaros
相关产品推荐
相关产品推荐

