0

I have a StatefulWidget class 'FirstClass' which extends a State '_FirstClassState'. From a separate State class, called '_SecondClassState', I'm trying to access the value of a variable (called 'counter' in '_FirstClassState').

I've found a solution, but I'm not sure if this is the best solution to the problem. I've tried looking at other similar questions:

  • How to access Stateful widget variable inside State class outside the build method? (Declaring the variable in the StatefulWidget class and using 'widget.' in the State class does not allow me to get the value of 'counter' from '_SecondClassState')
  • I've also seen other sites that recommend using a GlobalKey. However, even though this might be a solution, I didn't explore further into this as I found other sites which recommend minimising the use of GlobalKeys

second_class.dart:

import 'package:brew_crew/question/first_class.dart';
import 'package:flutter/material.dart';

class SecondClass extends StatefulWidget {
  @override
  _SecondClassState createState() => _SecondClassState();
}

class _SecondClassState extends State<SecondClass> {
  final FirstClass firstClass = FirstClass();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("MyApp"),
        centerTitle: true,
      ),
      body: Column(
        children: <Widget>[
          firstClass, //Displays the button to increase 'counter'
          FlatButton(
              child: Text("Submit"),
              onPressed: () {
                print(firstClass
                    .getCounter()); //I need to get the value of 'counter' here to use elsewhere
              }),
        ],
      ),
    );
  }
}

first_class.dart

import 'package:flutter/material.dart';

class FirstClass extends StatefulWidget {
  final _FirstClassState firstClassState = _FirstClassState();

  @override
  _FirstClassState createState() => firstClassState;

  int getCounter() {
    return firstClassState.counter;
  }
}

class _FirstClassState extends State<FirstClass> {
  int counter = 0;

  //Increases counter by 1
  void _increaseCounter() {
    setState(() {
      counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    //A button which increases counter when clicked
    return FlatButton.icon(
      icon: Icon(Icons.add),
      label: Text("Counter: $counter"),
      onPressed: () {
        _increaseCounter();
      },
    );
  }
}

As you can see, within 'FirstClass', I initialise a new instance of '_FirstClassState'. Using this in the 'getCounter()' function, I can get the 'counter' value.

Is there a better way (e.g. best practice, fewer lines of code, an easier to understand method, etc.) than this to achieve what I'm trying to do?

  • How are your FirstClass and SecondClass structured? Does SecondClass lies below or above the FirstClass in the widget tree or are they siblings? – Ashutosh Singh Nov 07 '20 at 14:26
  • @AshutoshSingh Sorry, I'm new to Flutter, so apologies if this doesn't answer it. I load up the 'SecondClass' widget first and then the 'FirstClass' widget is loaded within the 'SecondClass' widget. So I think 'SecondClass' would be the parent of 'FirstClass' – Sidharth Ranjith Nov 07 '20 at 15:45

1 Answers1

1

The use of GlobalKey is definitely the recommended approach if absolutely you have to access the state of a widget from outside. However, in this case, you shouldn't use either approach.

_SecondClassState should contain the counter, and you should pass it, along with the increaseCounter function, as parameters to FirstClass. If you want to increase the number, just call that function.

Something along these lines:

class SecondClass extends StatefulWidget {
  @override
  _SecondClassState createState() => _SecondClassState();
}

class _SecondClassState extends State<SecondClass> {
  int counter = 0;

  //Increases counter by 1
  void _increaseCounter() {
    setState(() {
      counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("MyApp"),
        centerTitle: true,
      ),
      body: Column(
        children: <Widget>[
          FirstClass(counter: counter, increaseCounter: _increaseCounter), //Displays the button to increase 'counter'
          FlatButton(
              child: Text("Submit"),
              onPressed: () {
                print(counter.toString()); //I need to get the value of 'counter' here to use elsewhere
              }),
        ],
      ),
    );
  }
}
class FirstClass extends StatefulWidget {
  final int counter;
  final Function increaseCounter;

  FirstClass({this.counter, this.increaseCounter});

  final _FirstClassState firstClassState = _FirstClassState();

  @override
  _FirstClassState createState() => firstClassState;
}

class _FirstClassState extends State<FirstClass> {
  @override
  Widget build(BuildContext context) {
    //A button which increases counter when clicked
    return FlatButton.icon(
      icon: Icon(Icons.add),
      label: Text("Counter: ${widget.counter}"),
      onPressed: () {
        widget.increaseCounter();
      },
    );
  }
}

Riwen
  • 4,734
  • 2
  • 19
  • 31
  • Thanks for the answer. I've tried implementing this but I get the following error: 'The method "call" was called on null. Receiver: null Tried calling: call()'. The function 'increaseCounter()' is passed through as 'null' in the 'FirstClass' constructor (but the value of 'counter' passes through correctly). Do you know why this might be? – Sidharth Ranjith Nov 07 '20 at 15:42
  • @SidharthRanjith Not sure. Try restarting the debugging process, the above code works on my end. – Riwen Nov 07 '20 at 15:51
  • found the error. I'd passed 'increaseCounter()' instead of 'increaseCounter' as one of the parameters to 'FirstClass'. Thanks for your help on this. Out of interest, could you let me know why this is considered to be better than the word around I'd used in the question? – Sidharth Ranjith Nov 07 '20 at 16:18
  • @SidharthRanjith Ah, understandable why that didn't work, then. Well, generally speaking, data flows "down", from parent widgets to children. If you don't have a very strong reason to access the state of a a widget externally, you shouldn't, as it's antipattern. [Here's a good read about where state should be managed](https://flutter.dev/docs/development/ui/interactive). – Riwen Nov 07 '20 at 22:51