Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.8k views
in Technique[技术] by (71.8m points)

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

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...