Flutter GlobalKey current state is null

What I'm basically trying to do (And I did, details up ahead) is create a screen with several forms the content of which is changed using a button (NEXT). So I have 3 forms, when I press next, I save the first form and pass to the other, what I did so far works when I put my class in main.dart. For example, here is my main.dart content.

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Entries'),
    );
  }
}
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  static final _scafoldKey = new GlobalKey<ScaffoldState>();
  final firstForm = new FirstForm(title: "First Form");
  final secondForm = new SecondForm(title: "First Form",);
  final thirdForm = new ThirdForm(title: "First Form");
  final Map<int,Widget> forms = {};
  final Map<String,Map> allValues = new Map();

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  Widget currentForm;
  final Firestore _fireStore = Firestore.instance;
  Map<String,Map> thirdFormNested = new Map();
  int thirdFormPos = 1;

  @override
  void initState() {
    widget.forms[0] = widget.firstForm;
    widget.forms[1] = widget.secondForm;
    widget.forms[2] = widget.thirdForm;
    currentForm = widget.forms[0];
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: MyHomePage._scafoldKey,
      appBar: AppBar(
        title: Text(widget.title),
        leading: IconButton(
          icon: Icon(Icons.arrow_back),
          onPressed: () => Navigator.of(context).pop(),
        ),
      ),
      body: SingleChildScrollView(
        child: currentForm,
      ),
      floatingActionButton: Stack(
        children: <Widget>[
          Align(
            alignment: Alignment(0.4, 1),
            child: SizedBox(
              width: 75,
              child: FloatingActionButton(
                shape: RoundedRectangleBorder(),
                child: Text("NEXT"),
                onPressed: () {
                  GlobalKey<FormState> key;
                  if(currentForm is FirstForm) {
                    key= widget.firstForm.formKey;
                    if(key.currentState.validate()){
                      key.currentState.save();
                      widget.allValues["First Form"] = widget.firstForm.values;
                      setState(() {
                        currentForm = widget.forms[1];
                      });
                    }
                  }
                  else if(currentForm is SecondForm) {
                    setState(() {
                      currentForm = widget.forms[2];
                    });
                  }
                  else if (currentForm is ThirdForm) {
                    key = widget.thirdForm.formKey;
                    if(key.currentState.validate()){
                      key.currentState.save();
                      var tmp = widget.thirdForm.values;
                      widget.thirdForm.values = <String,String>{};
                      thirdFormNested.addAll({"Bit $thirdFormPos":tmp});
                      key.currentState.reset();
                      widget.thirdForm.build(context);
                      thirdFormPos++;
                    }
                  }
                },
              ),
            ),
          ),
          Align(
            alignment: Alignment(1, 1),
            child: SizedBox(
              width: 75,
              child: FloatingActionButton(
                shape: RoundedRectangleBorder(),
                child: Text("SUBMIT"),
                onPressed: () async {
                  GlobalKey<FormState> key;
                  if(currentForm is FirstForm) {
                    key= widget.firstForm.formKey;
                    if(key.currentState.validate()) {
                      key.currentState.save();
                      await _fireStore.collection('form').document(Uuid().v1()).setData(widget.firstForm.values);
                    }
                  }else if(currentForm is SecondForm) {
                    await _fireStore.collection('form').document(Uuid().v1()).setData(widget.firstForm.values);
                  }else if(currentForm is ThirdForm) {
                    key= widget.thirdForm.formKey;
                    if(key.currentState.validate()){
                      key.currentState.save();
                      if(thirdFormNested.length == 0) {
                        var tmp = widget.thirdForm.values;
                        widget.thirdForm.values = <String,String>{};
                        thirdFormNested.addAll({"Bit 1":tmp});
                      }else {
                        var tmp = widget.thirdForm.values;
                        widget.thirdForm.values = <String,String>{};
                        thirdFormNested.addAll({"Bit $thirdFormPos":tmp});
                      }
                      widget.allValues["Third Form"] = thirdFormNested;
                      await _fireStore.collection('form').document(Uuid().v1()).setData(widget.allValues);
                    }
                  }
                },
              ),
            ),
          ),
        ],
      )
    );
  }
}

And this is the first form (other forms are the same, the inputs are the only difference):

class FirstForm extends StatelessWidget {
  FirstForm({Key key, this.title}) : super(key: key);
  final String title;
  final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
  Map<String,String> _values = new Map();

  get formKey => _formKey;
  get values => _values;
  set values(v) => _values = v;

  @override
  Widget build(BuildContext context) {
    print("FORM KEY: ${_formKey.currentState}");
    _values['Spud Date'] = _formatDate(DateTime.now().toString());
    _values['TD Date'] = _formatDate(DateTime.now().toString());
    return Form(
      key: _formKey,
      child: Column(
        children: <Widget>[
          ListTile(
            leading: Text(
              "API#",
              style: TextStyle(
                fontSize: 18,
              ),
            ),
            title: TextFormField(
              decoration: InputDecoration(
                hintText: "<api>"
              ),
              validator: (value) {
                if(value.isEmpty) {
                  return "Please enter API value!";
                }
                return null;
              },
              onSaved: (value) {
                _values['API#'] = value;
              },
            ),
          ..... Other inputs
          Padding(
            padding: EdgeInsets.symmetric(vertical: 36.0),
          )
        ],
      ),
    );
  }

  String _formatDate(String date) {
    DateTime d = DateTime.parse(date);
    return DateFormat("dd-MM-yyyy").format(d);
  }
}

So each form has a formkey that I pass to the Form(key: ..). I instantiate each form in the MyHomePage class and I use widget.firstForm.formKey to retrieve the form key to use it for validation.

So far this has been working perfectly. However, when I tried to fit my work into an existing app. It doesn't anymore.

In the existing app, I have a drawer. An item of the drawer is called Forms, which takes me to the "MyHomePage" that I renamed to Forms now. So the code is still the same, the only thing I removed is the runApp() and MyApp class which are in a different file now. This is the drawer item code:

ListTile(
                title: new Text('Forms'),
                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      maintainState: true,
                      builder: (context) => Forms(title: "Forms")
                    ),
                  );
                },
              ),

Now, using this appraoch. When clicking on Next in the Forms screen, I get the error:

validate() called on null

So after a few hours, I figured out that it's actually the GlobalKey of my forms that returns a null state. The question is why? And how can I solve this.

TL;DR: I have 3 forms with their respective GlobalKeys. The keys are used to validate the forms in another widget, which wraps these forms. The way I approached their creation works when I have the wrapper widget in the default MyHomePage class in main.dart. However, when moving the same wrapper class to an external file and renaming the class to a more appropriate name, the GlobalKeys states are now null.

EDIT: One extra awkward behavior I noticed, is that when I fill the form (FirstForm) and press Next for the first time when I run the app, I get the null error. However, on the first run of the app, if I press Next without filing the form, the validator works and it shows me the errors for filling the inputs.

Solution to answer:

You need to pass the key into the scaffold widget, then the context and state will be set.

class _MyHomePageState extends State<MyHomePage> {
  GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey(); //so we can call snackbar from anywhere

...

Widget build(BuildContext context) {
    return Scaffold(
        key: _scaffoldKey, //set state and context

...

//define method to call
void showInSnackBar(String value) {
    _scaffoldKey.currentState.showSnackBar(new SnackBar(
        content: new Text(value)
    ));
  }

...

//Then you can call it
showInSnackBar("Proximity Alert, please distance yourself from the person or dangerous object close to you! This incident has been logged.");