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

Flutter复制Login页创建Register页时触发_dependents.isEmpty断言错误

问题描述

我使用GetX包开发Flutter登录页面,通过Form组件创建TextFormField并为其分配了GlobalKey<FormState>(var formKey = GlobalKey<FormState>(););复制登录页面代码创建注册页面后,出现如下错误:

AssertionError ('package:flutter/src/widgets/framework.dart': Failed assertion: line 5403 pos 14: '_dependents.isEmpty': is not true.)

登录页面代码

import 'package:book_store/user/sign_up.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get/get_navigation/src/root/get_material_app.dart';
import 'package:get/get_state_manager/get_state_manager.dart';

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

  @override
  State<Login> createState() => _LoginState();
}

class _LoginState extends State<Login> {
  var formKey = GlobalKey<FormState>();
  var emailController = TextEditingController();
  var passwordController = TextEditingController();
  var isObsecure = true.obs;
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: "my app",
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: FutureBuilder(
        builder: (context, dataSnapShot) {
          return Scaffold(
            backgroundColor: Colors.black,
            body: LayoutBuilder(builder: (context, cons) {
              return ConstrainedBox(
                constraints: BoxConstraints(
                  minHeight: cons.maxHeight,
                ),
                child: SingleChildScrollView(
                    child: Column(
                  children: [
                    SizedBox(
                      width: MediaQuery.of(context).size.width,
                      height: 285,
                      child: Image.asset("lib/assets/book_books_bookshelf.jpg"),
                    ),
                    const SizedBox(
                      height: 20,
                    ),
                    Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Container(
                        decoration: const BoxDecoration(
                            color: Colors.white24,
                            borderRadius: BorderRadius.all(Radius.circular(60)),
                            boxShadow: [
                              BoxShadow(
                                blurRadius: 8,
                                color: Colors.black26,
                                offset: Offset(0, -3),
                              ),
                            ]),
                        child: Padding(
                          padding: const EdgeInsets.fromLTRB(30, 20, 30, 20),
                          child: Column(
                            children: [
                              const SizedBox(height: 20),
                              // for email
                              Form(
                                key: formKey,
                                child: Column(
                                  children: [
                                    TextFormField(
                                      controller: emailController,
                                      // if user put form empty
                                      validator: (val) => val == ""
                                          ? "please write email"
                                          : null,
                                      decoration: InputDecoration(
                                        prefixIcon: const Icon(
                                          Icons.email,
                                          color: Colors.amber,
                                        ),
                                        hintText: "abc@gmail.com",
                                        border: OutlineInputBorder(
                                            borderRadius:
                                                BorderRadius.circular(30),
                                            borderSide: const BorderSide(
                                              color: Colors.white70,
                                            )),
                                        enabledBorder: OutlineInputBorder(
                                            borderRadius:
                                                BorderRadius.circular(30),
                                            borderSide: const BorderSide(
                                              color: Colors.white70,
                                            )),
                                        disabledBorder: OutlineInputBorder(
                                          borderRadius:
                                              BorderRadius.circular(30),
                                          borderSide: const BorderSide(
                                            color: Colors.white70,
                                          ),
                                        ),
                                        focusedBorder: OutlineInputBorder(
                                            borderRadius:
                                                BorderRadius.circular(30),
                                            borderSide: const BorderSide(
                                              color: Colors.white70,
                                            )),
                                        contentPadding:
                                            const EdgeInsets.symmetric(
                                          horizontal: 3.0,
                                          vertical: 8.0,
                                        ),
                                        fillColor: Colors.white38,
                                        filled: true,
                                      ),
                                    ),
                                    SizedBox(
                                      height: 30,
                                    ),
                                    //----------------password---------------------------------------------------
                                    Obx(() => TextFormField(
                                          controller: passwordController,
                                          obscureText: isObsecure.value,
                                          // if user put form empty
                                          validator: (val) => val == ""
                                              ? "please write password"
                                              : null,
                                          decoration: InputDecoration(
                                            prefixIcon: const Icon(
                                              Icons.key,
                                              color: Colors.amber,
                                            ),
                                            suffixIcon:
                                                Obx(() => GestureDetector(
                                                      onTap: () => {
                                                        isObsecure.value =
                                                            !isObsecure.value,
                                                      },
                                                      child: Icon(
                                                        isObsecure.value
                                                            ? Icons
                                                                .visibility_off
                                                            : Icons.visibility,
                                                      ),
                                                    )),
                                            hintText: "password",
                                            border: OutlineInputBorder(
                                                borderRadius:
                                                    BorderRadius.circular(30),
                                                borderSide: const BorderSide(
                                                  color: Colors.white70,
                                                )),
                                            enabledBorder: OutlineInputBorder(
                                                borderRadius:
                                                    BorderRadius.circular(30),
                                                borderSide: const BorderSide(
                                                  color: Colors.white70,
                                                )),
                                            disabledBorder: OutlineInputBorder(
                                              borderRadius:
                                                  BorderRadius.circular(30),
                                              borderSide: const BorderSide(
                                                color: Colors.white70,
                                              ),
                                            ),
                                            focusedBorder: OutlineInputBorder(
                                                borderRadius:
                                                    BorderRadius.circular(30),
                                                borderSide: const BorderSide(
                                                  color: Colors.white70,
                                                )),
                                            contentPadding:
                                                const EdgeInsets.symmetric(
                                              horizontal: 3.0,
                                              vertical: 8.0,
                                            ),
                                            fillColor: Colors.white38,
                                            filled: true,
                                          ),
                                        )),
                                    SizedBox(
                                      height: 30,
                                    ),
                                    //---------------material-------------------------------------------
                                    Material(
                                      color: Colors.black,
                                      borderRadius: BorderRadius.circular(38),
                                      child: InkWell(
                                        onTap: () {},
                                        borderRadius: BorderRadius.circular(38),
                                        child: const Padding(
                                          padding: EdgeInsets.symmetric(
                                            vertical: 10,
                                            horizontal: 10,
                                          ),
                                          child: Text(
                                            "login",
                                            style: TextStyle(
                                              color: Colors.white,
                                              fontSize: 16,
                                            ),
                                          ),
                                        ),
                                      ),
                                    ),
                                  ],
                                ),
                              ),

                              const SizedBox(
                                height: 10,
                              ),
                              Row(
                                mainAxisAlignment: MainAxisAlignment.center,
                                children: [
                                  const Text("Don't have an Account?"),
                                  TextButton(
                                      onPressed: () {
                                        Get.to(SignUp());
                                      },
                                      child: const Text("Register here"))
                                ],
                              ),
                              const Text(
                                "Or",
                                style: TextStyle(
                                  fontSize: 16,
                                  color: Colors.grey,
                                ),
                              ),
                              //----------------admin text ---------------------------
                              Row(
                                mainAxisAlignment: MainAxisAlignment.center,
                                children: [
                                  const Text("Are you an admin?"),
                                  TextButton(
                                      onPressed: () {},
                                      child: const Text("click here"))
                                ],
                              ),
                              // SizedBox(height: 10,)
                            ],
                          ),
                        ),
                      ),
                    )
                  ],
                )),
              );
            }),
          );
        },
      ),
    );
  }
}

