Flutter CallBack Function - android

I have a quantity that needs to be updated in the parent widget. Quantity needs to be updated when pressing + or - Icon in the child widget. I passed the callback function the the child stateless widget, but it is not working. Instead I get an error saying setstate() or markneedsbuild() called during build.
This is the parent widget
class Wash extends StatefulWidget {
#override
_WashState createState() => _WashState();
}
class _WashState extends State<Wash> {
int quantity = 0;
void updateQuantity(command) {
if (command == 'add') {
setState(() {
quantity++;
});
} else {
setState(() {
quantity--;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: OrderTile(
imgPath: 'shorts',
itemType: 'Shorts',
quantityCallBack: updateQuantity,
),
);
}
This is the child widget
class OrderTile extends StatelessWidget {
OrderTile({this.itemType, this.imgPath, this.quantityCallBack});
final String imgPath;
final String itemType;
final Function quantityCallBack;
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(12.0),
child: Row(
children: <Widget>[
Expanded(
flex: 1,
child: CircleAvatar(
backgroundImage: AssetImage('images/${imgPath}.jpg'),
radius: 30.0,
),
),
Expanded(
flex: 3,
child: _Description(
title: itemType,
),
),
GestureDetector(
onTap: quantityCallBack('add'),
child: Icon(
Icons.add,
size: 24.0,
),
),
SizedBox(
width: 14,
),
Text('1'),
SizedBox(
width: 14,
),
GestureDetector(
onTap: quantityCallBack('remove'),
child: Icon(
Icons.remove,
size: 24.0,
),
),
],
),
);
}
}
Am I doing the right thing for the function call back implementation?

You're calling your callback function in the wrong way inside your onTap callback. Change:
onTap: quantityCallBack('add'),
for
onTap: () => quantityCallBack('add'),
You can only pass a function the way you passed if they have the same type. In this case the onTap is void function() it doesn't have any arguments.
Also, your not passing the updated quantity value to your Text Widget

Related

onTap function as parameter is not working

I have created a custom button widget class with Icon and onTap constructors. I want to use this button in any screen with custom icon and custom function on button tapped.
class ActionButton extends StatelessWidget {
ActionButton({this.icon, this.onPress});
final FaIcon? icon;
final Function? onPress;
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(18.0),
// padding: const EdgeInsets.all(0.2),
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(6.0)),
// padding: const EdgeInsets.all(1.0),
child: IconButton(
onPressed: () {
onPress!;
},
icon: icon!,
color: Colors.black,
),
);
}
}
As you can see, with this widget I can use this ActionButton in any screen with any icon and its action will be any function what I pass as argument.
But when I am using this button on my another page and passing function, function does not get executed:
class ProductDetailsScreen extends StatelessWidget {
final Product? product;
const ProductDetailsScreen({Key? key, this.product}) : super(key: key);
#override
Widget build(BuildContext context) {
Sizes(context).initSize();
final double _h = Sizes.screenHeight;
final double _w = Sizes.screenWidth;
final _theme = Theme.of(context);
final product = ModalRoute.of(context)!.settings.arguments as Product;
void _goBack() {
Navigator.of(context).pop();
print('the page is popped');
}
return Scaffold(
// backgroundColor: _theme.scaffoldBackgroundColor,
body: SafeArea(
child: Stack(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
ActionButton(
icon: FaIcon(
Icons.arrow_back,
),
onPress: () {
_goBack();
}),
ActionButton(
icon: FaIcon(Icons.share),
)
],
),
ListView(
scrollDirection: Axis.vertical,
children: <Widget>[
Container(
height: 60,
child: Image.asset('${product.imageSource}'),
)
],
)
],
),
),
);
}
}
that_ goBack() function is not working
please help me, thanks in advance
In Product Details page you need to pass the onPress parameter as follows:
ActionButton(icon:FaIcon(
Icons.arrow_back,
),onPress:_goBack)`
And in ActionButton class
IconButton( onPressed: () { onPress!(); }, icon: icon!, color: Colors.black, ),

Changing a container color when container is tapped using gesturedetector in flutter

Hello i have been stock here trying to figure out what am doing wrong, the male and female reuseable cards are surpose to change color when tapped but after placing the GestureDetector inside my card Class it stopped working. The color refuse to change when tapped, take a look at the code below.
import 'package:bmi_calculator/card_icon.dart';
import 'package:bmi_calculator/cards.dart';
import 'package:flutter/material.dart';
Color activeCardColor = const Color(0XFF1D1E33);
Color inactiveCardColor = const Color(0XFF111328);
Color buttomContainerColor = const Color(0xffeb1555);
const double buttomContainerHeight = 70.0;
enum Gender {
male,
female,
}
class InputPage extends StatefulWidget {
const InputPage({Key? key}) : super(key: key);
#override
_InputPageState createState() => _InputPageState();
}
class _InputPageState extends State<InputPage> {
Gender? selectedGender;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BMI CALCULATOR'),
),
body: Column(
children: [
Expanded(
child: Row(
children: [
Expanded(
child: ReuseableCard(
ontapped: () {
setState(() {
selectedGender = Gender.male;
});
},
colour: selectedGender == Gender.male
? activeCardColor
: inactiveCardColor,
cardChild: const CardIcon(Icons.male, 'Male'),
),
),
Expanded(
child: ReuseableCard(
ontapped: () {
setState(() {
selectedGender = Gender.female;
});
},
colour: selectedGender == Gender.female
? activeCardColor
: inactiveCardColor,
cardChild: const CardIcon(Icons.male, 'Female'),
),
)
],
),
),
Expanded(
child: ReuseableCard(
colour: activeCardColor,
),
),
Expanded(
child: Row(
children: [
Expanded(
child: ReuseableCard(
colour: activeCardColor,
),
),
Expanded(
child: ReuseableCard(
colour: activeCardColor,
),
)
],
),
),
Container(
margin: const EdgeInsets.only(top: 10.0),
color: buttomContainerColor,
height: buttomContainerHeight,
width: double.infinity,
)
],
));
}
}
And here is my Card Class
import 'package:flutter/material.dart';
class ReuseableCard extends StatelessWidget {
final Color? colour;
final Widget? cardChild;
final void Function()? ontapped;
const ReuseableCard({this.colour, this.cardChild, this.ontapped});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
ontapped;
print('Button Pressed');
},
child: Container(
child: cardChild,
margin: const EdgeInsets.all(10),
width: double.infinity,
decoration: BoxDecoration(
color: colour,
borderRadius: BorderRadius.circular(10.0),
),
),
);
}
}
add brackets to the function call
onTap: () {
ontapped(); // here
print('Button Pressed');
},
The problem is in passing the ontapped function. When you simply place ontapped without Parenthesis () the function will not be called so you need to change that in following ways. When using Lambda function you have options to pass function
For multiple functions
onTap: () {
ontapped(); // here
},
For single function
onTap: ontapped, // here

Flutter call setState() to update UI in another class

I am trying to call a setState when a button is pressed so the ui can show the new list but even using functions i cant use setState or it will give me the error saying im calling setState inside a constructor.
This is my code for the statlessWidget:
class _MessageCard extends StatelessWidget {
final Mensagem message;
final int messageLenght;
final List<Mensagem> messageList;
var i;
_MessageCard(
{#required this.message,
#required this.messageLenght,
#required this.messageList});
#override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 600,
child: InkWell(
child: Container(
width: 900,
color: Colors.grey[200],
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 0),
child: Center(
child: Container(
width: 600,
child: Column(
children: <Widget>[
ListTile(
leading: CircleAvatar(
child: Icon(
Icons.notifications,
color: Colors.red[400],
),
backgroundColor: Colors.grey[200],
),
title: Text(
(this.message.vDescricao ?? '').trim(),
style: TextStyle(
fontSize: 14,
color: Colors.black,
),
),
subtitle: Text(
(this.message.vData ?? '').trim() +
' ' +
(this.message.vHora ?? '').trim(),
style: TextStyle(
color: Colors.red[400],
fontSize: 13,
),
),
trailing: FlatButton(
child: Text(
Translations.of(context)
.trans('finishmessageshort'),
),
onPressed: () => _showDeleteAlertMessage(
this.message.vNumero, context)),
),
Divider(
color: Colors.black54,
),
],
),
),
),
),
),
),
));
}
Future _showDeleteAlertMessage(String id, BuildContext context) {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text(
Translations.of(context).trans('finishmessage') + '?',
),
actions: <Widget>[
FlatButton(
child: new Text(
Translations.of(context).trans('closealert'),
),
onPressed: () {
Navigator.of(context).pop();
}),
FlatButton(
child: new Text(("Ok")),
onPressed: () =>
{_deleteMessage(id), Navigator.of(context).pop()},
)
],
);
});
}
_deleteMessage(String id) async {
for (i = 0; i < this.messageLenght; i++) {
if (this.messageList[0].vNumero == this.message.vNumero) {
this.messageList.removeAt(i);
_HomePageState().mensagemRepo.confirmMessage(this.message.vNumero);
await _HomePageState()._getMessages();
return this.messageList;
}
}
}
}
And this is my _getMessages()
_getMessages() async {
setState(() {
_loading = true;
_errorMsg = '';
});
try {
_messages = await mensagemRepo.getMessages();
print('loaded messages: ${_messages?.length}');
} catch (e) {
_errorMsg = e.toString();
}
setState(() {
_loading = false;
});
}
How can i make it so i can use this setState?
Thank you for your time and attention
Edit: Now updates List but not UI, because im not able to set HomePage state from MessageCard
You can only use setState in a StatefulWidget.
class MessageCard extends StatefulWidget {
#override
_MessageCardState createState() => _MessageCardState();
}
class _MessageCardState extends State<MessageCard> {
#override
Widget build(BuildContext context) {
// your build method here
}
}
Well, you can't set value for something that doesn't exist. Stateless by name itself makes it clear that it can't hold any state. Changing the widget to a stateful widget would work.
Stateless widget can not change the state once its rendered. To use setState and re-render the widget StatefulWidget is used.
Just change your MessageCard from Stateless Widget to StatefulWidget
class MessageCard extends StatefulWidget {
final Mensagem message;
final int messageLenght;
final List<Mensagem> messageList;
var i;
MessageCard(
{#required this.message,
#required this.messageLenght,
#required this.messageList});
#override
_MessageCardState createState() => _MessageCardState();
}
class _MessageCardState extends State<MessageCard> {
#override
Widget build(BuildContext context) {
// your build method here
}
}
Also, now "to use your MessageCard properties" like message, messageLenght, messageList, in _MessageCardState you have to use a property like widget.message, widget.messageList and widget.messageLenght respectively.
can this work to refresh the ui?
_getMessages() async {
_HomePageState()._messages = await mensagemRepo.getMessages();
print('loaded messages: ${_messages?.length}');
setState(() {
_HomePageState()._messagesList();
});
}
The code for _messagesList() is:
SliverChildBuilderDelegate _messagesList() {
int count() {
if (_errorMsg != '')
return 1;
else
return _messages == null ? 0 : _messages.length;
}
return SliverChildBuilderDelegate(
(BuildContext context, int index) {
print("i: $index");
if (_errorMsg != '') {
return Padding(
padding: EdgeInsets.all(20),
child: ErrorMessage(
error: _errorMsg,
),
);
} else {
return _MessageCard(
message: this._messages[index],
messageLength: this._messages.length,
messageList: this._messages);
}
},
childCount: count(),
);
}
I managed to make it work, by making both classes into one and calling a function to draw the messagecards, thank you all for your help and attention

How to update Dart Class Variable and refresh flutter state?

I have a main dart file and a settings dart file. The settings dart file is responsible for the appearance of the main dart file. Settings dart has a AppTheme class. Upon users typing on this settings page I want the main page to update.
My attempt at this was calling the class and redefining the variables based on user input. Doesnt work whether or not I use setState(). I also tried jus staying on the main page and tried changing the theme onPressed for the settings button. That didnt work either. Iconbutton update doesnt update the state either.
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:resume/settings.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Title',
theme: ThemeData.light(),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
double buttonMargin = MediaQuery.of(context).size.width / 10;
double screenWidth = MediaQuery.of(context).size.width;
double screenHeight = MediaQuery.of(context).size.height;
return MaterialApp(
home: Scaffold(
backgroundColor: AppTheme().backgroundMain,
body: SafeArea(
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 20, 0, 0),
child: Column(
children: <Widget>[
Container(
height: (MediaQuery.of(context).size.height / 2.25),
child: Column(children: <Widget>[
Container(
margin: EdgeInsets.all(10),
child: Text(AppTheme().name,
style: TextStyle(
fontFamily: 'Pacifico',
fontSize: screenWidth / 8.57142857143,
color: AppTheme().nameTextColor,
fontWeight: FontWeight.bold)),
),
]),
),
Container(
height: screenHeight / 2.3,
alignment: Alignment.bottomCenter,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
Container(
margin: EdgeInsets.fromLTRB(
buttonMargin * 1.5,
buttonMargin * 3,
buttonMargin * 1.5,
buttonMargin * 3),
height: MediaQuery.of(context).size.height / 2,
width: MediaQuery.of(context).size.width / 2,
child: RaisedButton(
color: AppTheme().backgroundSecondary,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.settings,
color: Colors.white,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Settings',
style: TextStyle(color: Colors.white),
),
)
],
),
onPressed: () {
setState(() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return MySettings();
}),
);
},
);
},
),
),
],
),
),
],
),
),
),
),
);
}
}
settings.dart
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
void settings() {
runApp(MySettings());
}
class AppTheme {
var backgroundMain = Colors.red;
var backgroundSecondary = Colors.teal;
var backgroundAvatar = Colors.white;
var nameTextColor = Colors.white;
var professionTextColor = Colors.red[100];
var contactTextColor = Colors.teal;
var testPrint = print("hi");
String name = 'John Doe';
String nameFont = 'Pacifico';
String professionFont = 'roboto';
}
class MySettings extends StatefulWidget {
#override
MySettingsState createState() => MySettingsState();
}
class MySettingsState extends State<MySettings> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
backgroundColor: AppTheme().backgroundMain,
title: Text('Settings'),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
setState(() {
AppTheme().backgroundMain = Colors.yellow;
print(AppTheme().name);
Navigator.pop(context);
});
},
),
),
backgroundColor: Colors.teal,
body: SafeArea(
child: ListView(
children: <Widget>[
Container(
child: TextField(
onChanged: (text) {
setState(() {
AppTheme().name = text;
});
},
),
),
Container(
child: TextField(),
),
],
)),
),
);
}
}
1 Make sure to properly import
Flutter/Dart Static variables lost / keep getting reinitialized
2 You cannot alter the variables in the class. You must create an object and alter the object.
i.e I was doing
Class MyClass {
var myVariable = someValue
}
MyClass.myVariale = aDifferentValue
This did not update MyClass
What I needed to do worked once I created an object
var myClassObject = new Myclass();
myClassObject.myVariable = aDifferentValue
Now I just alter and call on myClassObject.

Flutter: Change height of a container when a button is pressed

The problem here is that the Container doesn't redraw itself after changing the funArg value with the press of a button which should change its height since it's used in its calculation
here is the code :
here is main.dart:
import 'package:flutter/material.dart';
import 'package:sqaure/ui/fun.dart';
Widget rect0;
String rectArg = "20";
class Home extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new HomeState();
}
}
class HomeState extends State<Home> {
var list = ["20", "15"];
Widget funTest() {
setState(() {
rectArg = list[1];
rect0 = new Plate(rectArg);
});
}
//final Color primaryColor = Colors.red;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("rect"),
backgroundColor: Colors.red,
),
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Container(
alignment: Alignment.topCenter,
color: Colors.white,
height: 245.0,
child: new Stack(
children: <Widget>[
Center(
child: Padding(
padding: const EdgeInsets.only(left: 55.0),
child: Row(
children: <Widget>[
//plates
rect0 = new Plate(rectArg),
],
),
),
)
],
),
),
new RaisedButton(onPressed: () {
funTest();
debugPrint(rectArg);
})
],
),
);
}
}
and here is fun.dart:
import 'package:flutter/material.dart';
class Plate extends StatefulWidget {
final String funArg2;
#override
State<StatefulWidget> createState() {
return new PlateState(funArg2);
}
[enter image description here][1]
Plate(this.funArg2);
}
class PlateState extends State<Plate> {
String funArg;
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(3.0),
child: Container(
alignment: Alignment.center,
color: Colors.redAccent,
height: funArg != "" ? (9.33 * double.parse(funArg) + 45) : 0.0,
width: 29.0,
child: new Text(
funArg,
style: new TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: funArg.length > 4
? 10.0
: funArg.length > 3 ? 14.0 : 19.0,
),
),
));
}
PlateState(this.funArg);
}
as you can see the height of the container is determined by the child text inside.
screenshot
thank you.
Here is a fixed and commented version of your code. Please read the comments!
The main problem is that you defined Plate as a stateful widget, and stored rectArg in the state! PlateState is only initiated once, until you leave the screen, it's not recreated when the parent widget is rebuilt!
Plate actually doesn't have any internal state, so it should be a StatelessWidget. You should always prefer StatelessWidgets. Understanding why is fundamental for Flutter development!
import 'package:flutter/material.dart';
main() => runApp(MaterialApp(home: Home()));
class Home extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new HomeState();
}
}
// this seems to be a constant, so can put it outside of the class
// or alternatively inside, with "static const" modifier
const list = ["20", "15"];
class HomeState extends State<Home> {
// stateful variables (things that change over time)
// must be inside of your state class
String rectArg = "20";
// we can return void here!
void funTest() {
setState(() {
// state is modified here. this triggers a rebuild/redraw
// that means the build function is called again
// note that we are only the storing the string value, NOT a widget!
rectArg = list[1];
});
}
// this is called every time you setState
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("rect"),
backgroundColor: Colors.red,
),
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Container(
alignment: Alignment.topCenter,
color: Colors.white,
height: 245.0,
child: new Stack(
children: <Widget>[
Center(
child: Padding(
padding: const EdgeInsets.only(left: 55.0),
child: Row(
children: <Widget>[
// DO NOT SET VARIABLES FROM THE BUILD METHOD!
// this is bad:
// rect0 = new Plate(rectArg),
Plate(
funArg: rectArg,
),
],
),
),
)
],
),
),
new RaisedButton(onPressed: () {
funTest();
debugPrint(rectArg);
})
],
),
);
}
}
// Plate is actually a StatelessWidget because it is not interactive and holds no internal state
// All the data (funArg) is passed in from the parent ==> StatelessWidget
// Always prefer stateless widgets!
// That means the widget is completely rebuilt every time the build() method is called in HomeState
class Plate extends StatelessWidget {
// Use named constructor parameters and call the super constructor!
// you can auto-generate the constructor with Android Studio
const Plate({Key key, this.funArg}) : super(key: key);
final String funArg;
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(3.0),
child: Container(
alignment: Alignment.center,
color: Colors.redAccent,
height: funArg != "" ? (9.33 * double.parse(funArg) + 45) : 0.0,
width: 29.0,
child: new Text(
funArg,
style: new TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: funArg.length > 4 ? 10.0 : funArg.length > 3 ? 14.0 : 19.0,
),
),
),
);
}
}
Just in case that you need a StatefulWidget with internal state that also has constructor parameters set by the parent widget (which is quite common): Inside the build method of your State, use the widget property to access the final fields of your widget:
class ColoredCheckbox extends StatefulWidget {
const ColoredCheckbox({Key key, this.color}) : super(key: key);
// this is passed in from the parent, can change when the parent is rebuilt
final Color color;
#override
State<StatefulWidget> createState() => ColoredCheckboxState();
}
class ColoredCheckboxState extends State<ColoredCheckbox> {
// this is internal state, kept even when the parent is rebuilt
bool checked = false;
// build is called when:
// - you call setState from this widget
// - when the parent widget is rebuilt
#override
Widget build(BuildContext context) {
return RaisedButton(
child: Text(checked ? 'X' : '0'),
// use "widget" to access the fields passed in from the parent
color: widget.color,
onPressed: () {
// always call setState when changing internal state
setState(() {
checked = !checked;
});
},
);
}
}

Categories

Resources