Reputation: 10720
This is a simple example of the problem that I have. Given the following example, how can I obtain the value of "counter" from outside the Class?
class Counter extends StatefulWidget {
@override
_CounterState createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int counter = 0;
void increaseCount() {
setState(() => this.counter++);
print("New count = $counter");
}
Widget build(context) {
return new RaisedButton(
onPressed: increaseCount,
child: new Text('Tap To Add'),
);
}
}
Upvotes: 8
Views: 7816
Reputation: 10720
This is an update of my previous answer to better illustrate what I want to achieve. I am currently using this concept in my code using a Checkbox to store its own value.
This is simply an example (proof-of-concept) to demonstrate what I want to achieve. I simply want the Widget to store its value and to give it to me when I want it (ie. to update data). The alternative is for the main program to store the value, but I don't think that is ideal.
The following appears to work. It seems top me like a lot to do to simply obtain the value of the counter. I hope that there is an easier way. Code is below:
import 'package:flutter/material.dart';
import 'counterWithState.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final String sTitle = 'Flutter Counter-With-State Demo';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: sTitle,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: sTitle),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
CounterWithState _counterWithState;
FloatingActionButton _fab;
int _iCounterOriginal = 99;
RaisedButton _raisedButton;
SizedBox _sizedBox;
final _scaffoldKey = GlobalKey<ScaffoldState>();
@override
void initState() {
super.initState();
_fab = FloatingActionButton(
onPressed: _showSnackbar,
tooltip: 'Press to show Counter',
child: Icon(Icons.info),
);
_raisedButton = RaisedButton(
child: const Text('Update'),
color: Theme.of(context).accentColor,
elevation: 4.0,
splashColor: Colors.blueGrey,
onPressed: () {
_iCounterOriginal = _counterWithState.iCounter;
_counterWithState = null;
_getCounterWithState(context);
setState(() {});
});
_sizedBox = SizedBox(height: _raisedButton.height);
}
fnCounterChanged(int iCounter) {
setState(() {});
}
_showSnackbar() {
_scaffoldKey.currentState.showSnackBar(SnackBar(
backgroundColor: Colors.blue,
content:
Text("Current value of Counter is ${_counterWithState.iCounter}")));
}
@override
Widget build(BuildContext context) {
_counterWithState = _counterWithState != null
? _counterWithState
: _getCounterWithState(context);
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text(widget.title),
centerTitle: true,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_counterWithState.getCounterWidget(),
_getUpdateButton(context),
],
),
),
floatingActionButton: _fab,
);
}
Widget _getUpdateButton(BuildContext context) {
return _counterWithState == null ||
_counterWithState.iCounter == _iCounterOriginal
? _sizedBox
: _raisedButton;
}
CounterWithState _getCounterWithState(context) {
if (_counterWithState == null)
_counterWithState = CounterWithState(
iCounter: _iCounterOriginal,
isAllowedChange: true,
fnNotifyChange: fnCounterChanged);
return _counterWithState;
}
}
import 'package:flutter/material.dart';
class CounterWithState {
int _iCounter;
final Function fnNotifyChange;
CounterWithStateInternal _counterWithStateInternal;
fnDataChanged(int iNewCounter) {
_iCounter = iNewCounter;
if (fnNotifyChange != null) fnNotifyChange(iNewCounter);
}
CounterWithStateInternal getCounterWidget() {
return _counterWithStateInternal;
}
CounterWithState(
{@required iCounter,
@required bool isAllowedChange,
this.fnNotifyChange}) {
_iCounter = iCounter;
_counterWithStateInternal = CounterWithStateInternal(
GlobalKey(), this._iCounter, isAllowedChange, fnDataChanged);
}
get iCounter => _iCounter;
}
class CounterWithStateInternal extends StatefulWidget {
final int iCounter;
final bool tfAllowChange;
final Function fnDataChanged;
CounterWithStateInternal(
Key key, this.iCounter, this.tfAllowChange, this.fnDataChanged)
: super(key: key);
@override
CounterWithStateMain createState() => CounterWithStateMain();
}
class CounterWithStateMain extends State<CounterWithStateInternal> {
int _iCounter;
int _iOriginalCounter;
@override
initState() {
super.initState();
_iCounter = widget.iCounter;
_iOriginalCounter = widget.iCounter;
}
void incrementCounter(int iValue) {
if (widget.tfAllowChange) {
setState(() => this._iCounter += iValue);
widget.fnDataChanged(_iCounter);
}
}
Widget build(context) {
return Column(children: <Widget>[
Text("Value of original counter = $_iOriginalCounter"),
_getSizedBox(),
Text("Value of counter = $_iCounter"),
_getSizedBox(),
RaisedButton(
onPressed: (() => incrementCounter(1)),
child: Text('Tap To Add'),
),
_getSizedBox(),
RaisedButton(
onPressed: (() => incrementCounter(-1)),
child: Text('Tap To Subtract'),
),
_getSizedBox(),
]);
}
SizedBox _getSizedBox() {
return SizedBox(height: 20.0);
}
}
Upvotes: 0
Reputation: 10720
This is simply an example (proof-of-concept) to demonstrate what I want to achieve. I simply want the Widget to store its value and to give it to me when I want it (ie. to update data). The alternative is for the main program to store the value, but I don't think that is ideal.
The following appears to work. It seems top me like a lot to do to simply obtain the value of the counter. I hope that there is an easier way. Code is below:
import 'package:flutter/material.dart';
import 'counterWithState.dart';
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: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
CounterWithState _counterWithState =
CounterWithState(iCounter: 0, isAllowedChange: true);
FloatingActionButton _fab;
final _scaffoldKey = GlobalKey<ScaffoldState>();
@override
void initState() {
super.initState();
_fab = FloatingActionButton(
onPressed: _showSnackbar,
tooltip: 'Press to show Counter',
child: Icon(Icons.info),
);
}
_showSnackbar() {
_scaffoldKey.currentState.showSnackBar(SnackBar(
backgroundColor: Colors.blue,
content:
Text("Current value of Counter is ${_counterWithState.iCounter}")));
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_counterWithState.getCounterWidget(),
],
),
),
floatingActionButton: _fab,
);
}
}
import 'package:flutter/material.dart';
class CounterWithState {
int _iCounter;
CounterWithStateInternal _counterWithStateInternal;
fnDataChanged(int iNewCounter) {
_iCounter = iNewCounter;
debugPrint("CounterWithState: New value = $_iCounter");
}
CounterWithStateInternal getCounterWidget() {
return _counterWithStateInternal;
}
CounterWithState({@required iCounter, @required bool isAllowedChange}) {
_iCounter = iCounter;
_counterWithStateInternal = CounterWithStateInternal(
GlobalKey(), this._iCounter, isAllowedChange, fnDataChanged);
}
get iCounter => _iCounter;
}
class CounterWithStateInternal extends StatefulWidget {
final int iCounter;
final bool tfAllowChange;
final Function fnDataChanged;
CounterWithStateInternal(
Key key, this.iCounter, this.tfAllowChange, this.fnDataChanged)
: super(key: key);
@override
CounterWithStateMain createState() => CounterWithStateMain();
}
class CounterWithStateMain extends State<CounterWithStateInternal> {
int _iCounter = 0;
@override
initState() {
super.initState();
_iCounter = widget.iCounter;
}
void increaseCount() {
if (widget.tfAllowChange) {
setState(() => this._iCounter++);
widget.fnDataChanged(_iCounter);
print("CounterWithStateMain: New count = $_iCounter");
}
}
Widget build(context) {
return Column(children: <Widget>[
Text("Value of counter = $_iCounter"),
SizedBox(
height: 20.0,
),
RaisedButton(
onPressed: increaseCount,
child: Text('Tap To Add'),
)
]);
}
}
Upvotes: 0
Reputation: 492
You could use a GlobalKey like this:
class Counter extends StatefulWidget {
@override
final globalKey = GlobalKey<_CounterState>();
@override
_CounterState createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int counter = 0;
void increaseCount() {
setState(() => counter++);
print("New count = $counter");
}
@override
Widget build(context) {
return RaisedButton(
onPressed: increaseCount,
child: Text('Tap To Add'),
);
}
}
and access the counter like this:
Counter counter = Counter();
int count = counter.globalKey.currentState.counter;
A word of caution: This is not recommended.
You're transitioning from a simple, contained state to a state which is shared between several widgets. There are several better ways to address this problem. For further information and better ways to deal with the problem visit https://flutter.dev/docs/development/data-and-backend/state-mgmt .
Upvotes: 0
Reputation: 5292
In Flutter what you normally do is to pass a callback function, in that function you can pass the value you need, e.g.
class Counter extends StatefulWidget {
// you can use a callback function
final ValueSetter<int> callback;
Counter({this.callback});
@override
_CounterState createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int counter = 0;
void increaseCount() {
setState(() => this.counter++);
print("New count = $counter");
// Here you can pass the value
widget.callback(this.counter);
}
Widget build(context) {
return new RaisedButton(
onPressed: increaseCount,
child: new Text('Tap To Add'),
);
}
}
And when calling your widget, you do as follow:
Counter(callback: (counter){
// Here you can take some actions on counter
});
This is the most simple way I know, or you can use some other patterns like bloc or something else.
Hope this help.
Upvotes: 11