main.dart代码

import 'package:book_store/user/login_screen.dart';
import 'package:flutter/material.dart';
import 'package:get/get_navigation/src/root/get_material_app.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    // using get dependency so that state can be easily managed
    return MaterialApp(
      title: 'book store app',
      theme: ThemeData(
        primarySwatch:  Colors.blue,
      ),
      home: FutureBuilder(
        builder: (context, dataSnapShot){
          return Login();
        },
      ),
    );
  }
}
问题原因与修复方案

核心问题

  1. 重复嵌套根App组件:Login页面返回了GetMaterialApp,但main.dart中已经使用了MaterialApp,导致Widget树存在多个根App上下文,引发GlobalKey依赖管理冲突。
  2. GlobalKey复用风险:复制代码创建注册页时,若未重新定义独立的GlobalKey<FormState>,会导致同一个Key绑定到多个Form组件,触发断言错误。

修复步骤

1. 移除Login页面中的GetMaterialApp

Login页面应作为普通页面组件,直接返回Scaffold:

@override
Widget build(BuildContext context) {
  return Scaffold(
    backgroundColor: Colors.black,
    body: LayoutBuilder(builder: (context, cons) {
      // 原有body内容保持不变
    }),
  );
}

2. 将main.dart的MaterialApp替换为GetMaterialApp

使用GetX路由必须在根组件使用GetMaterialApp:

@override
Widget build(BuildContext context) {
  return GetMaterialApp(
    title: 'book store app',
    theme: ThemeData(
      primarySwatch: Colors.blue,
    ),
    home: FutureBuilder(
      builder: (context, dataSnapShot){
        return Login();
      },
    ),
  );
}

3. 给注册页分配独立的GlobalKey

在注册页的State类中重新定义专属的formKey,禁止复用登录页的Key:

class _SignUpState extends State<SignUp> {
  var formKey = GlobalKey<FormState>();
  var emailController = TextEditingController();
  var passwordController = TextEditingController();
  var confirmPasswordController = TextEditingController();
  // 其他变量也需独立定义
}

4. 简化多余的Obx嵌套

登录页密码输入框的suffixIcon嵌套了两层Obx,可简化为一层,减少不必要的响应式重建:

Obx(() => TextFormField(
  controller: passwordController,
  obscureText: isObsecure.value,
  validator: (val) => val == "" ? "please write password" : null,
  decoration: InputDecoration(
    prefixIcon: const Icon(Icons.key, color: Colors.amber),
    suffixIcon: GestureDetector(
      onTap: () => isObsecure.value = !isObsecure.value,
      child: Icon(
        isObsecure.value ? Icons.visibility_off : Icons.visibility,
      ),
    ),
    // 其余装饰内容保持不变
  ),
)),

错误原因说明

当同一个GlobalKey被多个Widget绑定,或Widget树存在多个根App上下文导致依赖关系混乱时,Flutter框架会检测到未清理的依赖项,触发_dependents.isEmpty断言,确保GlobalKey的绑定关系唯一且可被正确管理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 13:08:09