I have made an app, that basically suggests some music artists and some of their top songs to the user. I want to add a feature that would allow the user to mark some songs as their favorite and I also want to show all the marked as favorite songs on a separate screen. In my flutter project, I have created a class Data in a file "data.dart" which has all the data that all the screens use. In "data.dart", I have a list of Map<String, Object> that has all the songs and each Map has a 'favorite' key which is initially set to false.
var allSongs = [
...
{
'url':
'https://open.spotify.com/track/3E6iea9uEmB7gRru4lyP6h?si=b062300e24cf47d8',
'name': 'Stop this train',
'time': '4:45',
'image':
'https://imagesvc.meredithcorp.io/v3/mm/image?url=https%3A%2F%2Fstatic.onecms.io%2Fwp-content%2Fuploads%2Fsites%2F20%2F2017%2F01%2Fjohn-mayer-wave-one-2000.jpg',
'id': 'jm',
'favorite': false,
},
...
];
I have added a button below each song Widget that is supposed to allow the user to mark the song as their favorite. I call addToFavs(val) (a function) that is called whenever the button is pressed.
InkWell(
onTap: () {
addToFavs(val);
},
child: Icon(
Icons.favorite,
color: color,
size: 30,
),
),
In addToFavs(), I want to access the list allSongs in Data class, and I want to change the value of 'favorite' key for the specific song that user has selected.
This is how my addToFavs() function looks like
void addToFavs(Map<String, Object> info) {
setState(() {
//here I am finding the index of the song that the user wants to mark as favorite
int index = Data().allSongs.indexWhere((element) {
return info['name'] == element['name'];
});
if (Data().allSongs[index]['favorite'] == false) {
Data().allSongs[index]['favorite'] = true;
color = Colors.red;
} else {
Data().allSongs[index]['favorite'] = false;
color = Colors.white;
}
});
}
And then when I go to my favorites screen, I finding all the Maps that have favorite key as true in allSong list.
var favorites = Data().allSongs.where((val) {
return val['favorite'] == true;
});
but I don't see the songs that I have marked as favorite. I think the data is being temporarily changed in the Data class and when I go to favorite screen the data is set to what it was before.
How do I fix this issue?
I think when you call Data(), a new instance of Data is returned. Instead of doing this way, you can declare your allSongs variable static. This way you can call it like this : Data.allSongs (Notice Data without parenthesis).
static var allSongs = [
...
{
'url':
'https://open.spotify.com/track/3E6iea9uEmB7gRru4lyP6h?si=b062300e24cf47d8',
'name': 'Stop this train',
'time': '4:45',
'image':
'https://imagesvc.meredithcorp.io/v3/mm/image?url=https%3A%2F%2Fstatic.onecms.io%2Fwp-content%2Fuploads%2Fsites%2F20%2F2017%2F01%2Fjohn-mayer-wave-one-2000.jpg',
'id': 'jm',
'favorite': false,
},
...
];
But unless you store your data in a database or a shared_preference, changes made to your data will be lost on next launch of the app.
Related
I am building a Flutter app that allows users to create locations and then have those locations uploaded to a database and shown to other users on a map. I have gotten the database and displaying of locations to work properly, but I want the map, a Google Map, to dynamically reload after the user has created a location and is returned to the map screen.
Right now, I am using Firebase Realtime Database to listen for changes and trying to update the map accordingly after the user has clicked on the button to create a new location. Here is the code for that:
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const LocationCreation()),
).then((valuef) {
locationsRef.onValue.listen((DatabaseEvent event) {
final data = event.snapshot.value;
if (event.snapshot.value != null) {
allMarkers = {};
final map = data as Map<dynamic, dynamic>;
map.forEach((key, value){
print(value['name']);
allLocations.add(value);
// Code to create a Marker object called marker
}
);
setState(() {
allMarkers.add(marker);
});
});
}
});
I know this code is reached, because the print statement is being called and the correct names are being printed out, including the name of the newly created location. However, the map screen is not being updated with the new locations and their markers.
The Google Map is being built in the build function as so:
GoogleMap(
scrollGesturesEnabled: true,
onMapCreated: (onCreated),
initialCameraPosition: CameraPosition(
target: _center,
zoom: 11.0,
),
markers: allMarkers,
),
And here is the onCreated function:
print('here again');
mapController = controller;
final snapshot = await locationsRef.get();
final map = snapshot.value as Map<dynamic, dynamic>;
setState(() {
allMarkers.clear();
map.forEach((key, value) {
allLocations.add(value);
//Code to make a Marker called marker
allMarkers.add(marker);
});
});
The print statement is called once upon initial building of the app, but is not called again after a new location has been created and the user is returned to the map screen.
allMarkers is not being initialized within the build function, which I found was a common problem for others.
Each time I try to make a new location, the database will store the new location and the print statement within the listen for new database event will be called, but it seems that Flutter does not detect a change to allMarkers. I have also tried allMarker.clear(), but the problem is the same. I have also tried to move the call to setState(), but nothing has worked.
I have 2 lists one from phone (contacts list) and the other one from server.
What I'm trying to do is: that check server data are contain with numbers of the phone and show related buttons to user.
The issue is: that it always show else part (its always falling to false)
Code
Each part is commented for better understanding
List<Contact> contacts = []; // <-- list of contacts in phone
List serverContacts = []; // <-- the data that coming from server which is include phone numbers to be check against phone contacts
// This part is inside my ListTile as `trailing`
trailing: serverContacts.contains(contact.phones != null ? contact.phones!.elementAt(0).value:'') ?
TextButton(
child: Icon(Icons.chat, color: Colors.grey[600],),
onPressed: () {
//
},
) :
TextButton( // <-- it always show this part
child: Icon(Icons.sms, color: Colors.grey[400],),
onPressed: () {
//
},
),
And here is data that comes from server
{
"id":4,
"name":"John",
"username":"john",
"phone":"+41000000000", // <-- to be check against phone contacts
"photo":null
}
Any suggestions?
Solved
Here is how I done it:
String? contactPhone = contact.phones != null ? flattenPhoneNumber(contact.phones!.first.value.toString()):'';
final foundPeople = serverContacts.where((element) => element['phone'] == contactPhone);
Then
trailing: foundPeople.isNotEmpty ? ... : ...,
if you are trying to compare phone number it is better to map your list first because .contain method does not work with custom classes unless you define == operator for that class, so i suggest that you map your lists to List first
List<String> contactPhones = contacts.map((c)=>c.phone??"").toList()
List<String> serverPhones = serverContacts.map((c)=>c.phone??"").toList()
now based on what you what check above list for your conditions
So, I was working on this app from a tutorial and I got everything to work, but the part where the update stays persistent The ListTile items update when I press the button to update (onTap), but they change their values to original ones after I leave the page. Here is my code for update:
Future<int> update(Food food) async {
final db = await database;
return await db.update(
TABLE_FOOD,
food.toMap(),
where: "id = ?",
whereArgs: [food.id],
);
}
class UpdateFood extends FoodEvent {
Food newFood;
int foodIndex;
UpdateFood(int index, Food food) {
newFood = food;
foodIndex = index;
}
}
The update button updates the values of the ListTile items, but they do not stay persistent, they return to original values after I change the page and then return to the main one.
Here is the init state, maybe something is wrong with it:
void initState() {
super.initState();
DatabaseProvider.db.getFoods().then(
(foodList) {
BlocProvider.of<FoodBloc>(context).add(SetFoods(foodList));
},
I understand if the issue is not in one of these, but maybe a setState or initState, so here is a full Github code, it is a very short app, and it would probably take just a few minutes for someone to tell what the issue is:
https://github.com/cheetahcoding/CwC_Flutter/tree/sqflite_tutorial/lib
I think you miss to update the updated Food object into DB in food_form.dart
// This one is the updated one,
Food food = Food(
id: widget.food.id, // add this line
name: _name,
calories: _calories,
isVegan: _isVegan,
);
DatabaseProvider.db.update(food).then( // <= change "widget.food" to "food"
(storedFood) => BlocProvider.of<FoodBloc>(context).add(
UpdateFood(widget.foodIndex, food),
),
);
the DB update transaction is success but the values are the old one.
I want to make a dropdown list, in which one can select the country. The drop down list must be searchable, whenever we type a letter, the drop down list changes.
The drop down list is using a api/json data.
We are not making selection Manually.
you can use searchable_dropdown or dropdown_search packages
For API/JSON question :
check out the second package example in Readme
example
DropdownSearch<UserModel>(
label: "Name",
onFind: (String filter) async {
var response = await Dio().get(
"http://5d85ccfb1e61af001471bf60.mockapi.io/user",
queryParameters: {"filter": filter},
);
var models = UserModel.fromJsonList(response.data);
return models;
},
onChanged: (UserModel data) {
print(data);
},
);
I am currently working on a project with a team where I implemented a barcode scanner that is going to be a widget on a "search" page. The issue I am currently facing is that I use the barcode to generate a list of ingredients that I want to include in the search but I don't know how to return that information to the search page. I have tried two methods so far:
I tried creating a member variable of the scanner class that I would then access on the search page when I need it but because my function that returns the list is a part of the private state class that I am unsure how to access from the public class. This is the method I would prefer to solve.
I have tried using the Navigator class to push and pop the information from the seperate screens but the issue is the barcode scanner is automatically closed once a barcode is scanned so I can't pop from the stack or else it will leave the search page and go back to whatever page was previous.
Here is the code I have. The first is the function that opens the barcode scanner, scans a barcode and creates an ingredient list that I return. This 'test' list is what I'd ideally like to set as a class member for the public portion of the class not the private state class.
Future<void> scanBarcodeNormal(BuildContext context) async {
String barcodeScanRes;
List<String> test;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
barcodeScanRes = await FlutterBarcodeScanner.scanBarcode(
"#ff6666", "Cancel", true, ScanMode.BARCODE);
} on PlatformException {
barcodeScanRes = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return null;
//Call the backend with the barcode to return the Bread Crumb list
List<String> breadCrumbs = await BackendRequest.barcode("089836187635", "42e96d88b6684215c9e260273b5e56b0522de18e");
//If the backend does not return us anything this displays a popup
if(breadCrumbs == null){
showDialog(
context: context,
builder: (BuildContext context) => CustomDialog(
title: "Uh Oh",
description:
"Barcode not found in our database, please try entering the item manually",
buttonText: "Okay",
),
);
}
else{
setState(() {
_scanBarcode = breadCrumbs.toString();
});
//Check the breadcrumbs for usable ingredients
test = await getIngredients(breadCrumbs, "42e96d88b6684215c9e260273b5e56b0522de18e");
}
setState(() {
_itemName = test[0].toString();
});
//Navigator.pop(context, test);
}
Here is where I add my widget to the search page. All I do is add the widget and the build function within my scanner class handles onPressed() functionality.
decoration: InputDecoration(
labelText: "Input an Ingredient",
hintText: "Search",
prefixIcon: Icon(Icons.search),
suffixIcon: ScanButton(),
you can use provide consumer pattern in which you can put all this page into one parent theme child and than you can access variable and all other stuff.
https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple
other wise you can also use block patten
https://medium.com/flutterpub/architecting-your-flutter-project-bd04e144a8f1
or if you dont want to use that in that case you can make one common class and declare one static varible in that and set varible on result after scaning in completed and use when ever you want