Unique Counters For All Items In ListView - Flutter - firebase

Disclaimer: I saw some solutions on stackoverflow but none of them had listviews generated with data from firestore. So a hint on that questions will not help me :/
Hey
I have a little problem with my counters within my ListView from the Firestore database. As you can see below, I am trying to create unique counters for each entry in the list from the database. The number currently changes with each entry, so it is identical everywhere.
It may be useful to know when you try to help me solving that problem, that in the following I still need the number of the counter in order to save it in the database.
(I saw other solutions where that point was pretty heavy)
So here is the extract of my code:
body: StreamBuilder(
stream: FirebaseFirestore.instance
.collection(globals.selectedKat)
.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
return Padding(
padding: const EdgeInsets.all(20.0),
child: GridView.count(
children: snapshot.data.docs.map((document) {
return Center(
[...]
child: Container(
[...]
child: Column(
children: [
Center(
child: Text(
document["titel"],
)),
Row(
children: [
IconButton(
onPressed: () {
setState(() {
counter++;
});
},
icon: Icon(Icons.add_circle_outline)),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
counter.toString(),
style: TextStyle(
color: Colors.white, fontSize: 30),
),
),
IconButton(
iconSize: 40,
color: Colors.white,
onPressed: () {
setState(() {
counter--;
});
},
icon: Icon(Icons.remove_circle_outline))
],
)
],
)),
);
}).toList(),
),
);
}
}),
And here a Screen of my sim:
If anything is not clear I'll answer in a few minutes.
Thanks for helping me :)

Related

How to toLowerCase() on data from Flutter Firestore?

I made an application and coded a search system between documents in the application. But there is a problem:
The incoming record is registered as "Durmuş Bolat" in Firestore. And as you can see, the letter D of Durmuş is capitalized. That's why I have to capitalize the first letter in the search. And that's a problem.
I want to get the result of "Durmuş Bolat" even if I write it in the following ways:
durmuş bolat
DURMUŞ BOLAT
DuRmUs BoLaT
As a solution to this, I thought of shrinking the searched content and all the incoming content. So all incoming data and searched content will be downsized with toLowerCase.
I don't have the slightest idea where to place this toLowerCase code. Can you help me?
Codes:
var _searchText;
return Scaffold(
appBar: AppBar(
title: Container(
height: 50,
width: 250,
child: TextFormField(
autofocus: true,
style: TextStyle(color: Colors.white),
onChanged: (value) {
setState(() {
_searchText = value;
});
},
),
),
),
body: Column(
children: [
Container(
height: MediaQuery.of(context).size.height * 0.8, //MediaQuery.of(context).size.height * 0.8,
child: StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance.collection('bolatTamir').where('nameSurname', isGreaterThanOrEqualTo: _searchText).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
return InkWell(
child: ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(snapshot.data!.docs[index].data()['urunFotografi']),
),
title: Text(snapshot.data!.docs[index].data()['nameSurname'], style: TextStyle(fontSize: 20),),
subtitle: Text(snapshot.data!.docs[index].data()['yapildiMi'] == false ? 'Tamamlanmadı' : 'Tamamlandı', style: TextStyle(fontSize: 17),),
trailing: Text(snapshot.data!.docs[index].data()['tamirUcreti'], style: TextStyle(fontSize: 20),),
),
);
},
);
},
),
),
],
),
);
Thank you in advance for your help.
To my knowledge, lower-casing text that is already stored is not possible with Firebase. One can only lower-case the search term.
If it is just names and not tons of text, one could
.split(' ') these names, then
.toLowerCase() all resulting words, then
store them in a search-index-collection.
Then, search this search-index-collection and resolve the $userId.
.
users/34td24y3gtdb724/
{
name: 'Durmuş Bolat'
}
searchindex/
{
word: 'durmuş',
userId: 34td24y3gtdb724
word: 'bolat',
userId: 34td24y3gtdb724
}
Google itself asks to use Third-Party-Providers for full text search:
https://cloud.google.com/firestore/docs/solutions/search
Also, there is an approach to generate permutations as an Array:
https://medium.com/flobiz-blog/full-text-search-with-firestore-on-android-622af6ca5410

Flutter - How to make a call on Flutter Firestore, return value if it contains?

I'm building a system. Purpose product search. But I'm having a problem with this search. I want this search system like this: If what the person is looking for is in any value, it should be returned as a result. For example, if the person is looking for shoes as a product, when I type sho, I want it to come to the listView.
Or let me give you another example: Finding the glass when typing gla for glass. How can I make this search system?
Firestore:
I tried a code like this:
Container(
height: 200,
child: StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance.collection("bolatAktar").where("urunAdi", isEqualTo: _arananUrun).snapshots(), // !!!!!!!!!!!!!!<<<<<<<<<<<<<<<<<<<<<
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
else {
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
return InkWell(
child: ListTile(
leading: Icon(Icons.label),
title: Text(snapshot.data!.docs[index].data()["urunAdi"], style: TextStyle(fontSize: 20),),
),
onTap: () {
showModalBottomSheet(
isScrollControlled:true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
),
),
context: context,
builder: (context) {
return FractionallySizedBox(
heightFactor: 0.93,
child: Container(
padding: EdgeInsets.all(25),
height: MediaQuery.of(context).size.height * 0.5,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("Product name:", style: TextStyle(fontSize: 20)),
SizedBox(height: 10),
TextFormField(
style: TextStyle(fontSize: 19),
decoration: InputDecoration(
border: OutlineInputBorder(),
),
initialValue: snapshot.data!.docs[index].data()["urunAdi"],
),
]
)
),
);
}
);
},
);
},
);
}
},
),
),
Result:
Thank you in advance for your help and valuable information.
Google Firestore: Query on substring of a property value (text search)
Is there a way to search sub string at Firestore?
Firestore Comparison Operators - contains, does not contain, starts with
I have reviewed the above topics, but they did not contribute because they are not up to date.

