Call method from onPressed of an IconButton - flutter

I have an IconButton and in the onPressed() method I want to call a void method that receives an incremental number as a parameter when the Button is clicked, then when obtaining this value it must call another void which calculates the days of weeks.
I have tested the function and if I put the values ​​in hard it does work, however when I put it in the widget it always returns the same value and does not change, no matter how much I put the setState method, I don't know how to increase said value.
Here I attach part of the code:
import 'package:intl/intl.dart';
import 'package:flutter/material.dart';
class ExampleScreen extends StatefulWidget {
final String? text1;
final String? text2;
final String? text3;
const ExampleScreen({Key? key,
this.text1,
this.text2,
this.text3}
) : super(key: key);
#override
State<ExampleScreen> createState() => _ExampleScreenState();
}
class _ExampleScreenState extends State<ExampleScreen> {
int counter = 1;
void increase(){
counter++;
setState(() {
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.fromLTRB(10,0,0,0),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
_calendar(widget.text1,widget.text2,widget.text3,counter)
],
),
//),
),
)
);
}
}
_calendar(text1,text2,text3, int counter) => Container(
width: 250,
height: 50,
margin: const EdgeInsets.fromLTRB(50,0,0,0),
alignment: Alignment.center,
child: Wrap(
children:[
Container(
height: 225,
width: 225,
child: Column(
children: [
const SizedBox(height: 10,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
IconButton(
onPressed: () {
//When I call the function increase it tells me that the function is not declared
calculation_days(counter);
print('amount : $counter');
},
icon:const Icon(Icons.arrow_back_ios_new_outlined,size: 10)
),
const Text('Title',style: TextStyle(fontSize: 12)),
IconButton(
onPressed: () {},
icon:const Icon(Icons.arrow_forward_ios_outlined,size: 10)
),
],
),
]
),
),
]
)
);
void calculation_days(int week)
{
int firstday;
List<String> days;
List<String> daysxWeek;
days=findPreviousSaturdays1(count: week);
firstday= int.parse(days[week-1]);
daysxWeek=[firstday.toString()];
daysxWeek.add(firstday.toString());
}

since you declare it inside the StatefulWidget, not the State, you need to access it inside your onPressed method like this:
widget.increase()
IconButton(
onPressed: () {
widget.increase(); // call it like this.
},
icon:const Icon(Icons.arrow_back_ios_new_outlined,size: 10)
);
Another solution is that you can move your increase() from StatefulWidget to the State class, then you can use it by calling increase() instead of widget.increase().

Related

Multiple Instance with GetX tag not working in flutter

Ive been working on a multiple instance project. so i tried doing this in the counter app.
but only first instance is updating. below is my all code.
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
InstanceModel selected = InstanceModel.instances.first;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: SizedBox(
height: 50,
child: Row(
children: [
const SizedBox(
width: 50,
),
Expanded(child: Container()),
Expanded(
flex: 3,
child: ListView.separated(
itemCount: InstanceModel.instances.length,
scrollDirection: Axis.horizontal,
separatorBuilder: (c, i) => const SizedBox(
width: 50,
),
itemBuilder: (context, i) {
final InstanceModel instance =
InstanceModel.instances[i];
final isSelected = selected == instance;
return SizedBox(
width: 80,
child: ElevatedButton(
onPressed: () {
selected = instance;
setState(() {});
},
style: ElevatedButton.styleFrom(
backgroundColor:
isSelected ? Colors.green : Colors.black,
),
child: Text(instance.name),
),
);
}),
),
],
),
),
),
const SizedBox(
height: 20,
),
const Text(
'You have pushed the button this many times:',
),
GetBuilder<InstanceController>(
tag: selected.name,
builder: (_) {
return Text(
'${selected.controller.count}',
style: Theme.of(context).textTheme.headline4,
);
}),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
selected.controller.increment();
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
Model class
import 'package:get/get.dart';
import 'package:train/instance_controller.dart';
class InstanceModel {
final InstanceController controller;
final String name;
InstanceModel({
required this.controller,
required this.name,
});
static List<InstanceModel> instances = [
InstanceModel(
name: "1",
controller:
Get.put<InstanceController>(InstanceController(), tag: "1")),
InstanceModel(
name: "2",
controller:
Get.put<InstanceController>(InstanceController(), tag: "2")),
];
}
and controller class
import 'package:get/get.dart';
class InstanceController extends GetxController {
var count = 0.obs;
void increment() {
count++;
update();
}
}
only calling setstate is updating the second instance. i dont understand where im wrong.
any help will be appreciated---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
I really was confused at first that this might be some bug since logically your code is right, then I noticed that this about Flutter widgets replacing each by other on new state rather than completely removing that widget then adding it again in the widget tree.
on update state ( setState(() {}) ), flutter searches first for widgets with the same type to replace each with other, in this situation, when you're expecting that GetBuilder should trigger state for each separated unique controllers, flutter just update one with the other with saving it's state.
And to fix it, you should simply add a Key to the GetBuilder to tell flutter that each of it is unique like this:
GetBuilder<InstanceController>(
key: UniqueKey(), // add this
tag: selected.name,
builder: (_) {
return Text(
'${selected.controller.count}',
style: Theme.of(context).textTheme.headline4,
);
}),

