How to pass a function as a parameter in widget? - flutter

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) {}
}

Related

type 'Null' is not a subtype of type Function

I want to feed Two to the function. When function receives Two as the data, it should print success to the console. But it throws an error.
Error: type 'Null' is not a subtype of type Function
~~~PAGE1 for button
const ToolSetButton(
{Key? key,
this.function = myRunFunction,
const Function myRunFunction = defaultFunction;
void defaultFunction() {
print('Button Tapped');
}
PAGE 2 where the above widget being used
List
enum ButtonList {One, Two, Three}
Calling function
function: testFunc(ButtonList.Two)),
Function
testFunc( ButtonList type) {
if (type == ButtonList.Two ){print('sucess ')};
}
~~~PAGE 3 which is executing the function
Executing function inside an inkwell
class ToolSetButton extends StatefulWidget {
final Function function ;
}
child: InkWell(
splashColor: Colors.white,
onTap: () {
Function.apply(widget.function, [], {});
},
If I got it correctly you want when you tap on the ToolSetButton if it's of type ButtonList.two you want success to be printed on console. A simple approach would be to change your ToolSetButton widget to have a buttonType parameter (EDIT: you can also pass your own function):
enum ButtonList { One, Two, Three }
class ToolSetButton extends StatefulWidget {
const ToolSetButton({
required this.buttonType,
required this.buttonText,
required this.myFunction,
Key? key,
}) : super(key: key);
final ButtonList buttonType;
final String buttonText;
final Function myFunction;
#override
_ToolSetButtonState createState() => _ToolSetButtonState();
}
class _ToolSetButtonState extends State<ToolSetButton> {
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
widget.myFunction();
},
child: Text(widget.buttonText),
);
}
}
Now when you use the create ToolSetButton you could define its type for later use, and also pass your own function to be executed when onTap:
class TestClass extends StatelessWidget {
const TestClass({Key? key}) : super(key: key);
void testFunc(ButtonList type) {
if (type == ButtonList.Two) {
print('sucess ');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ToolSetButton(
buttonText: 'One',
buttonType: ButtonList.One,
myFunction: () {
testFunc(ButtonList.One);
},
),
ToolSetButton(
buttonText: 'Two',
buttonType: ButtonList.Two,
myFunction: () {
testFunc(ButtonList.Two);
},
),
ToolSetButton(
buttonText: 'Three',
buttonType: ButtonList.Three,
myFunction: () {
testFunc(ButtonList.Three);
},
),
],
),
);
}
}

Using Type Function and pass it into ElevatedButton onPressed, Flutter

I am new to Flutter + Dart. I basically have class in the following.
First I have a clas called BottomForm where it have build function that returns ElevatedButton problem when I call Function type variable in onPressed I have an issue saying that:
The argument type 'Function' can't be assigned to the parameter type 'void Function()?'.dartargument_type_not_assignable
import 'formbutton.dart';
// Define a corresponding State class.
// This class holds the data related to the Form.
class _MyCustomFormState extends State<MyCustomForm> {
// Create a text controller and use it to retrieve the current value
// of the TextField.
final email = TextEditingController();
final password = TextEditingController();
void _logIn() {
print("Logged In.");
}
#override
void dispose() {
// Clean up the controller when the widget is disposed.
email.dispose();
password.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: TextFormField(
autocorrect: true,
controller: email,
),
),
ButtonForm(_logIn, "Hello"),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text(email.text),
);
});
},
tooltip: "Show me the value",
child: Icon(Icons.text_fields),
),
);
}
}
//Define a Custom Widget
class MyCustomForm extends StatefulWidget {
#override
_MyCustomFormState createState() => _MyCustomFormState();
}
Than I have a problem in the main class for our Button . When I pass the Function functionApply;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class ButtonForm extends StatelessWidget {
final Function functionApply;
final String textButton;
ButtonForm(this.functionApply, this.textButton);
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
child: ElevatedButton(
child: Text(this.textButton),
onPressed: this.functionApply, // I have a problem here!!
),
);
}
}
onPressed is a type of VoidCallback
typedef VoidCallback = void Function()
So instead of using
final Function functionApply;
use
final VoidCallback functionApply;
So your ButtonForm will be
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class ButtonForm extends StatelessWidget {
final VoidCallback functionApply;
final String textButton;
ButtonForm(this.functionApply, this.textButton);
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
child: ElevatedButton(
child: Text(textButton),
onPressed: functionApply, // Problem Solved!!
),
);
}
}
Try this:
ElevatedButton(
child: Text(this.textButton),
onPressed: () {
functionApply();
},
)
Give the return type of your function. If you don't give any return type then by default the return type will be dynamic. But onPressed function 's return type is void. So just change the function deceleration and it will work nicely.
final void Function() functionApply;

