Flutter: Passing a function as parameter to a Stateful Widget Class - flutter

I have a stateful widget that returns a scaffold as follows:
class TimerPage extends StatefulWidget {
const TimerPage({Key? key, required this.title}) : super(key: key);
final String title;
#override
TimerPageState createState() => TimerPageState();
}
class TimerPageState extends State<TimerPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
mainAxisAlignment: MainAxisAlignment.center,
children: [
getMaterialTextButton('1', 'Roboto', 24, keypadPressed('1')), /// ERROR
getMaterialTextButton('2', 'Roboto', 24, keypadPressed2('2')), /// ERROR
]
),
);
}
}
So what I'm trying to do is pass a generic function inside getMaterialTextButton() that will return a material button as follows:
// this function is within the TimerPageState class
Widget getMaterialTextButton(String text, String fontname, double fontsize, Function onPressAction) {
return (
MaterialButton(
onPressed: () {
onPressAction(text);
},
color: Colors.blue,
textColor: Colors.white,
child: Text(
text,
style: TextStyle(fontFamily: fontname, fontSize: fontsize)
),
padding: const EdgeInsets.all(24),
shape: const CircleBorder(),
)
);
}
// this function is to be called when the button 1 is pressed
// also resides inside TimerPageState class
void keyPressed (String text) {
print('Pressed: $text');
}
// this function is also to be called when button 2 is pressed
// also resides inside TimerPageState class
void keyPressed2 (String text) {
print('Pressed: $text');
}
But this doesn't seem to work as dart is giving me an exception:
Exception has occurred. _TypeError (type 'Null' is not a subtype of type 'Function')
. How can I do this operation properly?

Firstly you should prefer a full signature in your function type annotations https://dart.dev/guides/language/effective-dart/design#prefer-signatures-in-function-type-annotations
so
Widget getMaterialTextButton(String text, String fontname, double fontsize, Function onPressAction) {...}
should be
Widget getMaterialTextButton(String text, String fontname, double fontsize, void Function(String) onPressAction) {...}
Secondly, this is not passing in a function:
getMaterialTextButton('1', 'Roboto', 24, keypadPressed('1')),
That is calling the keypadPressed and passing in the result of the function, which appears to be void. Remove the parenthesis, and do not pass anything into keypadPressed, the body of getMaterialTextButton is where the function should be called.
getMaterialTextButton('1', 'Roboto', 24, keypadPressed),

you are passing the return value of the function, not the function itself
change this
getMaterialTextButton('1', 'Roboto', 24, keypadPressed('1')),
to this
getMaterialTextButton('1', 'Roboto', 24, keypadPressed),

Related

I can't get a function to work from another file