How to change the colour of button on press to green and other buttons in row back to neutral

The idea behind this is i would like users to select their sex, sexual orientation and relationship status. So there would be a button in the first row saying "Male" another one saying "female" and another one saying "other". Below that there would be 3 other buttons saying "straight" "gay/lesbian" and "other" and below that another row with 3 buttons (options). Once the user clicks a button the button toggles on essentially and it goes from e.g black to green. I created the boolean toggleOn and will be setting to true or false on button press. The issue is right now i am getting an error on the screen and i think it is because i am going over the available pixels and singleChildScrollView does not work. Any ideas?
Also i am sorry if you are cringing right now i am just making a personal project to learn to code.
This is the screen where i would like to have 3 rows with 3 buttons each
import 'package:./components/rounded_button.dart';
import 'package:./screens/register_screen.dart';
import 'package:flutter/material.dart';
class UserRegisterPreferences extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Scaffold(
backgroundColor: Color(0xff1e1e1e),
body: Body(),
),
);
}
}
class Body extends StatefulWidget {
#override
_BodyState createState() => _BodyState();
}
class _BodyState extends State<Body> {
#override
Widget build(BuildContext context) {
bool toggleOn = false;
Size size = MediaQuery.of(context).size;
return Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
verticalDirection: VerticalDirection.down,
children: <Widget>[
Container(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
roundedButton(
press: () => setState(() => toggleOn = !toggleOn),
),
roundedButton(
press: () => setState(() => toggleOn = !toggleOn),
),
roundedButton(),
],
),
),
),
Column(
children: <Widget>[
roundedButton(),
roundedButton(),
roundedButton(),
],
),
Column(children: <Widget>[
roundedButton(),
roundedButton(),
roundedButton(),
]),
],
);
}
}
This is the file that contains the button so i can keep reusing on the app
import 'package:./constants.dart';
import 'package:flutter/material.dart';
// ignore: camel_case_types
class roundedButton extends StatelessWidget {
final String text;
final Function press;
final Color color, textColor;
const roundedButton({
Key key,
this.text,
this.press,
this.color = kPrimaryColor,
this.textColor = Colors.white,
}) : super(key: key);
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container(
margin: EdgeInsets.symmetric(vertical: 10),
width: size.width * 0.7,
child: ClipRRect(
borderRadius: BorderRadius.circular(29),
child: TextButton(
style: TextButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 20, horizontal: 40),
primary: Colors.white,
backgroundColor: kPrimaryColor,
),
onPressed: press,
child: Text(text),
),
),
);
}
}

setState not updating