flutter, How to judge whether another widget is in the tree

Actually, I have a parent widget, and It has some of the child widgets in its Column.
like this
Container(
width: double.infinity,
color: Colors.white,
padding: EdgeInsets.fromLTRB(20.0, 50.0, 20.0, 0.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Title(),
SizedBox(height: 80.0,),
confirmLoginType(),
SizedBox(height: 10.0),
LoginButton(),
PolicyTips(
key: IndexGlobalKey.policyTipsKey,
updateState: receiveMessageUpdateState
),
Bottom()
],
),
),
in the PolicyTips, I pass a key to it, and I want to get the key in the LoginButton , but It has always been null when I get currentState.
The code is below:LoginButton
class LoginButton extends StatefulWidget {
LoginButton({Key key}) : super(key: key);
#override
_LoginButtonState createState() => _LoginButtonState();
}
class _LoginButtonState extends State<LoginButton> {
#override
Widget build(BuildContext context) {
policyTipsKey = IndexGlobalKey.policyTipsKey.currentState;
return Container(
child: Text()
)
}
}
what can I do? help me please, thanks.
This is IndexGlobalKey code.
class IndexGlobalKey {
static final GlobalKey<_PolicyTipsState> policyTipsKey = GlobalKey<_PolicyTipsState>();
static GlobalKey<_FormState> phoneLoginKey = GlobalKey<_FormState>();
static GlobalKey<_FormForIdCardLoginState> idCardLoginKey = GlobalKey<_FormForIdCardLoginState>();
}
Build method of _LoginButtonState runs before PolicyTips renders and before IndexGlobalKey.policyTipsKey is actually set. The reason is LoginButton goes before PolicyTips in column. Thats why you get null when you call IndexGlobalKey.policyTipsKey.currentState from build of _LoginButtonState.
To solve this you need to call IndexGlobalKey.policyTipsKey.state right where you use it. For example, when you need to get policy tips state on button tap just use it inside onPressed callback:
class _LoginButtonState extends State<LoginButton> {
#override
Widget build(BuildContext context) {
// An example of your button
return TextButton(
onPressed: () {
final policyTipsState = IndexGlobalKey.policyTipsKey.currentState;
// Here you can use policyTipsState
},
child: Text('button'),
);
}
}

Flutter, calling a function inside the button