How can I update Listview in Streambuilder in flutter

I have a streambuidler widget that contains a listview to display whom the current user has recently chatted with. When the current user receives a message that message should be pushed to the top of the listview, however that message is always display at the bottom of the list view, it's only display on the top if I refresh my screen.
NoSearchResultScreen() {
final Orientation orientation = MediaQuery.of(context).orientation;
print("hasAlreadyChatWithSomeone: $hasAlreadyChatWithSomeone");
return hasAlreadyChatWithSomeone
? StreamBuilder<QuerySnapshot>(
stream: (FirebaseFirestore.instance
.collection("user")
.where("id", isEqualTo: currentUserId)
.snapshots()),
builder: (context, snapshot) {
List<ProfileChatWith> chatWithList = [];
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: circularProgress(),
);
}
if (snapshot.hasData) {
final chatWithSnapshot = snapshot.data?.docs.first['chatWith'];
//print("chatWithSnapshot: $chatWithSnapshot");
for (var userChatWith in chatWithSnapshot) {
final user = ProfileChatWith(
userId: userChatWith,
currentUserId: currentUserId,
);
chatWithList.add(user);
//print("I have chatted with: $userChatWith");
}
return Container(
width: MediaQuery.of(context).size.width,
child: ListView(
//shrinkWrap: true,
children: chatWithList,
),
);
} else {
return Center(
child: circularProgress(),
);
}
},
)
: Container(
child: Center(
child: ListView(
shrinkWrap: true,
children: <Widget>[
Icon(
Icons.group,
color: Colors.greenAccent,
size: 200.0,
),
Text(
"Search Users",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.greenAccent,
fontSize: 50.0,
fontWeight: FontWeight.bold),
)
],
),
),
);
}
Try reverse: true,
return SizedBox(
width: MediaQuery.of(context).size.width,
child: ListView(
reverse: true,
children: chatWithList,
),
);
Use Listview.builder for performance optimization
return SizedBox(
width: MediaQuery.of(context).size.width,
child: ListView.builder(
reverse: true,
itemBuilder: (BuildContext context, int index) => chatWithList[index],
),
);
The solution which worked is as below, #Leo Tran own words
I found a way to solve my question is that I will rebuild my widget whenever the data got updated.

Cant get StreamBuilder to display data from cloud firestore