I have a function start timer that requires a stateful widget and I need it to be called in another Class which is also a stateful widget class, I have tried making an object of the class PhoneAuthState phone = PhoneAuthState() and taping it into the function but the timer wouldn't start counting down after pressing the send button,
Here is the first class
`class PhoneAuth extends StatefulWidget {
const PhoneAuth({Key? key}) : super(key: key);
#override
State<PhoneAuth> createState() => PhoneAuthState();
}
class PhoneAuthState extends State<PhoneAuth> {
int start = 30;
#override
Widget build(BuildContext context) {
return Scaffold(
ReusableTField(),
RichText(
text: TextSpan(children: [
TextSpan(
text: "Send OTP again in",
style: TextStyle(color: Colors.orange, fontSize: 17),
),
TextSpan(
text: " 00:$start",
style: TextStyle(color: Colors.redAccent, fontSize: 17),
),
TextSpan(
text: "sec",
style: TextStyle(color: Colors.orange, fontSize: 17),
),
]),
),
**Here is the function below**
void startTimer() {
const onsec = Duration(seconds: 1);
Timer timer = Timer.periodic(onsec, (timer) {
if (start == 0) {
setState(() {
timer.cancel();
});
} else {
setState(() {
start--;
});
}
});
`
Then this is the class(in another file) that needs the function to start counting down when the send button is tapped nothing happens on the emulator, even after the startTimer contains setState
class ReusableTField extends StatefulWidget {
#override
State<ReusableTField> createState() => _ReusableTFieldState();
}
class _ReusableTFieldState extends State<ReusableTField> {
#override
Widget build(BuildContext context) {
return Container(
suffixIcon: InkWell(
onTap: () {} // Here is where I called the function with PhoneAuthState phone =
// PhoneAuthState()... phone.startTimer() but the code does not work,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 14),
child: Text(),
),
)
if you want to use the same code on multiple classes define a static class and write this method as a static method and call it from anywhere.
I think you function is at wrong place keep it out of build method and then call.

How to pass a function as a parameter in widget?

I created a widget and I want to pass a function to this widget, but when I try to do this I get this error.
Moreover when this function is passed to a widget it is automatically used, because it debugPrint "MONEY CHANGED".
This is the function code:
class Functions {
changeMoney(int money) {
Boxes.getCharacter().get(0)!.money =
Boxes.getCharacter().get(0)!.money + money;
debugPrint("MONEY CHANGED");
}
}
And this is widget's code:
class DialogButton extends StatefulWidget {
const DialogButton(
{Key? key,
required this.answer,
required this.function,
required this.possible})
: super(key: key);
final String answer;
final Function function;
final bool possible;
#override
State<DialogButton> createState() => _DialogButtonState();
}
class _DialogButtonState extends State<DialogButton> {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: ElevatedButton(
onPressed: () => {
Navigator.of(context, rootNavigator: true).pop(),
widget.function
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 18.0),
child: Row(children: [
Expanded(child: Container()),
Text(
widget.answer,
style: rajdhaniStyle(weight: FontWeight.w600, size: 18),
),
Expanded(child: Container()),
]),
)),
);
}
}
So the question is how should i correctly pass a function inside widget without automatically make it turn on the function and without getting an error?
If the function have parameter, then the received widget (here is DialogButton) also need to declare parameter type. Example, the function is: void doSomething(String hehe, int yolo) {}. Then your DialogButton should be declare as Function(String, int) onTap.
More details, in first screen, you can call DialogButton like this:
class Screen1 extends StatelessWidget {
#override
build(...) {
// do something
DialogButton(
answer: "your answer",
function: (double money) {},
possible: true,
)
}
}
Otherwise, if you want to split/separate the function (not write-in-line like above), you could do as follow:
class Screen1 extends StatelessWidget {
#override
build(...) {
// do something
DialogButton(
answer: "your answer",
function: myFunction,
possible: true,
)
}
myFunction(double money) {}
}

Assigning color value to parameter's | dart flutter

i have GridView where there is 40 buttons, that's one of them:
MyCalcButton(
//clr: Colors.amber
myNum: "2",
funkcja: () {},
),
In my button class there are 2 required parameters, myNum and funkcja and one unrequired - > clr:. Now i want to change value (color) of this clr parameter some of those buttons, not every. Is there a way to assign this color to some of these and give rest of them one static color? Is it necessary to give one by one color for this clr ??
There is class of this button
String myNum = "";
class MyCalcButton extends StatefulWidget {
const MyCalcButton({Key? key, this.myNum, required this.funkcja, this.clr})
: super(key: key);
final Function funkcja;
final myNum;
final Color? clr;
#override
State<MyCalcButton> createState() => _MyCalcButtonState();
}
class _MyCalcButtonState extends State<MyCalcButton> {
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
widget.funkcja();
},
style: ElevatedButton.styleFrom(
//maybe here i can change something to do this?? I dont know
primary: const Color.fromARGB(255, 195, 204, 203),
),
child: Center(
child: Text(
widget.myNum.toString(),
)),
);
}
}
You can provide default value on parameter, in your case on clr
class MyCalcButton extends StatefulWidget {
const MyCalcButton({
Key? key,
this.myNum,
required this.funkcja,
this.clr =const Color.fromARGB(255, 195, 204, 203),
}) : super(key: key);
and use this like primary: widget.clr,.
Or just provide default value on null clr case like
primary: widget.clr ?? const Color.fromARGB(255, 195, 204, 203),
If you like to update color within this state. you can create a local variable inside state class.
class _MyCalcButtonState extends State<MyCalcButton> {
late Color? color = widget.clr;
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
widget.funkcja();
},
style: ElevatedButton.styleFrom(
primary: color,
),
child: Center(
child: Text(
widget.myNum.toString(),
)),
);
}
}

ERROR: "Exception caught by gesture" in Flutter

