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

如何正确使用flutter_screenutil第二种方案?点击TextField报错求助

flutter_screenutil使用问题:点击TextField触发“No Overlay widget exists...”错误

问题描述

项目中使用flutter_screenutil的第二种配置方式后,点击TextField出现断言错误,需明确该包的正确使用方法。

报错信息

No Overlay widget exists above EditableText-[LabeledGlobalKey#d3506](controller: TextEditingController#dc3bb(TextEditingValue(text: ┤ghf├, selection: TextSelection.collapsed(offset: 3, affinity: TextAffinity.downstream, isDirectional: false), composing: TextRange(start: 0, end: 3))), focusNode: FocusNode#482e2([PRIMARY FOCUS]), autocorrect: false, debugLabel: ((englishLike titleMedium 2014).merge(blackMountainView titleMedium)).merge(unknown), inherit: false, color: Color(0xff878787), family: EncodeSans_regular, familyFallback: [EncodeSans], size: 16.0, weight: 400, baseline: alphabetic, decoration: TextDecoration.none, textAlign: start, keyboardType: TextInputType(name: TextInputType.text, signed: null, decimal: null), autofillHints: [], dirty, dependencies: [Directionality, MediaQuery, ScrollConfiguration], state: EditableTextState#1d73f(tickers: tracking 1 ticker)). Usually the Navigator created by WidgetsApp provides the overlay. Perhaps your app content was created above the Navigator with the WidgetsApp builder parameter. 'package:flutter/src/widgets/text_selection.dart': Failed assertion: line 610 pos 7: 'overlay != null'

现有代码

main.dart

void main() async {
  await ScreenUtil.ensureScreenSize();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      builder: (ctx, child) {
        ScreenUtil.init(ctx);
        return Theme(
            data: ThemeData(
              primarySwatch: Colors.blue,
            ),
            child: const HomeScreen());
      },
    );
  }
}

HomeScreen.dart

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  Widget build(BuildContext context) {
    ScreenUtil.init(context, designSize: const Size(360, 739));
    TextEditingController searchController = TextEditingController();

    return SafeArea(
      child: Scaffold(
        body: Padding(
          padding: EdgeInsets.symmetric(
            horizontal: kPaddingHorizontal,

          ),
          child: Column(
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Text(
                        "Hello, Welcome",
                        style: kEncodeSansRegular.copyWith(color: kDarkBrown),
                      ),
                      SizedBox(height: 5.h),
                      Text(
                        "Hello, Welcome",
                        style: kEncodeSansBold.copyWith(color: kDarkBrown),
                      ),
                    ],
                  ),
                  CircleAvatar(
                    radius: 23.r,
                    backgroundImage: const AssetImage("assets/images/image5.png"),
                  ),
                ],
              ),
              SizedBox(height: 20.h),
              Row(children: [
                Expanded(
                  child: TextField(
                    style: kEncodeSansRegular.copyWith(color: kDarkGrey),
                    controller: searchController,
                    keyboardType: TextInputType.text,
                    autocorrect: false,
                    decoration: InputDecoration(
                      isDense: true,
                      contentPadding: EdgeInsets.symmetric(horizontal: 10.w),
                      prefixIcon: const Icon(
                        Icons.search,
                        color: kGrey,
                      ),
                      hintText: "Search Clothes...",
                      border: kInputBorder,
                      errorBorder: kInputBorder,
                      disabledBorder: kInputBorder,
                      focusedBorder: kInputBorder,
                      focusedErrorBorder: kInputBorder,
                      enabledBorder: kInputBorder,
                      hintStyle: kEncodeSansRegular.copyWith(color: kDarkGrey),
                    ),
                  ),
                ],

问题根源及解决方案

问题本质

报错核心是Overlay缺失:MaterialApp的builder参数中直接返回了HomeScreen,跳过了Navigator及其提供的Overlay。TextField依赖Overlay实现光标、文本选择菜单等功能,无Overlay则触发断言错误。此外,在HomeScreen的build方法中重复调用ScreenUtil.init属于冗余操作,会导致重复初始化。

正确配置方案

  1. 修复MaterialApp的builder逻辑:
    保留builder参数中的child(该child包含Navigator和Overlay),不要直接替换为HomeScreen,将ScreenUtil初始化逻辑包裹在child外层。

  2. 统一初始化位置:
    仅在MaterialApp的builder中初始化一次ScreenUtil,无需在页面build方法中重复调用。

修改后代码

main.dart

void main() async {
  await ScreenUtil.ensureScreenSize();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const HomeScreen(), // 将HomeScreen交由Navigator管理
      builder: (ctx, child) {
        // 使用ScreenUtilInit组件完成初始化,保留原child层级
        return ScreenUtilInit(
          designSize: const Size(360, 739), // 统一设置设计稿尺寸
          builder: (context, widget) => Theme(
            data: ThemeData(
              primarySwatch: Colors.blue,
            ),
            child: child!, // 返回原child,确保Navigator和Overlay存在
          ),
        );
      },
    );
  }
}

HomeScreen.dart

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  // 将Controller定义为成员变量,避免每次build重建
  final TextEditingController searchController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    // 移除此处的ScreenUtil.init调用
    return SafeArea(
      child: Scaffold(
        body: Padding(
          padding: EdgeInsets.symmetric(
            horizontal: kPaddingHorizontal,
          ),
          child: Column(
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Text(
                        "Hello, Welcome",
                        style: kEncodeSansRegular.copyWith(color: kDarkBrown),
                      ),
                      SizedBox(height: 5.h),
                      Text(
                        "Hello, Welcome",
                        style: kEncodeSansBold.copyWith(color: kDarkBrown),
                      ),
                    ],
                  ),
                  CircleAvatar(
                    radius: 23.r,
                    backgroundImage: const AssetImage("assets/images/image5.png"),
                  ),
                ],
              ),
              SizedBox(height: 20.h),
              Row(children: [
                Expanded(
                  child: TextField(
                    style: kEncodeSansRegular.copyWith(color: kDarkGrey),
                    controller: searchController,
                    keyboardType: TextInputType.text,
                    autocorrect: false,
                    decoration: InputDecoration(
                      isDense: true,
                      contentPadding: EdgeInsets.symmetric(horizontal: 10.w),
                      prefixIcon: const Icon(
                        Icons.search,
                        color: kGrey,
                      ),
                      hintText: "Search Clothes...",
                      border: kInputBorder,
                      errorBorder: kInputBorder,
                      disabledBorder: kInputBorder,
                      focusedBorder: kInputBorder,
                      focusedErrorBorder: kInputBorder,
                      enabledBorder: kInputBorder,
                      hintStyle: kEncodeSansRegular.copyWith(color: kDarkGrey),
                    ),
                  ),
                ),
              ]),
            ],
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    // 释放Controller资源
    searchController.dispose();
    super.dispose();
  }
}

额外注意事项

  • 不要在build方法中创建TextEditingController,应在成员变量或initState中定义,并在dispose方法中释放资源,避免不必要的重建。
  • 优先使用ScreenUtilInit组件(flutter_screenutil官方推荐方式)完成初始化,替代直接调用ScreenUtil.init,确保初始化流程安全。
  • 所有依赖Overlay的组件(如TextField、DropdownButton等)必须处于Navigator层级之下,即MaterialApp的home或routes配置的页面中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 08:40:28