How to change a state of a widget that is generated from list in flutter - flutter

My problem is simple. I was building a ListTile from the list of documents I get from firebase by iterating through the results. The ListTile contains a leading icon, a title, and a trailing favorite IconButton. The list tile shows perfectly as I want it to. But the problem arises when I try to change the color of the IconButton while a user taps on it. For some reason, the code I wrote isn't doing the trick. What i tried to do was to set the value of the IconButton's color by a ternary which uses a class variable named isFavorited. What i wanted it to do is change the color of the IconButton when i tap on that same IconButton. Here is my code block:
// Builds a tile for each brought up names of taxistops
if (retrievedData.isNotEmpty) {
retrievedData.forEach((element) {
if (element.contains(query) ||
element.contains(query.toUpperCase()) ||
element.contains(query.toLowerCase())) {
ListTile listTile = ListTile(
leading: Icon(Icons.local_taxi),
title: Text(element),
trailing: IconButton(
icon: isFavorited
? Icon(
Icons.star,
color: Colors.amber[400],
)
: Icon(Icons.star_border),
onPressed: () => {
setState(() {
isFavorited = true;
}),
addToFavorite()
},
),
onTap: () => close(context, element),
);
searchedTiles.add(listTile);
}
});
}
Any help is appreciated! Thank you in advance!

I think the problem is because you are adding the widget in the list you should preview it directly inside the widget father (ListView) so it can do the setState correctly and not inside a list you created as a data structure to store elements.
I think that's the issue if it still doesn't work I would need to see how you are showing the list.

Related

How do i modify the data of an existing variable in flutter?

