I have issue's when i will using Function callback - flutter

i have custom button widget from different file, then i create callback function it might will showing pointer from main function, when i'm running i have issues this:
Issues
and this code, main layout:
import 'package:flutter/material.dart';
import './increment.dart';
void main() => runApp(MaterialApp(
home: HomePage(),
));
class HomePage extends StatefulWidget {
HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _counter = 0;
void increment() {
setState(() {
_counter = _counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
Text('Result = ${_counter}'),
SizedBox(
height: 100,
),
Inc(increment)
// ElevatedButton(onPressed: increment, child: Text('-'))
],
),
),
);
}
}
and this widget custom:
import 'package:flutter/material.dart';
class Inc extends StatelessWidget {
final Function selectInc;
Inc(this.selectInc);
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
height: 100,
child: ElevatedButton(onPressed: selectInc(), child: Text('+')),
);
}
}
how i can solve this?

onPressed: selectInc()
This is calling the function and using the result. You need to use the function only as a parameter:
onPressed: () => selectInc()
Since your selectInc method is a void function, the exact type the callback requires, you could also omit the anonymous function wrapper and pass your callback directly:
onPressed: selectInc
However, it seems you need some more type safety for the compiler to not complain. Make
final Function selectInc;
look like this:
final VoidCallback selectInc;

Related

Flutter Argument passing

I was trying to pass argument to a button widget but I'm getting below error message.
Here is my argument:
ElevatedRegisterButton(
navigator: Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
return const RegisterPage1();
})))
Here is my widget where I was trying to pass argument:
import 'package:flutter/material.dart';
class ElevatedRegisterButton extends StatefulWidget {
const ElevatedRegisterButton({super.key, required this.navigator});
final String navigator;
#override
State<ElevatedRegisterButton> createState() => _ElevatedRegisterButtonState();
}
class _ElevatedRegisterButtonState extends State<ElevatedRegisterButton> {
#override
Widget build(BuildContext context) {
return ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
onPressed: () {
widget.navigator;
},
child: const Text('Register'),
);
}
}
Here is the error message I'm getting:
The argument type 'Future' can't be assigned to the parameter type 'String'.
You need to set the navigator member to type final void Function(), because the onPressed property of ElevatedButton requires this type. You also need to pass it differently, because push is another type of function. Lastly, simply set onPressed to widget.navigator.
An example code is below based on your code snippet:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) =>
const MaterialApp(home: Scaffold(body: HomePage()));
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
#override
Widget build(BuildContext context) => SafeArea(
// see the difference, push is within () {}
child: ElevatedRegisterButton(navigator: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const RegisterPage1()));
}),
);
}
class ElevatedRegisterButton extends StatefulWidget {
const ElevatedRegisterButton({super.key, required this.navigator});
final void Function() navigator;
#override
State<ElevatedRegisterButton> createState() => _ElevatedRegisterButtonState();
}
class _ElevatedRegisterButtonState extends State<ElevatedRegisterButton> {
#override
Widget build(BuildContext context) => ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
// here simply set the function
onPressed: widget.navigator,
child: const Text('Register'),
);
}
// added for demo purposes
class RegisterPage1 extends StatelessWidget {
const RegisterPage1({super.key});
#override
Widget build(BuildContext context) =>
const Scaffold(body: SafeArea(child: Text('RegisterPage1')));
}
Problem 1
In your code:
class ElevatedRegisterButton extends StatefulWidget {
const ElevatedRegisterButton({super.key, required this.navigator});
// this line
final String navigator;
you are accepting a String navigator; but you are passing to it a Future:
ElevatedRegisterButton(
navigator: Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
return const RegisterPage1();
})))
problem 2
If you try running your code, you'll get an error:
setState() or markNeedsBuild called during build
So, to fix the issues, refactor your code as follows:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MaterialApp(home: MyApp()));
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light().copyWith(),
debugShowCheckedModeBanner: false,
home: SafeArea(
child: Scaffold(
body: ElevatedRegisterButton(navigator: Navigator.of(context))),
),
);
}
}
class ElevatedRegisterButton extends StatefulWidget {
const ElevatedRegisterButton({super.key, required this.navigator});
final NavigatorState navigator;
#override
State<ElevatedRegisterButton> createState() => _ElevatedRegisterButtonState();
}
class _ElevatedRegisterButtonState extends State<ElevatedRegisterButton> {
#override
Widget build(BuildContext context) {
return ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
onPressed: () {
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.navigator.push(MaterialPageRoute(
builder: (context) => Text("h1"),
));
});
},
child: const Text('Register'),
);
}
}
See also
setState() or markNeedsBuild called during build

lib/answer.dart:16:20: Error: The argument type 'Function' can't be assigned to the parameter type 'void Function()?'

