You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中OutlinedButton偶发StackOverflowError问题求助

偶发StackOverflowError:Material组件OutlinedButton相关问题

错误日志

======== 由widgets库捕获的异常 =======================================================
构建OutlinedButton(style: ButtonStyle#dc6a2(foregroundColor: _OutlinedButtonDefaultColor#e3bc0, overlayColor: _OutlinedButtonDefaultOverlay#5f70a, side: MaterialStatePropertyAll(BorderSide(width: 0.0, style: none))), dependencies: [MediaQuery, _InheritedTheme, _LocalizationsScope-[GlobalKey#28c3a]], state: _ButtonStyleState#ac5c5)时抛出以下StackOverflowError:
Stack Overflow

相关错误触发组件:
  OutlinedButton OutlinedButton:file:///Users/mac/Documents/flutter_projects/zense_timer_project/zense_timer/lib/pages/home_page/banner_settings/custom_home_button.dart:35:20
异常抛出时的调用栈:
#0      _LerpProperties.resolve (package:flutter/src/material/material_state.dart:693:3)
#1      _LerpProperties.resolve (package:flutter/src/material/material_state.dart:695:29)
#2      _LerpProperties.resolve (package:flutter/src/material/material_state.dart:695:29)
...(中间重复栈帧省略)
#4232   RenderStack._computeSize (package:flutter/src/rendering/stack.dart:580:43)
#4233   RenderStack.performLayout (package:flutter/src/rendering/stack.dart:607:12)
#4234   RenderObject.layout (package:flutter/src/rendering/object.dart:2395:7)
...(后续布局栈帧省略)
====================================================================================================

注意:目前仅在iPhone模拟器上触发该错误,Android模拟器未出现此问题。

相关代码

自定义按钮所在的Row容器代码

return Align(
      alignment: Alignment.bottomCenter,
      child: SizedBox(
        height: kToolbarHeight * 1.25,
        width: size.width,
        child: appState.sessionState == SessionState.notStarted ?
          IgnorePointer(
            ignoring: appState.sessionState != SessionState.notStarted,
            child: Container(
              decoration: BoxDecoration(
                color: backgroundColor,
                borderRadius: const BorderRadius.only(
                  topLeft: Radius.circular(kBorderRadiusBig),
                  topRight: Radius.circular(kBorderRadiusBig),
                ),
              ),

              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  CustomHomeButton(
                    title: bellTitle,
                    iconData: !audioState.bellOnStart &&
                            audioState.bellSound.name == kNone &&
                            audioState.bellOnEndSound.name == kNone
                        ? FontAwesomeIcons.bellSlash
                        : FontAwesomeIcons.bell,
                    onPressed: () {
                      /// 回调代码...
                    },
                  ),
                  CustomHomeButton(
                    title: audioState.ambience.toText(),
                    iconData: audioState.ambience.toIcon(),
                    onPressed: () async {
                           /// 回调代码...
                    },
                  ),
                  CustomHomeButton(
                      title: appState.selectedPreset == ""
                          ? kPresets
                          : appState.selectedPreset,
                      iconData: Icons.tune_outlined,
                      onPressed: () {
                           /// 回调代码...
                      }),
                  //// 其他按钮...

自定义按钮组件代码

import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:zense_timer/configs/constants.dart';
import 'package:zense_timer/state/app_state.dart';

class CustomHomeButton extends StatelessWidget {
  const CustomHomeButton({
    super.key,
    required this.iconData,
    required this.onPressed,
    this.title,
  });

  final String? title;
  final IconData iconData;
  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
        return SizedBox(
          width: size.width / 4,
          height: size.height * 0.8, // 存在布局约束问题
          child: OutlinedButton(
            style: OutlinedButton.styleFrom(
                side: BorderSide.none, // 可能触发样式插值递归
            ),

            onPressed: onPressed,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Padding(
                  padding:
                      EdgeInsets.symmetric(vertical: size.height * 0.003),
                  child: Center(child: Icon(iconData)),
                ),
                SizedBox(
                  width: size.width / 4,
                  height: size.height * 0.018,
                  child: title != null
                      ? Text(
                          title!,
                          textAlign: TextAlign.center,
                          style: Theme.of(context)
                              .textTheme
                              .labelSmall!
                              .copyWith(
                                  color: Theme.of(context).colorScheme.onBackground,
                                  fontSize: 8,),
                          maxLines: 1,
                          overflow: TextOverflow.ellipsis,
                        )
                      : null,
                ),
              ],
            ),
          ),
        );

  }
}

解决方案

问题根源

  1. 布局约束冲突:CustomHomeButton内部的SizedBox设置了height: size.height * 0.8,但父容器高度仅为kToolbarHeight * 1.25(约70),远小于按钮设置的高度,导致iOS布局系统计算约束时触发无限递归。
  2. 样式插值递归:BorderSide.none与OutlinedButton默认样式的插值逻辑冲突,_LerpProperties.resolve方法递归解析样式时出现无限调用。

修复步骤

  1. 修正按钮高度约束:将CustomHomeButton中的SizedBox高度改为double.infinity,让按钮自动填充父容器高度,避免约束冲突:
    SizedBox(
      width: size.width / 4,
      height: double.infinity, // 替换原有的size.height * 0.8
      child: OutlinedButton(
        // ...其他代码
      ),
    )
    
  2. 调整边框样式设置:不要使用BorderSide.none,改用BorderSide(width: 0),或者直接替换为TextButton(无需边框时更贴合需求):
    // 方案1:修改side参数
    style: OutlinedButton.styleFrom(
      side: const MaterialStatePropertyAll(BorderSide(width: 0)),
    ),
    // 方案2:替换为TextButton
    child: TextButton(
      style: TextButton.styleFrom(padding: EdgeInsets.zero),
      onPressed: onPressed,
      child: Column(/* ...原有子组件 */),
    )
    
  3. 清理冗余布局:外层已判断appState.sessionState == SessionState.notStarted,可以简化IgnorePointer的条件:
    IgnorePointer(
      ignoring: appState.sessionState != SessionState.notStarted,
      child: Container(/* ... */),
    )
    

内容的提问来源于stack exchange,提问作者Kdon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 13:12:00