Search code examples
flutterdartflutter-getx

A GlobalKey was used multiple times in my flutter project


I have problem in my project.
I use Flutter and GetX to create routes.
For now it's a simple project but I have error when I refresh this code in my VS.

I check my project on Chrome browser and I have this info in my debugger:

This app is linked to the debug service: ws://127.0.0.1:49847/Kvc6Hh7Hagk=/ws
Debug service listening on ws://127.0.0.1:49847/Kvc6Hh7Hagk=/ws
Connecting to VM Service at ws://127.0.0.1:49847/Kvc6Hh7Hagk=/ws
***** Supabase init completed Instance of 'Supabase'
***** SupabaseDeepLinkingMixin startAuthObserver
**** onAuthStateChange: AuthChangeEvent.initialSession
[GETX] Instance "GetMaterialController" has been created
[GETX] Instance "GetMaterialController" has been initialized
[GETX] GOING TO ROUTE /splashpage
[GETX] GOING TO ROUTE /LoginScreen
[GETX] REMOVING ROUTE /splashpage

My project work like this - first user go to splashScreen - if he logged then he goes to HomePage() when not he will be redirect to /LoginScreen().

For now I dont know why /spashpage Is twice but there is no problem with project.

No errors - everything is ok. Then when I upload some new elements of code (for example I add simple change) and I save this project then I have hot reload in my VS and I have errors :
1)

 ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following TypeErrorImpl was thrown building _FocusInheritedScope:
Unexpected null value.

The relevant error-causing widget was:
  Directionality
  Directionality:file://.pub-cache/hosted/pub.dev/get-4.6.6/lib/get_navigation/src/root/get_material_app.dart:328:12

and second error in my main.dart file?

Another exception was thrown: A GlobalKey was used multiple times inside one widget's child list.
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following assertion was thrown building _FocusInheritedScope:
A GlobalKey was used multiple times inside one widget's child list.
The offending GlobalKey was: [LabeledGlobalKey<NavigatorState>#c60d4 Key Created by default]
The parent of the widgets with that key was:
  _FocusInheritedScope
The first child to get instantiated with that key became:
  Navigator-[LabeledGlobalKey<NavigatorState>#c60d4 Key Created by default]
The second child that was to be instantiated with that key was:
  _FocusInheritedScope
A GlobalKey can only be specified on one widget at a time in the widget tree.

Maybe code will help

main.dart

import 'package:get/get.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Supabase.initialize(
    url: 'url key',
    anonKey:
        'my anon key',
    debug: true,
  );
  runApp(MyApp());
}

final supabase = Supabase.instance.client;

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      initialRoute: '/splashpage',
      getPages: [
        GetPage(name: '/splashpage', page: () => SplashPage()),
        GetPage(name: '/loginpage', page: () => LoginScreen()),
        GetPage(name: '/homepage', page: () => HomePage()),
      ],
      title: 'Flutter Admin Panel2',
      theme: lightMode,
      darkTheme: darkMode,
    );
  }
}

login_page.dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';

class LoginScreen extends StatelessWidget {
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  double displayHeight(BuildContext context) {
    return displaySize(context).height;
  }

  double displayWidth(BuildContext context) {
    return displaySize(context).width;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        children: [
          Container(
            padding: EdgeInsets.all(24),
            width: 400,
            height: double.maxFinite,
            decoration: BoxDecoration(
              border: Border(
                right: BorderSide(
                  color: Colors.blue.shade300,
                  width: 1,
                ),
              ),
              color: Colors.white,
              boxShadow: [
                BoxShadow(
                  color: Colors.grey.withOpacity(0.5),
                  spreadRadius: 5,
                  blurRadius: 7,
                  offset: Offset(0, 3),
                ),
              ],
            ),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Image.asset('assets/images/$logo', width: 200, height: 200),
                SizedBox(height: 50),
                TextField(
                  key: Key('email-field'),
                  controller: _emailController,
                  decoration: textFieldDecoration.copyWith(labelText: 'Email'),
                ),
                SizedBox(height: 20),
                TextField(
                  key: Key('password-field'),
                  controller: _passwordController,
                  obscureText: true,
                  decoration:
                      textFieldDecoration.copyWith(labelText: 'Password'),
                ),
                SizedBox(height: 20),
                Container(
                  width: 200,
                  height: 50,
                  child: ElevatedButton(
                    style: buttonStyle1,
                    onPressed: () async {
                      try {
                        final email = _emailController.text.trim();
                        final password = _passwordController.text.trim();

                        if (email.isEmpty || password.isEmpty) {
                          ScaffoldMessenger.of(context).showSnackBar(
                            const SnackBar(
                              content:
                                  Text('Email and password cannot be empty.'),
                              backgroundColor: Colors.red,
                            ),
                          );
                          return;
                        }

                        final response = await supabase.auth.signInWithPassword(
                            email: email, password: password);

                        if (response.user != null) {
                          ScaffoldMessenger.of(context).showSnackBar(
                            const SnackBar(
                              content: Text('Login Success'),
                              backgroundColor: Colors.green,
                            ),
                          );
                          Get.to(() => HomePage());
                        } else {
                          ScaffoldMessenger.of(context).showSnackBar(
                            const SnackBar(
                              content: Text('Login failed, please try again.1'),
                              backgroundColor: Colors.red,
                            ),
                          );
                        }
                      } catch (error) {
                        ScaffoldMessenger.of(context).showSnackBar(
                          SnackBar(
                            content:
                                Text('An error occurred: ${error.toString()}'),
                            backgroundColor: Colors.red,
                          ),
                        );
                      }
                    },
                    child: Text('Login'),
                  ),
                ),
              ],
            ),
          ),
          Container(
            width: displayWidth(context) - 400,
            height: double.maxFinite,
            decoration: BoxDecoration(
              image: DecorationImage(
                image: AssetImage('assets/images/background_image.jpg'),
                fit: BoxFit.cover,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

and splash_page.dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

class SplashPage extends StatefulWidget {
  const SplashPage({super.key});

  @override
  State<SplashPage> createState() => _SplashPageState();
}

class _SplashPageState extends State<SplashPage> {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _redirect();
    });
  }

  Future<void> _redirect() async {
    // Zasymulowany opóźniony czas ładowania, aby zobaczyć CircularProgressIndicator
    await Future.delayed(const Duration(seconds: 2));
    final session = Supabase.instance.client.auth.currentSession;
    if (!mounted) return;
    if (session != null) {
      Get.offAll(() => HomePage());
    } else {
      Get.offAll(() => LoginScreen());
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: const <Widget>[
            CircularProgressIndicator(),
            SizedBox(height: 20),
            Text('Loading...'),
          ],
        ),
      ),
    );
  }
}

so as you can see code is not big but there is a problem in the beginning.
Important: When I stop project and run this project again problem not occur.
But when I do some small change in code and hot reload my project then the problem occur (exceptions).

Do you have some idea what is wrong ?

Thank you for your time and help


Solution

    1. Try stop and rerun you application instead of hot reloading.
    2. In case you are getting the error when the app just started: Add '/' route to the list of routes and restart.
    3. Try to make your GlobalKey "final". I think the fact your global key is mutable might be creating this problem.
    4. You are using Getx with flutter and if you will try to access the route which is not defined in your "AppPages", then you might get this error :

    Error : A global key was used multiple times inside one widgets child

    Solution to this is you need to define "unknownRoute" inside your GetMaterialApp like this :

    unknownRoute: GetPage(
              name: "/splash",
              page: () => SplashView(),
              binding: SplashBinding(),
            ),
    

    you can define your own views and route name. Hope it helps, Happy Coding!