This is the main.dart file:
import 'package:flutter/material.dart';
import './question.dart';
import './answer.dart';
import 'package:path/path.dart';
void main() {
runApp(MyFirstApp());
}
class MyFirstApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _MyFirstAppState();
}
}
class _MyFirstAppState extends State<MyFirstApp> {
var _questionIndex = 0;
void _ansButtonPress(context) {
setState(() {
_questionIndex = _questionIndex + 1;
});
}
var questions = ['favorate color ', 'favorate animal', 'where is this'];
var answers = ['Option 1', 'Option 2', 'Option 3'];
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('My first app '),
),
body: Column(
children: [
Question(
questions[_questionIndex],
),
Answer(_ansButtonPress),
Answer(_ansButtonPress),
Answer(_ansButtonPress),
],
),
),
);
}
}
This is answwers.dart file:
import 'package:flutter/material.dart';
class Answer extends StatelessWidget {
final Function selectHandler;
Answer(this.selectHandler);
// const Answer({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
child: RaisedButton(
color: Colors.blue,
child: Text('Answer 1'),
onPressed: selectHandler,
),
);
}
}`enter code here`
[enter image description here][1]
This is questions.dart:
import 'package:flutter/material.dart';
class Answer extends StatelessWidget {
final Function selectHandler;
Answer(this.selectHandler);
// const Answer({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
child: RaisedButton(
color: Colors.blue,
child: Text('Answer 1'),
onPressed: selectHandler,
),
);
}
}
I am working on a quiz app by watching a tutorial, I am just a beginner now facing an error. I can't fix it, I added the code and error with this.
lib/answer.dart:16:20: Error: The argument type 'Function' can't be assigned to the parameter type 'void Function()?'.
'Function' is from 'dart:core'.
onPressed: selectHandler,
The onPressed property of the button in question is a VoidCallback?, so for ease of use, make your property a VoidCallback (nullable or not is up to you), too:
final Function selectHandler;
becomes
final VoidCallback selectHandler;
Then make sure the method you pass to this property actually is of this signature:
void _ansButtonPress(context) {
becomes
void _ansButtonPress() {
I don't know what that "context" was supposed to do anyway, it wasn't used.
Now it should work.
Use
final void Function() selectHandler;
instead of
final VoidCallback? selectHandler;
and
final Function selectHandler;

Force rebuild of a stateful child widget in flutter

Let's suppose that I have a Main screen (stateful widget) where there is a variable count as state. In this Main screen there is a button and another stateful widget (let's call this MyListWidget. MyListWidget initialize it's own widgets in the initState depending by the value of the count variable. Obviously if you change the value of count and call SetState, nothing will happen in MyListWidget because it create the values in the initState. How can I force the rebuilding of MyListWidget?
I know that in this example we can just move what we do in the initState in the build method. But in my real problem I can't move what I do in the initState in the build method.
Here's the complete code example:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int count = 5;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
Expanded(
child: MaterialButton(
child: Text('Click me'),
color: Colors.red,
onPressed: () {
setState(() {
count++;
});
},
),
),
MyListWidget(count),
],
));
}
}
class MyListWidget extends StatefulWidget {
final int count;
const MyListWidget(this.count, {Key? key}) : super(key: key);
#override
_MyListWidgetState createState() => _MyListWidgetState();
}
class _MyListWidgetState extends State<MyListWidget> {
late List<int> displayList;
#override
void initState() {
super.initState();
displayList = List.generate(widget.count, (int index) => index);
}
#override
Widget build(BuildContext context) {
return Expanded(
child: ListView.builder(
itemBuilder: (BuildContext context, int index) => ListTile(
title: Text(displayList[index].toString()),
),
itemCount: displayList.length,
),
);
}
}
I don't think the accepted answer is accurate, Flutter will retain the state of MyListWidget because it is of the same type and in the same position in the widget tree as before.
Instead, force a widget rebuild by changing its key:
class _MyHomePageState extends State<MyHomePage> {
int count = 5;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
Expanded(
child: MaterialButton(
child: Text('Click me'),
color: Colors.red,
onPressed: () {
setState(() {
count++;
});
},
),
),
MyListWidget(count, key: ValueKey(count)),
],
),
);
}
}
Using a ValueKey in this example means the state will only be recreated if count is actually different.
Alternatively, you can listen to widget changes in State.didUpdateWidget, where you can compare the current this.widget with the passed in oldWidget and update the state if necessary.
USE THIS:
class _MyHomePageState extends State<MyHomePage> {
int count = 5;
MyListWidget myListWidget = MyListWidget(5);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
Expanded(
child: MaterialButton(
child: Text('Click me'),
color: Colors.red,
onPressed: () {
setState(() {
count++;
myListWidget = MyListWidget(count);
});
},
),
),
myListWidget,
],
));
}
}

Why is the print statement inside setState is executed as soon as the app starts?