I know I have a connection to the database and no errors are appearing so I'm pretty confused. The title and code should summarize the problem fairly well. Think I'm missing something?
here is the main code that should be displaying cards with titles from firebase
mainList() {
StreamBuilder(
stream: Firestore.instance.collection('Events').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text('Loading');
} else {
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot userPost = snapshot.data.documents[index];
return Container(
width: MediaQuery.of(context).size.width,
height: 350.0,
child: Padding(
padding: EdgeInsets.only(top: 8.0, bottom: 8.0),
child: Material(
elevation: 14.0,
shadowColor: Color(0x802196F3),
child: Center(
child: Padding(
padding: EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: 200.0,
child: Text(
'${userPost['title']}',
))
],
),
),
))),
);
},
);
}
});
}
and here is where the function is called:
lass MyAppmain extends State<MyApp> {
#override
Widget build(BuildContext context) {
var listView = ListView.builder(
itemCount: local.length,
itemBuilder: (BuildContext cnxt, int index) {
return new Text(local[index]);
});
return MaterialApp(
home: PageView(
controller: controller,
children: <Widget>[
//home page---------------------------
Scaffold(
appBar: AppBar(
title: Text(
'Events',
),
elevation: 20,
),
//main list view for the cards
//think I use streambuilder for this so google before starting
body: mainList(),//RIGHT HERE
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Navigator.push(context, NewEventTransition());
},
mini: true,
),
),
//Profile Page-------------------------------
Scaffold(
appBar: AppBar(
title: Text(
'Profile',
),
elevation: 20,
),
),
],
));
}
}
Want a listview of cards holding the titles from firebase (will soon be more than titles but want to get this working first)
This is a common problem.
return ListView.builder(
itemCount: snapshot.data.documents.length, // this line is the culprit!
itemBuilder: (context, index) {
print(snapshot.data.documents.length); // it will print null
.......
}
See, It takes some time to fetch data from firebase. When ListView.builder is called the value of snapshot.data.documents.length is actually null. Tho after few seconds it gets data but till then ListView had built the UI and that's why it's blank. To check the value, you can add a Print statement like shown above.
Now there are few ways to solve this problem:
Make an int variable say totalLength, make a function say setTotalLength which makes a call to Firebase/Firestore database and use setState to assign this value to totalLength and then change that code to this:
itemCount: totalLength,
You should Call setTotalLength in your initState method.
Or, you can change your code to this, But I'm NOT 100% sure that this will work:
itemCount: snapshot.data.documents.length ?? 0 // returns 0 if the value is null, may reload UI again when data comes

How to load image to the Card from data retrieved from async task in flutter?

I'm new to flutter development. I need to load images into a card depending on data loaded via async task.
I have an async task which returns Future> user data quired from the sqlite local database. With retrieved data, I build a ListView to show users using Card. But inside the card, I'm trying to show an image which will be downloaded from Firebase Storage depending on the data retrieved from the local database. But the image URL is null.
Widget build(BuildContext context) {
var allCards = DBProvider.db.getAllCards();
return FutureBuilder<List<User>>(
future: DBProvider.db.getAllCards(),
builder: (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Text('Loading...');
default:
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
User user = snapshot.data[index];
return Card(
elevation: 8.0,
margin:
new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Column(
children: <Widget>[
Stack(
children: <Widget>[
Container(
child: Image(
image: CachedNetworkImageProvider(FirebaseStorage().ref().child('employer_logo').child('00001').child('google-banner.jpg').getDownloadURL().toString()),
fit: BoxFit.cover,
),
),
Positioned(
bottom: 0,
left: 0,
child: Container(
padding: EdgeInsets.all(10),
child: Text(
'Google Incorperation',
style: TextStyle(
fontSize: 20, color: Colors.white),
),
),
)
],
),
Container(
decoration: BoxDecoration(
color: Colors.white10,
),
child: ListTile(
title: Text(user.fname + " " + user.lname,
style: TextStyle(
color: Colors.blue[400], fontSize: 20)),
subtitle: Text(user.designation,
style: TextStyle(
color: Colors.blue[300], fontSize: 16)),
onTap: () => {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Profile(
user.fname,
user.lname,
user.uid,
user.designation,
user.mobile,
user.employerId)))
},
),
)
],
),
);
},
);
}
},
);
}
I expect to show images downloaded from firebase storage
This would be my first answer, and there are probably many ways to improve my answer here. But I will give it a go: Actually, you will have to look up a lot on Futuresand Streams, because it is quite a big part in many a app. If your app needs any content on the web, it will need Futures, or it's bigger counterpart Stream. In this case, where you want to set up a Listview with probably multiple images, I would go for a Stream. Also, I would save all my database logic in a seperate file. However, if you don't want to modify your code too much now, I would use a FutureBuilder.
I've seen you already use one of them in your code. But in this case, use:
...
int maxsize = 10e6.round(); // This is needed for getData. 10e^6 is usually big enough.
return new Card (
FutureBuilder<UInt8List> ( // I also think getting Data, instead of a DownloadUrl is more practical here. It keeps the data more secure, instead of generating a DownloadUrl which is accesible for everyone who knows it.
future: FirebaseStorage().ref().child('entire/path/can/go/here')
.getData(maxsize),
builder: (BuildContext context, AsyncSnapshot<UInt8List> snapshot) {
// When this builder is called, the Future is already resolved into snapshot.data
// So snapshot.data contains the not-yet-correctly formatted Image.
return Image.memory(data, fit: BoxFit.Cover);
},
),
Widget build(BuildContext context) {
var allCards = DBProvider.db.getAllCards();
return FutureBuilder<List<User>>(
future: DBProvider.db.getAllCards(),
builder: (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Text('Loading...');
default:
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
User user = snapshot.data[index];
int maxsize = 10e6.round();
return Card(
elevation: 8.0,
margin:
new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Column(
children: <Widget>[
Stack(
children: <Widget>[
Container(
child: FutureBuilder<dynamic>(
future: FirebaseStorage()
.ref()
.child('employer_logo')
.child('00001')
.child('google-banner.jpg')
.getDownloadURL(),
builder: (BuildContext context,
AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState !=
ConnectionState.waiting) {
return Image(
image: CachedNetworkImageProvider(
snapshot.data.toString()),
fit: BoxFit.cover,
);
}
else {
return Text('Loading image....');
}
},
),
),
Positioned(
bottom: 0,
left: 0,
child: Container(
padding: EdgeInsets.all(10),
child: Text(
'Google Incorperation',
style: TextStyle(
fontSize: 20, color: Colors.white),
),
),
)
],
),
Container(
decoration: BoxDecoration(
color: Colors.white10,
),
child: ListTile(
title: Text(user.fname + " " + user.lname,
style: TextStyle(
color: Colors.blue[400], fontSize: 20)),
subtitle: Text(user.designation,
style: TextStyle(
color: Colors.blue[300], fontSize: 16)),
onTap: () => {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Profile(
user.fname,
user.lname,
user.uid,
user.designation,
user.mobile,
user.employerId)))
},
),
)
],
),
);
},
);
}
},
);
}

Resources