Why Flutter GlobalKey's currentState is NULL when accessed from other file

9,262

That is due to how dart import works.

In dart, there is two way to import sources :

  • import './relative/path.dart'
  • import 'myApp/absolute/path.dart'

The thing is, they are not compatible with each others. Both these imports will have a different runtimeType.

But how is that a problem ? I never used relative import

That's a problem, because in some situations you implicitly use "relative imports" : When using a class A defined in foo.dart inside foo.dart.

So, how do I solve the problem ?

There are multiple solutions :

  • Have everything related to your class App should be inside the same file. (That's the recommended thing in dart)
  • Extract App into it's own file. And import it everywhere using absolute imports.
  • Don't use GlobalKey to begin with. As your use case is definitely in the scope of InheritedWidget.
Share:
9,262
Edmund Tam
Author by

Edmund Tam

Updated on December 04, 2022

Comments

  • Edmund Tam
    Edmund Tam over 1 year

    Here's my code:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(new MyStatefulApp(key: App.appStateKey));
    }
    
    /// Part [A]. No difference when appStateKey is defined as variable.
    class App {
      static final GlobalKey<MyAppState> appStateKey = new GlobalKey<MyAppState>();
    }
    
    /// Part [B] 
    class MyStatefulApp extends StatefulWidget {
      MyStatefulApp({Key key}) :super(key: key);
    
      @override
      MyAppState createState() => new MyAppState();
    }
    
    class MyAppState extends State<MyStatefulApp> {
    
      int _counter = 0;
    
      add() {
        setState(() {
          _counter++;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: "App",
          theme: new ThemeData(
            primarySwatch: _counter % 2 == 0 ? Colors.blue : Colors.red,
          ),
          home: new MyHomePage(),
        );
      }
    }
    
    /// Part [C] 
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key}) : super(key: key);
    
      @override
      _MyHomePageState createState() => new _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
    
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          appBar: new AppBar(title: new Text("Main"),      ),
          body: new FlutterLogo(),
          floatingActionButton: new FloatingActionButton(
            onPressed: () {
              App.appStateKey.currentState.add(); // (X)
            },
            tooltip: "Trigger color change",
            child: new Icon(Icons.add),
          ),
        );
      }
    }
    

    In the code above, when the FAB is clicked, MaterialApp should rebuild, and the primary color will switch between blue and red.

    In fact, the code worked, until I attempted to split the portions of the code to different files. App.appStateKey.currentState on line (X) will be become null when:

    • Part A (The App class, or the variable) is moved to another file;
    • Part C (MyHomePage and _MyHomePageState) is moved to another file;
    • Part A and C are moved to another file

    So it looks like the GlobalKey.currentState only work when everything involving this GlobalKey is in the same file.

    The doc only states that currentState will be null when (1) there is no widget in the tree that matches this global key, (2) that widget is not a StatefulWidget, or the associated State object is not a subtype of T. It doesn't state that everything has to be in the same file.

    Breaking classes into files may not be "the Dart way", but I assume it should work anyhow (they're all public). So this puzzles me, and I suspect if I have stumbled upon certain Flutter feature that I am not aware of. Thanks.

  • Edmund Tam
    Edmund Tam about 6 years
    Thanks for pointing me to this direction. But my actual problem is due to the use of the "package:" directive to import local package files (or to be exact, the Flutter plugin in Android Studio did this for me). I have to use relative import, so that Dart won't see it as another package. I got the answer after asking in the Flutter discussion forum. And yes, using GlobalKey isn't a good idea for this particular case. I ended up passing a method callback to the child widget.
  • Rémi Rousselet
    Rémi Rousselet about 6 years
    That's exactly what I said here :)
  • Rémi Rousselet
    Rémi Rousselet about 6 years
    I made a better explanation of this "package" problem here
  • Rémi Rousselet
    Rémi Rousselet about 6 years
    And discussed a lot about it on flutter gittter