I have the following code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Home(),
),),
);
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int i = 0;
updatePage(int value) {
setState(() {
i = value;
print('$i button clicked');
});
}
#override
Widget build(BuildContext context) {
return Column(
children: [
MyButton(
s: '10',
f: updatePage(10),
),
MyButton(
s: '20',
f: updatePage(20),
),
],
);
}
}
class MyButton extends StatelessWidget {
final String s;
final Function f;
MyButton({this.s, this.f});
#override
Widget build(BuildContext context) {
return Container(
width: 70,
height: 40,
child: ElevatedButton(
child: Text(s),
onPressed: f,
style: ElevatedButton.styleFrom(
primary: Colors.white,
),
),
);
}
}
I thought I was structuring the app correctly in terms of breaking down it into smaller widgets easy to track and update the code. However, the print statements inside the following function are executed as and when app starts and the buttons are also disabled.
updatePage(int value) {
setState(() {
i = value;
print('$i button clicked');
});
}
I am not able to figure out why is this happening and what am I doing incorrectly.
Also, for the instance variables, I am getting null error in dartpad (if I turn off null safety they go away), but android studio does not show any error. Not able to understand why? Is this because null safety is off in android studio? AS and dartpad are on same versions of flutter & dart.
class MyButton extends StatelessWidget {
final String s;
final Function f;
MyButton({this.s, this.f});
Error that appears in dartpad but not in AS:
I am pretty much beginner to flutter and a detailed help would be greatly appreciated.
I believe the other answers already tell you how to solve your problem. Just as a didactic explanation of why this is happening:
When you do this:
MyButton(
s: '10',
f: updatePage(10),
)
you are calling the function updatePage(10) and using the result of this function call as the input to the parameter f.
To pass a Function, you can't use the () parenthesis. If you use the parenthesis you are calling the function itself and then it will represent its return value. This is why the print statement is being called on your example.
So, to call a function, you should use the function name without parameters:
MyButton(
s: '10',
f: updatePage,
)
This will pass the updatePage itself to the MyButton widget, and not its return value. However, this way you can't pass the 10 parameter you wanted. So, the way to solve this is to create a new anonymous function that only calls updatePage with the 10 parameter:
MyButton(
s: '10',
f: ()=>updatePage(10),
)
You should do like this,
MyButton(s: '10',f: () => updatePage(10)),
The reason for the instance variables being null and seeing the print statement to be executed is that you do not pass the function to the newly created instance but call the function and pass the return value (that is null in this case because of a void function).
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Home(),
),
),
);
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int i = 0;
updatePage(int value) {
setState(() {
i = value;
print('$i button clicked');
});
}
#override
Widget build(BuildContext context) {
return Column(
children: [
MyButton(
s: '10',
f: updatePage,
),
MyButton(
s: '20',
f: updatePage,
),
],
);
}
}
class MyButton extends StatelessWidget {
final String s;
final Function f;
MyButton({this.s, this.f});
#override
Widget build(BuildContext context) {
return Container(
width: 70,
height: 40,
child: ElevatedButton(
child: Text(s),
onPressed: () => f(int.parse(s)),
style: ElevatedButton.styleFrom(
primary: Colors.black,
),
),
);
}
}
replace onPressed: f, with onPressed:()=> f(),
class MyButton extends StatelessWidget {
final String s;
final Function f;
MyButton({this.s, this.f});
#override
Widget build(BuildContext context) {
return Container(
width: 70,
height: 40,
child: ElevatedButton(
child: Text(s),
onPressed:()=> f(),
style: ElevatedButton.styleFrom(
primary: Colors.white,
),
),
);
}
}
and while using it
MyButton(
s: "10",
f: () => updatePage(3),
),

Flutter: Access function in another class/file

In the following set-up, how would I allow fileb.dart to access the function reset that is in filea.dart:
filea.dart:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Test',
theme: ThemeData(
primaryColor: Colors.pink[200],
),
home: MyHomePage(title: ''),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
MyHomePageState createState() => MyHomePageState();
}
class MyHomePageState extends State<MyHomePage>
{
int testCount = 0;
void test() {
setState(() {
testCount++;
});
}
fileb.dart
import 'package:flutter/material.dart';
import './filea.dart';
class Buttons extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(bottom: 11.0, top: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
FloatingActionButton(
heroTag: "btn1",
onPressed: () => MyHomePageState.test(),
child: const Icon(Icons.cancel),
splashColor: Colors.pink[900],
backgroundColor: Colors.pink[200],
),
),
],
),
),
],
);
}
}
What do I need to change to make this work?
You cannot call a function in another class unless it is static, and it should not deal with any variables that are non-static/local or it will through an error.
Another way to call a function is to pass it as an argument to the page.
You can achieve this through function callback.
Pass the test callback to ButtonWifget's constructor and use the callback in fab button onPressed as below mentioned sample code:
// In Button class
Buttons({this.testCallback});
final Function testCallback;
...
FloatingActionButton(
heroTag: "btn1",
onPressed: testCallback,
)
// In MyHomePage class, pass the callback wherever you have used `Buttons` widget
Buttons(testCallback:test());
Hope this helps you :)