I just can't figure out what is the problem with this set state method in flutter. Everything seems okay. But the text is not updating on onPressed.
class NetBalanceWidget extends StatefulWidget {
#override
_NetBalanceWidgetState createState() => _NetBalanceWidgetState();
}
class _NetBalanceWidgetState extends State<NetBalanceWidget> {
#override
Widget build(BuildContext context) {
String text = 'NetBalance-Amount';
return RawMaterialButton(
onPressed: () {
setState(() {
text = 'It works';
});
},
child: Container(
height: 80.0,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(text),
Text('0.00'),
],
),
),
),
);
}
}
You have text as a local variable in the build method. setState is essentially just calling build again, and is resetting the value of text back to its default of 'NetBalance-Amount'.
Move its declaration outside of build:
class _NetBalanceWidgetState extends State<NetBalanceWidget> {
String text = 'NetBalance-Amount';
#override
Widget build(BuildContext context) {
return RawMaterialButton(
onPressed: () {
setState(() {
text = 'It works';
});
},
child: Container(
height: 80.0,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(text),
Text('0.00'),
],
),
),
),
);
}
}

Listview scrolling and selecting Textfield afterwards is freezing my app

I am using the package
country_code_picker: ^1.4.0
https://pub.dev/packages/country_code_picker#-installing-tab-
with flutter 1.17.3
Which is pretty much one of the only country code picker packages. But I have one serious problem an I don't have a clue what it could be.
When I run this code
import 'package:flutter/material.dart';
import 'package:country_code_picker/country_code_picker.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
App();
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: TestWidget(),
);
}
}
class TestWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(body: _buildCountryPicker(context));
}
Widget _buildCountryPicker(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Center(
child: CountryCodePicker(
initialSelection: 'NL',
),
),
);
}
}
And I open the dialog to select a country. I scroll in the list and then select the TextField my keyboard opens and when I try to type something my entire app freezes. I can't even hot reload. I don't get a single error.
I am running this on my Huawei P30, but I also experience this on other android devices. I don't know if this is a flutter bug or a country code picker bug.
I think it is probably in this widget somewhere. If anyone could point me in the right direction it would help me alot!
class SelectionDialog extends StatefulWidget {
final List<CountryCode> elements;
final bool showCountryOnly;
final InputDecoration searchDecoration;
final TextStyle searchStyle;
final TextStyle textStyle;
final WidgetBuilder emptySearchBuilder;
final bool showFlag;
final double flagWidth;
final Size size;
final bool hideSearch;
/// elements passed as favorite
final List<CountryCode> favoriteElements;
SelectionDialog(
this.elements,
this.favoriteElements, {
Key key,
this.showCountryOnly,
this.emptySearchBuilder,
InputDecoration searchDecoration = const InputDecoration(),
this.searchStyle,
this.textStyle,
this.showFlag,
this.flagWidth = 32,
this.size,
this.hideSearch = false,
}) : assert(searchDecoration != null, 'searchDecoration must not be null!'),
this.searchDecoration =
searchDecoration.copyWith(prefixIcon: Icon(Icons.search)),
super(key: key);
#override
State<StatefulWidget> createState() => _SelectionDialogState();
}
class _SelectionDialogState extends State<SelectionDialog> {
/// this is useful for filtering purpose
List<CountryCode> filteredElements;
#override
Widget build(BuildContext context) => SimpleDialog(
titlePadding: const EdgeInsets.all(0),
title: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
IconButton(
padding: const EdgeInsets.all(0),
iconSize: 20,
icon: Icon(
Icons.close,
),
onPressed: () => Navigator.pop(context),
),
if (!widget.hideSearch)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextField(
style: widget.searchStyle,
decoration: widget.searchDecoration,
onChanged: _filterElements,
),
),
],
),
children: [
Container(
width: widget.size?.width ?? MediaQuery.of(context).size.width,
height:
widget.size?.height ?? MediaQuery.of(context).size.height * 0.7,
child: ListView(
children: [
widget.favoriteElements.isEmpty
? const DecoratedBox(decoration: BoxDecoration())
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...widget.favoriteElements.map(
(f) => SimpleDialogOption(
child: _buildOption(f),
onPressed: () {
_selectItem(f);
},
),
),
const Divider(),
],
),
if (filteredElements.isEmpty)
_buildEmptySearchWidget(context)
else
...filteredElements.map(
(e) => SimpleDialogOption(
key: Key(e.toLongString()),
child: _buildOption(e),
onPressed: () {
_selectItem(e);
},
),
),
],
),
),
],
);
Widget _buildOption(CountryCode e) {
return Container(
width: 400,
child: Flex(
direction: Axis.horizontal,
children: <Widget>[
if (widget.showFlag)
Flexible(
child: Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Image.asset(
e.flagUri,
package: 'country_code_picker',
width: widget.flagWidth,
),
),
),
Expanded(
flex: 4,
child: Text(
widget.showCountryOnly
? e.toCountryStringOnly()
: e.toLongString(),
overflow: TextOverflow.fade,
style: widget.textStyle,
),
),
],
),
);
}
Widget _buildEmptySearchWidget(BuildContext context) {
if (widget.emptySearchBuilder != null) {
return widget.emptySearchBuilder(context);
}
return Center(
child: Text('No country found'),
);
}
#override
void initState() {
filteredElements = widget.elements;
super.initState();
}
void _filterElements(String s) {
s = s.toUpperCase();
setState(() {
filteredElements = widget.elements
.where((e) =>
e.code.contains(s) ||
e.dialCode.contains(s) ||
e.name.toUpperCase().contains(s))
.toList();
});
}
void _selectItem(CountryCode e) {
Navigator.pop(context, e);
}
}
Also filed an issue on the flutter github https://github.com/flutter/flutter/issues/59886
Edit:
I have a video of it right here
https://www.youtube.com/watch?v=669KitFG9ek&feature=youtu.be
I just had to remove the keys, so there probably was a duplicate key
...filteredElements.map(
(e) => SimpleDialogOption(
//key: Key(e.toLongString()),
child: _buildOption(e),
onPressed: () {
_selectItem(e);
},
),
),