I want to make an editable TextWidget in flutter but I don't really know how to go around it, I did some research, but still can't find a good solution.
Here's my sample code below.
I have a variable called
int qty = 1;
and so I called the variable in TextWidget
Column(
children: [
Text(
"${qty}",
style: TextStyle(),
)
],
),
I want to have these features that make user tab on the value to change it if they want, upon tap, a pop-up dialog will show to give the user the ability to change the existing value to whatever the user wants.
Please if anyone knows how, please help.
You will need a statfull widget to call setState and make the UI update with the new value stored in your qty variable. (I'am assuming that you are not using any state managment).
I wrote a possible solution for what you need.
Let look into some considerations:
Text will show whatever is in the qty as long we call setState after (or do it inside) we change the value of qty.
You need some widget to detect your tap. If you want to the text be 'clicable' then it should be wraped inside that widget.
The onTap/onPress call back of that widget should show a new widget. For this you can use the already made showDialog() and pass it a Dialog Widget. in here you will put your ui for that.
In some point of that UI you need to introduce the new value. So you can use a simple TextField that will save the introduced value, where you can assign it to qty, without forgetting to call setState! Note that it deal with strings, so you neet to do an int.parse() ou double.parse accordingly to you qty var type.
And I think that's it.
The could be other ways of doing it. This is a good and simple approach for your need.
I wrote a piece of code to help or somelse how is trying to do it:
InkWell(
// can be gesture detector, button, etc
onTap: () => showDialog(
context: context,
builder: (context) => Dialog(
child: Container(
color:
Colors.white60, // change it accordingly to you
height: 80, // change it accordingly to you
width: 200, // change it accordingly to you
child: Column(
children: [
const Text('Change your value here'),
TextField(
decoration:
InputDecoration(hintText: qty.toString()),
onChanged: (insertValue) => setState(() {
qty = int.parse(insertValue);
}),
// you can use other callBack function (like onComplete,
// onSaved), wich is more eficient than calling setState eveytime,
// but you have to do the needed adtaptions. Like onSave
// needs a key to call the save function. is easy just google it.
),
],
)),
)),
child: Text(
"${qty}",
),
),
What you are probably looking is a DropdownButton.
You would have something like this:
int qty = 1;
List<int> listOfValues = [1,2,3,4];
and then in your column you would have
DropdownButton<int>(
// This are the list of items that will appear in your dropdown menu.
// items is all the options you want your users to be able to select from,
// and it take a list of `DropdownMenuItem`. So instead of creating a `DropdownMenuItem`
// for each of the items in `listOfValues`, we iterate through it and return
// a `DropdownMenuItem`
items: listOfValues
.map((item) => DropdownMenuItem<int>(
value: item,
child: Text('$item'),
))
.toList(),
value: qty,
onChanged: (value) {
if (value != null) {
setState(() {
qty = value;
});
}
},
),
For more information on DropDownButton, check the following links:
https://api.flutter.dev/flutter/material/DropdownButton-class.html
https://www.youtube.com/watch?v=K8Y7sWZ7Q3s
Note: In a scenario where you want to increase the quantity of an item, like in a shopping cart, maybe having a button increment qty by 1 would be better.

Flutter- how to disable a Button untill the requireed fields are filled

I want to disable a button untill all the required fields are filled. I found similar questions here but all answers are based on making the onPressed property to null. But it does not disable the tap effect.
I want something like this. On disabled mode, clicking on the button won't even cause a Tap effect. Can someone help with a solution?
For a limited number of widgets, you can wrap them in a widget IgnorePointer: when its ignoring property is set to true, the sub-widget (actually, the entire subtree) is not clickable.
IgnorePointer(
ignoring: true, // or false
child: CustomButton(
onPressed: _login,
child: Text("Login"),
),
)
checkout this widget.set absorb to true when the required field is empty or is not validated.
https://api.flutter.dev/flutter/widgets/AbsorbPointer-class.html
In the textField add
onChanged : (val){ setastate((){});}
You mustbe already having a TextEditingController for the textfield. For example i shall name it phoneTextController.
Now in the button check the condition
phoneTextController.text.length > 10
For example
Inkwell(
onTap:(){
if(phoneTextController.text.length > 10){
SendOtp()
}
}
child: Container(
color: phoneTextController.text.length > 10 ? Colors.blue : Color.blue.withOpacity(0.5),
)
)
If you're using one of Flutter's built-in button Widgets, setting the onTap to null should automatically disable the button and its effect. Now all that remains is to conditionally do this during your build. Say, like the other answer, your TextEditingController is named phoneTextController:
ElevatedButton(
child: Text('Next'),
onTap: phoneTextController.text.length > 10 ? () => goToNextPage() : null,
),
Which will automatically enable the button and add the callback as soon as the condition is met (in this example, the input length is >10.

Build interactive flutter UI with class methods

I have a ListView of projects where each ListTile represents a project. For that I have a List of projects which is written into the ListView with a for loop. Each ListTile has an IconButton to define a project as favourite. To keep the code clean I defined methods inside the project class to build the UI. The reason behind that is that the project can be set as a favourite from different locations in the application.
Unfortunately I cannot get the application to update the UI depending on the favorite status. I suppose it has something to do with the return of the Widgets, as the state gets evaluated once on return and does not change until refresh.
I already extended the Project class with a ChangeNotifier without any result. Down below the code.
ListView(
children: [
for (var project in projects.projects) project.getListTile()
],
),
Project class with getListTile Method():
class Project extends ChangeNotifier{
bool favorite = false;
Project({this.favorite = false});
IconButton getFavoriteButton() {
return IconButton(
icon: favorite
? Icon(Icons.favorite_outlined)
: Icon(Icons.favorite_border_outlined),
color: favorite ? Colors.yellow : Colors.white,
onPressed: () {
favorite = !favorite;
notifyListeners();
},
);
}
ListTile getListTile() {
return ListTile(
leading: getFavoriteButton(),
),
);
}
}
Is there a way to refresh the UI, so that the favorite icon type and color updates on pressing the IconButton?
You should be using a StatefullWidget implementation.
These widgets are made to store a state and rebuild automatically on any state change when you do :
IconButton(
icon: favorite
? Icon(Icons.favorite_outlined)
: Icon(Icons.favorite_border_outlined),
color: favorite ? Colors.yellow : Colors.white,
onPressed: () {
setState((){
favorite = !favorite;
}
},
);

How do I save different Textfield values without creating endless TextEditingController

I am working on a ToDo app an am trying to create five Text() widgets that hold the value of one TextformField(). I don't want to create lots of controllers, because the user is able to add more ToDo-fields through the press of a button. I always encounter the problem, that all Text() widgets hold the same string. Does anyone have an idea how I could store different strings through one TextEditingController and pass it to different Text() widgets in a compact code?
You can see the concept and problem below.
Thank you for helping me out!
The best solution is to create a widget, which is responsible for one TODO row (contains one Text widget and one TextEditingController). Then, instead of adding a Text widget and a TextEditingController, you can add a new instance of your new widget to the list of fields on the screen.
You can check this code.
TextEditingController test = TextEditingController();
Column(
children: [
TextField(
controller: test,
),
RaisedButton(
child: Text("abc"),
onPressed: () {
setState(() {
test.text = test.text + "abc";
});
},
),
RaisedButton(
child: Text("efg"),
onPressed: () {
setState(() {
test.text = test.text + "efg";
});
},
)
],
)

Flutter identify Widget built by loop or listview.builder

I can't get around this issue, how do I select Widget built by for loop or a ListView.builder?
In code I will post, I have buttons built by for loop,
I want to change button's background color on click.
I'm using Provider in my project so this function is implemented with Provider.
In my Provider class I made a boolean and a function to switch that boolean, and then I assigned it to buttons, and set it's color to value of that boolean with ternary operator.
While this somewhat works, the problem is... All of the buttons built by the for loop are affected,
meaning. I press one button, all of them change color.
How do I fix this?
My widget which includes mentioned buttons:
ListView(
children: [
for(int i = 0; i < recipeIngredients.length; i++)
IngredientsTab(
ingredientText: recipeIngredients[i],
textColor: categoryAndSeeAllColor,
width: width,
**buttonColor: provider.isButtonPressed ? accentColor : Colors.grey[100],**
buttonIcon: Icons.add,
buttonText: 'Add to cart',
buttonTextColor: accentColor,
**buttonAction: provider.changeBackground,**
),
],
),
In my Provider class:
bool isButtonPressed = false;
void changeBackground() {
isButtonPressed = isButtonPressed ? false : true;
notifyListeners();
}
}
While I understand this happens for obvious reason, I'm assigning this isButtonPressed to all buttons so when its true they all change... So that makes sense, I just don't know how to solve it, I'm also having similar issue in another project where items are built with ListView.builder...
So if someone would help me with this would mean a lot to me :)
Usually you have in the Provider a List of Ingredients.
Each Ingredient have an ID and a bool selected variable.
The buttonAction calls the method with the ID:
void changeBackground(int ingredientID) {
ListOfIngredients.singleWhere((elem)=>elem.ID==ingredientID)).isButtonPressed = !isButtonPressed;
notifyListeners();
}
Then you build the ListView using the List of Ingredients and for each Ingredient you check the isButtonPressed value.