I'm making a favorites list viewer for my application. I wanted to make so when the users haven't had anything added to their favorites, it will show a text like a "You have no favorites" of some sorts.
Widget build(BuildContext context) {
var favoriteBloc = Provider.of<FavoriteBloc>(context);
SizeConfig().init(context);
return Scaffold(
resizeToAvoidBottomInset: true,
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Expanded(
child: favoriteBloc.isEmpty?
const Center(
child: Text(
"Anda belum memiliki favorites",
style: TextStyle(
fontSize: 22.0,
fontWeight: FontWeight.bold
),
)
)
Since I'm using provider, i also have a favorites_provider.dart. I've defined isEmpty as null here since it says that the getter was not available for favoriteBloc.
import 'package:aplikasi_jurnal_mobile/models/journals.dart';
import 'package:flutter/cupertino.dart';
class FavoriteBloc with ChangeNotifier {
int _count = 0;
List<JournalModel> items = [];
get isEmpty => null;
void addItems(JournalModel data) {
items.add(data);
notifyListeners();
}
int get count {
return _count;
}
List<JournalModel> get itemsList {
return items;
}
}
Here is the model to my item list, favoriteBloc pretty much consists of the following as well. It gets the JournalModel after the user has pressed on the favorite button.
class JournalModel{
String? id;
String? journalTitle;
int? journalReleaseYear;
String? author;
String? topic;
String? fileLocation;
bool status;
JournalModel({
this.id,
this.journalTitle,
this.journalReleaseYear,
this.author,
this.topic,
this.fileLocation,
this.status = false
});
}
When i try to run the application however, it throws the error stated at the title. Does it have to do something with the bool status in the model?
I've essentially implemented the same thing i did on my search function and it works just fine.
Related
I'm having this super annoying issue of being unable to grab and display a table from my server hosted on PhpmyAdmin. (I've managed to grab the data and have it printed in the console, but now that I'm trying to display it in a table I can't seem to get it working)
I've tried nulling my variables but I'm not really sure what the main culprit for this error is. Any help would be greatly appreciated.
Image of Error
data.dart File
class dataListing extends StatefulWidget {
const dataListing({Key? key}) : super(key: key);
#override
State<dataListing> createState() => _dataListingState();
}
class _dataListingState extends State<dataListing> {
#override
Widget build(BuildContext context) {
return Container();
}
}
class listingData{
String? ListingID, listingName, listingDescription, address, suburbName, phoneNumber, openingHours, Email, Website;
listingData({
this.ListingID,
this.listingName,
this.listingDescription,
this.address,
this.suburbName,
this.phoneNumber,
this.openingHours,
this.Email,
this.Website,
});
//constructor
List<listingData> datalist = [];
factory listingData.fromJSON(Map<String, dynamic> json){
return listingData(
ListingID: json["ListingID"],
listingName: json["listingName"],
listingDescription: json["listingDescription"],
address: json["address"],
suburbName: json["suburbName"],
phoneNumber: json["phoneNumber"],
openingHours: json["openingHours"],
Email: json["Email"],
Website: json["Website"],
);
}
}
Directory.dart file
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:app/pages/data.dart';
class directoryPage extends StatefulWidget {
#override
State<directoryPage> createState() => _directoryPageState();
}
class _directoryPageState extends State<directoryPage> {
// List serviceListing = [];
//
// getAllListing()async{
// String url = "URL HERE";
// var response = await http.get(Uri.parse(url));
// if (response.statusCode == 200){
// setState (() {
// serviceListing = json.decode(response.body);
// });
// print (serviceListing);
// return serviceListing;
// }
// }
bool error = false, dataloaded = false;
var data;
String dataurl = "URL HERE";
#override
void initState (){
loaddata();
super.initState();
// getAllListing();
}
void loaddata() {
Future.delayed(Duration.zero,() async {
var res = await http.post(Uri.parse(dataurl));
if (res.statusCode == 200) {
setState(() {
data = json.decode(res.body);
dataloaded = true;
});
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Directory'),
centerTitle: true,
elevation: 0,
backgroundColor: Color(0xFFA30B32),
//WSU Appbar Icon
leading: Padding(
padding: const EdgeInsets.all(8.0),
child: Image.asset("assets/wsulogo.png", scale: 8.0),
),
),
body: Container(
padding: EdgeInsets.all(15),
child:dataloaded?datalist():
Center(
child:CircularProgressIndicator()
),
)
);
}
Widget datalist(){
if(data["error"]) {
return Text(data["errmsg"]);
}else{
List<listingData> datalist = List<listingData>.from(data["data"].map((i){
return listingData.fromJSON(i);
})
);
return Table( //if data is loaded then show table
border: TableBorder.all(width:1, color:Colors.black45),
children: datalist.map((listingdata){
return TableRow( //return table row in every loop
children: [
//table cells inside table row
TableCell(child: Padding(
padding: EdgeInsets.all(5),
child:Text(listingdata.ListingID!)
)
),
TableCell(child: Padding(
padding: EdgeInsets.all(5),
child:Text(listingdata.listingName!)
)
),
TableCell(child: Padding(
padding: EdgeInsets.all(5),
child:Text(listingdata.listingDescription!)
)
),
TableCell(child: Padding(
padding: EdgeInsets.all(5),
child:Text(listingdata.address!)
)
),
]
);
}).toList(),
);
}
}
}
Looks like the issue was actually unrelated to the dart side of things, the php code wasn't properly structuring the data. Cannot have underscores or spaces.
Correct-> $json["dballlisting"] = array (); (I renamed it to just "data" later)
Incorrect->$json["db_all_listing"] = array ();
The error seems to be originating from this line, the data['data'] is null which is expected to be an Array.
List<listingData> datalist = List<listingData>.from(data["data"].map((i){
return listingData.fromJSON(i);
})
You need to investigate your API call to make sure why it is happening. If the null value is expected then you need to add safeguards in your code to make sure it won't break when it encounter such scenarios. You can add null safety checks for that one way to do it would be to
List<listingData> datalist = List<listingData>.from((data["data"] ?? []).map((i){
return listingData.fromJSON(i);
})
I was learning to use Contacts in flutter app, and while understanding and writing code, i saw this expression - _contacts?.length ?? 0,
I am not able to understand what does this mean, what is the use of question marks here?
Here is the full code -
import 'package:flutter/material.dart';
import 'package:contacts_service/contacts_service.dart';
class ContactsPage extends StatefulWidget {
#override
_ContactsPageState createState() => _ContactsPageState();
}
class _ContactsPageState extends State<ContactsPage> {
Iterable<Contact> _contacts;
#override
void initState() {
getContacts();
super.initState();
}
Future<void> getContacts() async {
//Make sure we already have permissions for contacts when we get to this
//page, so we can just retrieve it
final Iterable<Contact> contacts = await ContactsService.getContacts();
setState(() {
_contacts = contacts;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: (Text('Contacts')),
),
body: _contacts != null
//Build a list view of all contacts, displaying their avatar and
// display name
? ListView.builder(
itemCount: _contacts?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
Contact contact = _contacts?.elementAt(index);
return ListTile(
contentPadding:
const EdgeInsets.symmetric(vertical: 2, horizontal: 18),
leading: (contact.avatar != null && contact.avatar.isNotEmpty)
? CircleAvatar(
backgroundImage: MemoryImage(contact.avatar),
)
: CircleAvatar(
child: Text(contact.initials()),
backgroundColor: Theme.of(context).accentColor,
),
title: Text(contact.displayName ?? ''),
//This can be further expanded to showing contacts detail
// onPressed().
);
},
)
: Center(child: const CircularProgressIndicator()),
);
}
}
Here is the link to the page - How to access contacts in flutter
It first checks the _contacts.
If it is not null, it checks the _contacts.length.
If the _contacts.length is not null, it sets its value, otherwise it sets zero.
_contacts?.length ?? 0
first ? means if contacts is null, don't try to call length and just return null because calling .length on null will throw an error. So basically you're avoiding a null pointer error.
the second ?? means if the contacts or length is null (i.e., the value before ??), return 0.
The Dart Language Tour - Operators Section is a good reference for Dart's operator.
It means that if _contacts array is null, you will get 0 for length.
In my Flutter application I am using Provider version 4.0.4 to manage the state of my app. In basic terms, my app will list down the nearby companies with their rating. users can select a organisation, open it and add their rating as well, so the final rating will be updated. I am using the Consumer concept in Provider to handle the tasks.
In NearByPlacesPage class I am listing down the companies around me with rating information. User can click on a company and they will be taken to OrganizationPage page.
In OrganizationPage class, the rating is displayed again. user can add their rating to the system. Then the rating information in both OrganizationPage page and NearByPlacesPage (back page) need to be updated.
The issue is, when the user update the rating, the rating in OrganizationPage get updated but not NearByPlacesPage in back stack. When we go back to NearByPlacesPage, we can clearly see the old rating values. The page need to be reloaded to get updated values.
Below are the important sections in my code
NearByPlacesPage
class NearByPlacesPage extends StatelessWidget {
int orgTypeID;
String orgTypeName;
NearByPlacesPage(this.orgTypeID, this.orgTypeName);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => RatingService()),
],
child: SingleChildScrollView(
child: _NearByPlacesPageUI(orgTypeID, orgTypeName),
),
),
appBar: AppBar(
title: Text(orgTypeName),
),
);
}
}
class _NearByPlacesPageUI extends StatefulWidget {
int orgTypeID;
String orgTypename;
_NearByPlacesPageUI(this.orgTypeID, this.orgTypename);
#override
State<StatefulWidget> createState() {
return _NearByPlacesPageState();
}
}
class _NearByPlacesPageState extends State<_NearByPlacesPageUI> {
#override
Widget build(BuildContext context) {
Consumer<RatingService>(builder: (context, data, child){
return Flexible(
child: ListView.builder(
itemCount: orgList.length,
itemBuilder:(BuildContext context, int index) {
Organization organization = orgList[index];
if (organization.isDisabled != true) {
RatingValue ratingValue = data.getData();
return Container(
margin: EdgeInsets.only(
top: 5, left: 5, right: 5),
child: _buildPlace(organization, ratingValue));
} else {
return Container();
}
},),
);
},);
}
}
OrganizationPage
class OrganizationPage extends StatelessWidget {
Organization organization;
String orgTypeName;
OrganizationPage(this.organization, this.orgTypeName);
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: _OrganizationPageUI(organization, orgTypeName),
),
backgroundColor: Colors.white,
appBar: AppBar(
title: Text(organization.name),
),
);
}
}
class _OrganizationPageUI extends StatefulWidget {
Organization organization;
String orgTypeName;
_OrganizationPageUI(this.organization, this.orgTypeName);
#override
State<StatefulWidget> createState() {
return _OrganizationPageState();
}
}
class _OrganizationPageState extends State<_OrganizationPageUI> {
#override
Widget build(BuildContext context) {
Consumer<RatingService>(
builder: (context, data, child) {
Consumer<RatingService>(
return Row(
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 10, left: 10),
child: Text(daa.getData()
style: Theme.of(context).textTheme.bodyText2.apply(color: Colors.grey),
),
),
],
);
),
}
}
}
In OrganizationPage there is a AlerDialog, which allows the user to rate and save. When saved, it will call another method which will reload the data.
Widget _ratingDialog(double _rating) {
RatingService _ratingService =
Provider.of<RatingService>(context, listen: false);
Rating _rating = _ratingService.returnRating();
double _ratingValue = _ratingService.returnRating().rating;
return AlertDialog(
title: const Text("Your Rating"),
actions: [
new FlatButton(
child: const Text("Save"),
//onPressed: () => Navigator.pop(context),
onPressed: () async {
Rating rating = Rating(
idrating:
_rating.idrating != null ? _rating.idrating : null,
user: _user,
organization: widget.organization,
rating: _ratingValue,
dateCreated: DateTime.now().millisecondsSinceEpoch,
lastUpdated: DateTime.now().millisecondsSinceEpoch);
await _ratingService.saveOrUpdateRating(rating, authToken);
_loadRatingByUserAndOrganization(authToken);
_loadRatingValueByOrganization(authToken);
Navigator.pop(context);
},
),
],
);
}
Future _loadRatingByUserAndOrganization(String authToken) {
RatingService _ratingService =Provider.of<RatingService>(context, listen: false);
return _ratingService.getRatingByUserAndOrganization(
_authService.getDatabaseUser().user.iduser,
widget.organization.idorganization,
authToken);
}
RatingService
This is the class which is responsible for calling notifyListeners(). It will be triggered by the above AlertDialog and the expected behaviour is to reload data in both OrganizationPage and NearByPlacesPage
class RatingService with ChangeNotifier {
List<RatingValue> _ratingValueList ;
List<RatingValue> getData()
{
return _ratingValueList;
}
//Load rating by user and Organization
Future<void> getRatingByUserAndOrganization(int idUser, int organizationID, String authToken) async {
try {
var data = await http.get(
_navLinks.getRatingByUserAndOrganization(idUser, organizationID),
headers: {HttpHeaders.authorizationHeader: "Bearer $authToken"},
);
print(data.body);
_rating = Rating.fromJson(convert.json.decode(data.body));
notifyListeners();
} catch (error) {
print(error);
throw error;
}
}
}
What I have I done wrong?
I'm building a selectable checkbox contact list in flutter but if a contact has only an email and no number, an error is thrown. I want to create a loop to add a number of '99999' to the contacts phone number if they don't have one. Please can someone guide me with an explanation of what I should change to make this work? I have had a go, but I am quite new to flutter so I'm not completely certain on syntax etc...
Here is the part of the code that I am trying to put the function into.
setMissingNo()async {
Iterable<Contact> contactsToLoop = (await ContactsService.getContacts()).toList();
contactsToLoop.forEach((Contact) { contactsToLoop = []..add(Item.fromMap({'label': 'work', 'value': 99999})); });
}
//fetch contacts from setMissingNo
getAllContacts() async{
Iterable<Contact> _contacts = (await ContactsService.getContacts()).toList();
setState(() {
contacts = _contacts;
}
);
}
Here is my whole code
import 'package:flutter/material.dart';
// TODO: make it ask for permissions otherwise the app crashes
import 'package:contacts_service/contacts_service.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<Contact> contacts = [];
List<Contact> contactsFiltered = [];
TextEditingController searchController = new TextEditingController();
#override
void initState() {
super.initState();
getAllContacts();
searchController.addListener(() => filterContacts());
}
//remove the +'s and spaces from a phone number before its searched
String flattenPhoneNumber(String phoneStr) {
return phoneStr.replaceAllMapped(RegExp(r'^(\+)|\D'), (Match m) {
return m[0] == "+" ? "+" : "";
});
}
//loop and set all contacts without numbers to 99999, pass new list to getAllContacts
setMissingNo()async {
Iterable<Contact> contactsToLoop = (await ContactsService.getContacts()).toList();
contactsToLoop.forEach((Contact) { contactsToLoop = []..add(Item.fromMap({'label': 'work', 'value': 99999})); });
}
//fetch contacts from setMissingNo
getAllContacts() async{
Iterable<Contact> _contacts = (await ContactsService.getContacts()).toList();
setState(() {
contacts = _contacts;
}
);
}
//filtering contacts function to match search term
filterContacts() {
List<Contact> _contacts = [];
_contacts.addAll(contacts);
if (searchController.text.isNotEmpty) {
_contacts.retainWhere((contact) {
String searchTerm = searchController.text.toLowerCase();
String searchTermFlatten = flattenPhoneNumber(searchTerm);
String contactName = contact.displayName.toLowerCase();
bool nameMatches = contactName.contains(searchTerm);
if (nameMatches == true) {
return true;
}
if (searchTermFlatten.isEmpty) {
return false;
}
var phone = contact.phones.firstWhere((phn) {
String phnFlattened = flattenPhoneNumber(phn.value);
return phnFlattened.contains(searchTermFlatten);
}, orElse: () => null);
return phone != null;
});
setState(() {
contactsFiltered = _contacts;
});
}
}
final selectedContacts = Set<Contact>();
#override
Widget build(BuildContext context) {
bool isSearching = searchController.text.isNotEmpty;
return Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
AppBar(
title: Text('Create Group'),
),
Container(
child: TextField(
controller: searchController,
decoration: InputDecoration(
labelText: 'Search Contacts',
border: OutlineInputBorder(
borderSide: new BorderSide(
color: Theme.of(context).primaryColor
)
),
prefixIcon: Icon(
Icons.search,
color: Theme.of(context).primaryColor
)
),
),
),
Expanded( child: ListView.builder(
shrinkWrap: true,
itemCount: isSearching == true ? contactsFiltered.length : contacts.length,
itemBuilder: (context, index) {
Contact contact = isSearching == true ? contactsFiltered[index] : contacts[index];
//TODO: make it so when you clear your search, all items appear again & when you search words it works
return CheckboxListTile(
title: Text(contact.displayName),
subtitle: Text(
contact.phones.elementAt(0).value
),
value: selectedContacts.contains(contact),
onChanged: (bool value) {
if (value) {
selectedContacts.add(contact);
} else {
selectedContacts.remove(contact);
}
setState((){});
// TODO: add in function to add contact ID to a list
});
},
),
/*new Expanded(
child: Align(
alignment: Alignment.bottomLeft,
child: BottomNavigationBar(
currentIndex: _currentIndex,
items: const <BottomNavigationBarItem>[
//TODO: create new contact functionality to add someone by name + email
BottomNavigationBarItem(
icon: Icon(Icons.add),
title: Text('Add Contact'),
),
BottomNavigationBarItem(
icon: Icon(Icons.create),
title: Text('Create Group'),
),
],
onTap: (index) {
setState(() {
_currentIndex = index;
});
}
)
)
)*/
)
],
)
),
);
}
}
Updating all contacts listed on the device might take a long time depending on the size of the device's contact list. Add that the task is being done on the UI thread. You may want to consider using Isolate for the task to move away the load from the UI thread. If you can also provide the errors that you're getting, it'll help us get a picture of the issue.
Another thing is, the way you're approaching the issue might be impractical. Is it really necessary to write a placeholder phone number to the contacts? The issue likely stems from trying to fetch the number from a contact but the Object returns null. Perhaps you may want to consider only updating the phone number when the contact is selected.
In my application I would like to display vaccines according to the species of the animal (If is a dog or cat). I'm experiencing an error: The method 'map' was called on null. Tried calling: map DropdownMenuItem. Why is this happening? I already put async and await in the methods, I don't understand why it is still null. Bellow my code:
1) This is where I call my DropdownContent class in init to prepare my DropdownMenuItem in the row inside the widget
class _VaccineDetailFormState extends State<VaccineDetailForm> {
final DataRepository repository = DataRepository();
String selectedVaccine = "Select";
List<String> vaccinesBySpecie;
initState() {
DropdownContent.getVaccines(widget.selectedPetID).then((value) => vaccinesBySpecie = value);
}
#override
Widget build(BuildContext context) {
return Scaffold(
[...]
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new DropdownButton<String>(
value: widget.vaccine.name == null ? selectedVaccine: widget.vaccine.name,
underline: Container(
height: 2,
color: Colors.grey,
),
onChanged: (String newValue) {
setState(() {
selectedVaccine = newValue;
widget.vaccine.name = newValue;
});
},
items: vaccinesBySpecie.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)
]),
[...]
2) Here is the DropdownContent class that, inside the getVaccines() method, searches the repository to find out the species of the current animal and then returns the appropriate vaccine list.
class Dropdown content
static Future<List<String>> getVaccines(String petId) async {
final DataRepository repository = DataRepository();
String currentSpecie = await repository.getSpecie(petId);
if (currentSpecie.contains('Dog')) {
return listOfVaccinesForDogs();
}
if (currentSpecie.contains('Cat')) {
return listOfVaccinesForCats();
}
}
3) Finally, the repository class that searches for the species of the animal
class Repository
Future<String> getSpecie(String petId) async {
DocumentReference documentReference = petCollection.document(petId);
await documentReference.get().then((snapshot) {
return snapshot.data['specie'].toString();
});
}
While your initState method may be asynchronous, your build method isn't. So at the time that the build method is called, your vaccinesBySpecie method is null.
The best way to fix this would be to initialize your List<String> vaccinesBySpecie like so List<String> vaccinesBySpecie = [];. This way it isn't null when the build method is called.
As a side note, I would suggest using a FutureBuilder or StreamBuilder if you can, that way you can handle when there isn't a value (i.e it is null) vs when there is a value(ie it is not null)
What Dean said illuminated my ideas. I managed to solve it by reaching the following answer:
1) DropdownMenuItem inside the widget
Container(
child: FutureBuilder <List<String>>(
future: DropdownContent.getVaccines(widget.selectedPetID),
builder: (context, AsyncSnapshot snapshot) {
if(snapshot.data == null) {
return CircularProgressIndicator();
}
else {
return DropdownButton<String>(
value: widget.vaccine.name == null? selectedVaccine: widget.vaccine.name,
underline: Container(
height: 2,
color: Colors.grey,
),
onChanged: (String newValue) {
setState(() {
selectedVaccine = newValue;
widget.vaccine.name = newValue;
});
},
items: snapshot.data.map<DropdownMenuItem<String>>((value) =>
new DropdownMenuItem<String>(
child: Text(value),
value: value,
))
.toList(),
);
}
})
)
2) DropdownContent class that searches the repository to find out the species of the current animal and then returns the appropriate vaccine list:
static Future<List<String>> getVaccines(String petId) async {
final DataRepository repository = DataRepository();
String currentSpecie;
await repository.getSpecie(petId).then((value) {
currentSpecie = value;
});
if (currentSpecie.contains('Dog')) {
return listOfVaccinesForDogs();
}
if (currentSpecie.contains('Cat')) {
return listOfVaccinesForCats();
}
}
3) The repository class that searches for the species of the animal
Future<String> getSpecie(String petId) async {
DocumentReference documentReference = petCollection.document(petId);
String specie;
await documentReference.get().then((snapshot) {
specie = snapshot.data['specie'].toString();
});
return specie;
}