在iOS上,Flutter的 TypeAheadField组件的覆盖层和阴影在对话框后仍然可见

移动开发 2026-07-09

我在iOS上使用 flutter_typeahead 时遇到了一个问题。

当屏幕上已经存在一个 TypeAheadField 时,打开对话框(showDialog)后,对话框背后会出现一个怪异的深色覆盖层/阴影/幽灵式UI。

这个问题只在iOS上出现。Android表现正常。

即使在以下情况下,这个问题仍然存在:

  • 建议已关闭
  • 键盘已收起
  • 输入框未获得焦点

看起来来自 TypeAheadFieldOverlayEntry 仍在对话框后面被渲染出来。

截图:

enter image description here

当前实现:

class _AddOnsTypeAhead extends StatelessWidget {
  const _AddOnsTypeAhead({
    required this.index,
    required this.controller,
  });

  final int index;
  final VendorServiceController controller;

  @override
  Widget build(BuildContext context) {
    return TypeAheadField<AllAddonsModel>(
      constraints: const BoxConstraints(maxHeight: 250),

      listBuilder: (context, children) =>
          CustomTypeAheadScrollbar(
            maxHeight: 250,
            children: children,
          ),

      direction: VerticalDirection.up,

      hideOnEmpty: true,

      controller: controller.addOnsType[index],

      suggestionsCallback: (pattern) {
        return controller.allAddonsModel.where((element) {
          final name = (element.name ?? '').toLowerCase();
          final patternLower = pattern.toLowerCase();

          final isDuplicate = controller.addOnsList.any(
            (e) => (e.name ?? '').toLowerCase() == name,
          );

          return name.contains(patternLower) && !isDuplicate;
        }).toList();
      },

      decorationBuilder: (context, child) => DecoratedBox(
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.circular(12),
          boxShadow: [
            BoxShadow(
              color: Colors.black.withOpacity(0.2),
              offset: const Offset(0, 5),
              blurRadius: 4,
            ),
          ],
        ),
        child: child,
      ),

      itemBuilder: (context, suggestion) {
        return ListTile(
          title: Text(suggestion.name ?? ''),
        );
      },

      onSelected: (suggestion) {
        controller.addOnsType[index].text =
            suggestion.name ?? '';
      },

      builder: (context, textController, focusNode) {
        return TextField(
          focusNode: focusNode,
          controller: textController,
        );
      },
    );
  }
}

对话框代码:

void openChangeDialog(BuildContext context) {
  showDialog(
    context: context,
    builder: (context) => BackdropFilter(
      filter: ImageFilter.blur(sigmaY: 3, sigmaX: 3),
      child: Dialog(
        backgroundColor: ThemeColors.scaffoldBack(context),
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(Utils.s20)),
        child: Padding(
          padding: const .all(Utils.commonPad),
          child: Column(
            mainAxisSize: .min,
            children: [
              const AppText(
                'Leave without saving?',
                fontSize: Utils.s22,
                fontFamily: FontFamily.bold,
                textAlign: .center,
              ),
              Utils.s12.vertical,
              Row(
                children: [
                  Expanded(
                    child: CommonBtn(
                      AppString.yes,
                      vertical: Utils.s12,
                      btnColor: ThemeColors.textColor(context),
                      onTap: () {
                        pageBack(context);
                        pageBack(context);
                      },
                    ),
                  ),
                  Utils.s15.horizontal,
                  Expanded(
                    child: CommonBtn(vertical: Utils.s12, AppString.no, onTap: () => pageBack(context)),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    ),
  );
}

解决方案

BackdropFilter 不能直接成为 showDialog 构建器的直接子节点。相反,设置 barrierColor: Colors.transparent,并在对话框控件内部自行处理模糊和变暗效果。

void openChangeDialog(BuildContext context) {
  showDialog(
    context: context,
    barrierColor: Colors.transparent, // 👈 kill the default dark barrier
    builder: (context) => Stack(
      children: [
        // Your blur + dim layer — scoped entirely within the dialog route
        BackdropFilter(
          filter: ImageFilter.blur(sigmaX: 3, sigmaY: 3),
          child: Container(
            color: Colors.black.withOpacity(0.3), // replaces the barrier dimming
          ),
        ),
        // The actual dialog
        Center(
          child: Dialog(
            backgroundColor: ThemeColors.scaffoldBack(context),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(Utils.s20),
            ),
            child: Padding(
              padding: const EdgeInsets.all(Utils.commonPad),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  const AppText(
                    'Leave without saving?',
                    fontSize: Utils.s22,
                    fontFamily: FontFamily.bold,
                    textAlign: TextAlign.center,
                  ),
                  Utils.s12.vertical,
                  Row(
                    children: [
                      Expanded(
                        child: CommonBtn(
                          AppString.yes,
                          vertical: Utils.s12,
                          btnColor: ThemeColors.textColor(context),
                          onTap: () {
                            pageBack(context);
                            pageBack(context);
                          },
                        ),
                      ),
                      Utils.s15.horizontal,
                      Expanded(
                        child: CommonBtn(
                          AppString.no,
                          vertical: Utils.s12,
                          onTap: () => pageBack(context),
                        ),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ),
        ),
      ],
    ),
  );
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章