I am trying to store data in a class to store this information and use them later, but I am getting this error message and can't find out what I have to change to make it work. Here is a simplified version of my code where I get the same error.
class LUI {
String lui;
LUI({
this.lui,
});
}
class Test extends StatefulWidget {
final LUI data;
Test({
Key key,
this.data,
});
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
TextEditingController _controller = new TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
child: Container(
child: TextField(
controller: _controller,
keyboardType: TextInputType.number,
))),
TextButton(
child: Text('add information'),
onPressed: () {
_sendresult(context);
print(widget.data.lui);
},
),
],
),
),
);
}
void _sendresult(BuildContext context) {
final result = _controller.text;
print(result);
setState(() {
widget.data.lui = result;
});
}
}```
The problem is that, the Test class expects a value to be passed for the data argument. However as you are not passing any value to that while creating the instance, which makes the field to remain null.
Later when you access the same field in the print(widget.data.lui); it results in a null pointer exception. As you already ahve figured, the soultion is to pass a value to data.
However, you can write your code in a better way so that you can catch this error in the compile time rather than waiting till the runtime.
Approach 1: (With Null-Safety enabled)
Use the keyword required to specify the filed is a must e.g.
Test({
Key key,
required this.data,
});
Now, with this, creating an instance like Test() with arguments input will throw compilation errors compalining about the missing input argument.
Approach 2: (Without Null-Safety enabled)
Use the annotation #required to specify the filed is a required e.g.
Test({
Key key,
#required this.data,
});
In this case, failing to pass an argument will throw a compile time warning (yellow line highlighter) which unlike the above case will inform you but will not block your execution.
I had to send a value for the data parameter from where I create the instance of the Test class. Thanks, Sisir for your answer.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Basis',
theme: ThemeData(
primaryColor: Colors.red,
textTheme: TextTheme(
headline1: TextStyle(
fontSize: 40,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
),
home: Test(data: new LUI()));
}
}

When to use Valuechanged<> versus Valuesetter<> in flutter

I've been following some of the beginner flutter tutorials on their website and was doing this tutorial for basic interactivity, specifically the part where a parent widget is used to manage the state of a child widget. There is a ParentWidget and _ParentWidgetState class the code for which is as follows:
class ParentWidget extends StatefulWidget {
#override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
bool _active = false;
void _handleTapboxChanged(bool newValue) {
setState(() {
_active = newValue;
});
}
#override
Widget build(BuildContext context) {
return Container(
child: TapboxB(
active: _active,
onChanged: _handleTapboxChanged,
),
);
}
}
TapboxB is a class which is a child of ParentWidget, the code for which is as follows:
class TapboxB extends StatelessWidget {
TapboxB({this.active: false, #required this.onChanged});
final bool active;
final ValueChanged<bool> onChanged;
void _handleTap() {
onChanged(!active);
}
Widget build(BuildContext context) {
return GestureDetector(
onTap: _handleTap,
child: Container(
child: Column(
//aligns column in the centre vertically
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
//sets text depending on _active boolean
active ? 'Active' : 'Inactive',
style: TextStyle(fontSize: 20.0, color: Colors.white),
),
Text(
'Tapbox B',
style: TextStyle(fontSize: 14.0, color: Colors.white),
),
],
),
width: 100.0,
height: 100.0,
decoration: BoxDecoration(
//sets colour depending on _active boolean
color: active ? Colors.lightGreen[700] : Colors.grey[600],
),
),
);
}
}
The _handleTap method is called when the widget is clicked, which calls the onChanged callback, which toggles the active variable. In the definition for onChanged the type is ValueChanged<bool> which is documented as a "signature for callbacks that report that an underlying value has changed." If I change this however to ValueSetter<bool> the app works in the exact same way and nothing seems to change. So my question is what is the difference between the two of these? Is one better in this particular scenario?
I searched the documentation for you, using just flutter ValueChanged ValueSetter, and quickly found this:
void ValueSetter (T Value)
Signature for callbacks that report that a value has been set.
This is the same signature as ValueChanged, but is used when the callback is called even if the underlying value has not changed. For example, service extensions use this callback because they call the callback whenever the extension is called with a value, regardless of whether the given value is new or not.
typedef ValueSetter<T> = void Function(T value);
So they're just typedefs to the same underlying type, but one has a different semantic meaning, presumably used as self-documenting code despite the same underlying signature.
If you don't need the latter meaning of ValueSetter, then use ValueChanged, as the API already said.