Flutter: Calling SetState() from another class

I am trying to make a simple image that appears or disappears when a button is pushed. This button resides in a separate class to the image, so in Flutter this creates a massive headache of an issue.
I have read many forums on this and I have tried all the solutions posed but none of them are working for me.
What I am trying to do:
class SinglePlayerMode extends StatefulWidget {
#override
SinglePlayerModeParentState createState() => SinglePlayerModeParentState();
}
class SinglePlayerModeParentState extends State<SinglePlayerMode> {\
bool coinVisible = false;
toggleCoin() {
setState(() {
coinVisible = !coinVisible;
});
}
Widget topMenuRow() {
return Stack(
children: [
Column(
children: [
coinVisible == true ?
Padding(
padding: EdgeInsets.all(50),
child: Container(
height: 60,
width: 60,
color: Colors.blueGrey[0],
decoration: BoxDecoration(
color: Colors.blueAccent,
image: DecorationImage(
image: ExactAssetImage('lib/images/coin_head.jpg'),
fit: BoxFit.cover,
),
),
),
) : Container(
height: 60,
width: 60,
color: Colors.black,
),
],
),
],
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
child: ListView(
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
children: [
topMenuRow(),
SizedBox(height: 40),
],
),
),
);
}
And this is the separate class which I would like to trigger the SetState() on coinVisible from:
class dropDownMenu extends StatefulWidget { #override
_dropDownMenuState createState() => _dropDownMenuState();
}
class _dropDownMenuState extends State<dropDownMenu> {
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget> [
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Container(
child: Opacity(
opacity: 0.0,
child: FloatingActionButton(
heroTag: null,
onPressed: (){
//SOMEHOW CALL SetState() ON coinVisble HERE!
},
),
),
);
}
}
But nothing I have tried is working, and I have lost hours.
It simple, you need to send your SinglePlayMode::toggleCoin function as callback to dropDownMenu class.
class dropDownMenu extends StatefulWidget {
final _callback; // callback reference holder
//you will pass the callback here in constructor
dropDownMenu( {#required void toggleCoinCallback() } ) :
_callback = toggleCoinCallback;
#override
_dropDownMenuState createState() => _dropDownMenuState();
}
class _dropDownMenuState extends State<dropDownMenu> {
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget> [
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Container(
child: Opacity(
opacity: 0.0,
child: FloatingActionButton(
heroTag: null,
onPressed: (){
widget?._callback(); // callback calling
},
),
),
);
}
}
Then when you create a dropDownMenu class instance in your SinglePlayerMode class you will do
dropDownMenu(
toggleCoinCallback: toogleCoin,
);