I am new to flutter and I make some practices. I have a StatelessWidget called doChange and makeChange and one StatefulWidget. This class which is statefulwidget I made child of the home page of the app also. But, I think that it is unnecessary to define here. My purpose in this case is that, I want to change the state of the button make open,make closed and at the same time the text open and close will also change. I think that class changeText has not problem but in makeChange class I have some trouble with creating constructor and function to call into onPress. The states do not change. How can i solve this or is that any way to do this without function ?
class changeText extends StatelessWidget{
final doChange;
changeText({#required this.doChange});
#override
Widget build(BuildContext context){
return Container(
//some codes
//some codes
child: doChange ? Text("open") : Text("close"),
);
}
}
class makeChange extends StatelessWidget{
final changeState;
makeChange({#required this.changeState}); // I want to add constructor here lets say onPressButton
whenPressed(){ // I want to create a function with the that constructor that I have add.
}
#override
Widget build(BuildContext context){
return Container(
//some codes
//
child: Column(
children: [
MaterialButton(
//some codes
//
onPressed: () {} // Here I want to call a function when press the button.
child: changeState ? Text("make open") : Text("make close"),
),
],
),
);
}
}
class Mainarea extends StatefulWidget{
#override
_MainareaState createState() => _mainAreaState();
}
class _MainareaState extends State<Mainarea> {
bool isChange= false;
#override
Widget build(BuildContext context){
return Container(
//some codes
//
child: Column(
children: <Widget>[
changeText(doChange: !this.isChange),
makeChange(changeState: !this.isChange),
],
),
);
}
}
I just added a final Function(bool) callback as a parameter, which can be called inside from the stateless widget, and returns to the calling function. From there you can call setState
class changeText extends StatelessWidget {
final bool doChange;
changeText({#required this.doChange});
#override
Widget build(BuildContext context) {
return Container(
//some codes
//some codes
child: doChange ? Text("open") : Text("close"),
);
}
}
class makeChange extends StatelessWidget {
final bool changeState;
final Function(bool) callback;
makeChange(
{#required
this.changeState,
#required
this.callback}); // You can rename this to onPressed or whatever
#override
Widget build(BuildContext context) {
return Container(
//some codes
//
child: Column(
children: [
MaterialButton(
//some codes
//
onPressed: () => callback( changeState),
child: changeState ? Text("make close") : Text("make open"), //I had to swap these around to make the text correct
),
],
),
);
}
}
class Mainarea extends StatefulWidget {
#override
_MainareaState createState() => _MainareaState();
}
class _MainareaState extends State<Mainarea> {
bool isChange = false;
#override
Widget build(BuildContext context) {
return Container(
//some codes
//
child: Column(
children: <Widget>[
changeText(doChange: !this.isChange),
makeChange(
changeState: !this.isChange,
callback: (bool val) {
setState(() => isChange = val); //this is your function that returns and resetst the value in the parent widget
},
),
],
),
);
}
}

How to make widget out of a GestureDetector with a Container child?

I want to make a reusable button with a container in GestureDetector which will execute some function if I tap it and its color will become dark if I hold it. Any help, hint, tip would be very much appreciated.
I tried writing the GestureDetector in the custom widget file but it gives me errors.
When i try to extract widget on the GestureDetector it gives an Reference to an enclosing class method cannot be extracted error.
(the main page)
import 'package:flutter/material.dart';
import 'ReusableTwoLineList.dart';
import 'Text_Content.dart';
const mainTextColour = Color(0xFF212121);
const secondaryTextColour = Color(0xFF757575);
const inactiveBackgroundCardColor = Color(0xFFFFFFFF);
const activeBackgroundCardColor = Color(0xFFE5E5E5);
enum CardState {
active,
inactive,
}
class SettingsPage extends StatefulWidget {
#override
_SettingsPageState createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
CardState currentCardState = CardState.inactive;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Settings'),
),
body: ListView(
children: <Widget>[
GestureDetector(
onTapDown: (TapDownDetails details) {
setState(() {
currentCardState = CardState.active;
});
},
onTapCancel: () {
setState(() {
currentCardState = CardState.inactive;
});
},
onTap: () {
setState(() {
currentCardState = CardState.inactive;
//some random function
});
},
child: ReusableTwoLineList(
mainTextColor: mainTextColour,
secondaryTextColor: secondaryTextColour,
backgroundCardColor: currentCardState == CardState.active
? activeBackgroundCardColor
: inactiveBackgroundCardColor,
cardChild: TextContent(
mainLabel: 'First Day',
secondaryLabel: 'This is the first day of the week',
),
),
),
ReusableTwoLineList(
mainTextColor: mainTextColour,
secondaryTextColor: secondaryTextColour,
cardChild: TextContent(
mainLabel: '2nd day',
secondaryLabel: 'This is the end day',
),
),
ReusableTwoLineList(
mainTextColor: mainTextColour,
secondaryTextColor: secondaryTextColour,
),
],
),
);
}
}
ReusableTwoLineList.dart (the custom widget i am trying to make)
class ReusableTwoLineList extends StatelessWidget {
ReusableTwoLineList({
#required this.mainTextColor,
#required this.secondaryTextColor,
this.backgroundCardColor,
this.cardChild,
this.onPressed,
});
final Color mainTextColor, secondaryTextColor, backgroundCardColor;
final Widget cardChild;
final Function onPressed;
#override
Widget build(BuildContext context) {
return Container(
color: backgroundCardColor,
padding: EdgeInsets.symmetric(horizontal: 16),
height: 72,
width: double.infinity,
child: cardChild,
);
}
}
This is what i want but in a custom widget so i can use it over and over.
Normal-https://i.imgur.com/lVUkMFK.png
On Pressed-https://i.imgur.com/szuD4ZN.png
You can use extract method instead of extract widget. Flutter will add everything as it is, and instead of a class you will get a reusable function.