I wrote a code like this:
StreamBuilder(
stream: _firestore.collection("Products").where("Name", isGreaterThanOrEqualTo: SearchText.toLowerCase()).snapshots(),
builder: (BuildContext context, snapshot) {
if (snapshot.data == null) {
return const Text("No data");
}
return ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text(snapshot.data.docs[index].data()["Name"]),
),
);
},
);
}
),
I want to shrink the data from Firestore with .toLowerCase() or otherwise. In order to make a search system, I need to shrink the incoming data. How can I do that?
Thanks for help.
I don't understand what you mean by shrink. You mentioned toLowerCase() so this is what I think the problem is
You have a stream of product names from firestore and you want to be able to make them searchable. The user search query text might be lowercase so you want to run your search on the products from firestore(lowercased)
One way to do this is to modify the stream of products that you are getting from your firestore . You can run this on dartpad.dev/
Here is a simple example with a fake list of products. I have illustrated how to use something called a streamTransformer
// A mock list of products
final List<String> productList = [
"Airpods",
"Wallet",
"Glasses",
"Gatorade",
"Medicine"
];
// A stream that exposes the product list
Stream<String> productStream() async* {
for(var product in productList){
yield product;
}
}
void main() {
// Use a stream transformer to transform or modify the stream
StreamTransformer<String, dynamic> lowerCaser = StreamTransformer.fromHandlers(handleData: (data,sink)=> sink.add(data.toString().toLowerCase()));
// Transform the stream with the .transform function
productStream().transform(lowerCaser).listen(
(product)=>print(product)
);
}
Related
Problem: Both of my streams from the code below do not update my UI automatically.
So the new data is only fetched and displayed when I do a hot reload or a hot restart. I am trying to fetch the most recent messages from each chat room and display them to the user.
Question: How can I change my code to make the streams work properly? Or is there maybe a better solution to what I am doing below?
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:blabber_tech/services/auth.dart';
import 'package:blabber_tech/services/chat_services.dart';
class MyChatsScreen2 extends StatelessWidget {
static const String id = "mychats2_screen";
// get current user id
String? userId = AuthService().getUserId();
// Stream of all rooms of current user
Stream getRoomsStream() async* {
// get rooms of current user
QuerySnapshot roomsSnapshot = await FirebaseFirestore.instance
.collection("rooms")
.where("userId1", isEqualTo: userId)
.get();
// get rooms of current user
QuerySnapshot roomsSnapshot2 = await FirebaseFirestore.instance
.collection("rooms")
.where("userId2", isEqualTo: userId)
.get();
// add rooms of current user to rooms list
List<QueryDocumentSnapshot> rooms = roomsSnapshot.docs;
// add rooms of current user to rooms list
List<QueryDocumentSnapshot> rooms2 = roomsSnapshot2.docs;
// add rooms of current user to rooms list
rooms.addAll(rooms2);
// sort rooms list by when last message was sent
// rooms.sort(
// (a, b) => b["lastMessageSentAt"].compareTo(a["lastMessageSentAt"]));
yield rooms;
}
// Stream to get last message of each room
Stream getLastMessageStream(String roomId) async* {
try {
// get last message of room
QuerySnapshot lastMessageSnapshot = await FirebaseFirestore.instance
.collection("rooms")
.doc(roomId)
.collection("messages")
.orderBy("createdAt", descending: true)
.limit(1)
.get();
// get last message of room
List lastMessage = lastMessageSnapshot.docs;
// return last message of room
yield lastMessage;
} catch (error) {
print(error);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
// create listview of all chats of current user and show last message and other user name and photo
child: StreamBuilder(
stream: getRoomsStream(),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return StreamBuilder(
stream: getLastMessageStream(snapshot.data[index].id),
builder: (context, AsyncSnapshot<dynamic> snapshot2) {
if (snapshot2.hasData) {
return ListTile(
leading: CircleAvatar(
//backgroundImage: NetworkImage(
//snapshot.data[index]["userPhotoUrl"]),
),
//title: Text(snapshot.data[index]["userName"]),
subtitle: Text(snapshot2.data[0]["message"]),
);
} else {
return Container();
}
},
);
},
);
} else {
return Container();
}
},
),
),
);
}
}
Since you're using get() when the widget is created, the data is only loaded from the database once when the widget is created. If you want to get the new data whenever it is updated, use a snapshot() listener - which returns a stream which gets an initial event with the initial data, and a new event whenever the data is updated.
To wire the Stream up in your build method, you'll want to use a StreamBuilder as shown in the Firebase documentation on listening for realtime updates in Flutter.
i wanna get a Data from GetStorage(),
i got a Shop List and i can use it as Static for only page, that way im saving my List in GetStorage,if i go to another Page that will showed also. Like a SharedPreferences. How can i show a data from GetStorage List Value , like
Text(basket[name]), that will show only the Names,
Text(basket[price]), that will show only Prices,
var basketsd = GetStorage("totalList");
My ProductModel for Shop,
class PriceModel2 {
final String name;
final double price;
PriceModel2({
this.name,
this.price,
});
}
its also not worked.
Text(controller.basketsd.read(["name"])
Container(
height: Get.size.height * 0.3,
child: Obx(() => ListView.builder(
itemCount: controller.basket.length,
itemBuilder: (BuildContext context, int index) {
return Column(
children: [
SizedBox(
height: 20,
),
Container(
width: Get.size.width,
child: ElevatedButton(
onPressed: () {
controller.basket
.remove(controller.basket[index]);
},
child: (Text(controller.basketsd.read(["name"]) +
" " +
controller.basket[index].price.toString() +
" €"))),
),
SizedBox(
height: 20,
),
],
);
})),
),
Codes show like this ,
(https://prnt.sc/1fs6mbf)
There's a couple issues here. At least from what you're sharing, you're never really storing your PriceModel2 object properly. If you try and store a random custom object, GetStorage will not know what to do with it. (Neither would SharedPreferences in the same scenario).
So for starters, you can implement a toMap function that converts it to a map that GetStorage can read.
Add this to your PriceModel2 class.
Map<String, dynamic> toMap() {
return {
'name': name,
'price': price,
};
}
Then you add a named constructor in the same class so you can create your object from the Map that you stored.
PriceModel2.fromMap(Map map)
: name = map['name'],
price = map['price'];
I also suggest, regardless of whichever storage library you're using, keeping all storage related functionality, including boxes in this case, in its own class, that stores and returns whatever data you need. This way if you ever need to upgrade to Hive, ObjectBox etc... you do it in one class and not all over your app.
An example would look like this.
class Database extends GetxController {
final box = GetStorage();
Future<void> initStorage() async {
await GetStorage.init();
}
void storePriceModel(PriceModel2 model) {
box.write('model', model.toMap());
}
PriceModel2 restoreModel() {
final map = box.read('model') ?? {};
return PriceModel2.fromMap(map);
}
}
Then you would initialize your controller and storage in main.
void main() async {
await Get.put(Database()).initStorage();
runApp(MyApp());
}
An example of storing a model would look like this.
ElevatedButton(
onPressed: () {
final model = PriceModel2(name: 'test name', price: 23.0);
Get.find<Database>().storePriceModel(model);
},
child: Text('Store Model'),
),
And displaying the storage data in the UI could be done like so.
Text(Get.find<Database>().restoreModel().name)
You don't need an Obx just to display the stored data because its not an observable stream based variable.
As for displaying a list of products you do the same thing but store a list of maps instead of a single map.
If you need help with that then share how you're trying to add and store to the PriceModel2 list.
I'm new of Flutter and Dart in general, I'm trying to do a expansive computation during the loading of the page but the loader is stuck when I try to do something like this:
body: Center(
child:FutureBuilder(
future: _lorem()
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done){
print("loader");
return Center(
child: CircularProgressIndicator(
backgroundColor: Theme.of(context).primaryColor)
);
}
[...]
Future<void> _lorem() async {
//there is not a request to service, there is a more than one filter on map and some lists. I set the for loop for example of a local computation
return Future(() {
for (int i = 0; i < 50000; i++){
print(i);
}
}
);
}
I think the easier way to implement this is using a field in your widget of type Completer, eg Completer calc. You can start your expensive computation in your widget initialization (never in your build function), and when the computation is done you complete that Completer by calling calc.complete().
In your widget's FutureBuilder you should then listen to calc's future by including future: calc.future instead of your future: _lorem().
See FutureBuilder for an example of this UI paradigm.
Solved with a Future and compute.
In the detail:
Future<List<CustomObject>> _retrieveCustomObjects() async {
SourceData data = SourceData(CustomSourceData());
return compute(getFilteredClients, data);
}
List<CustomObject> computeCustomObject(SourceData data) {
List<CustomObject> list = [];
// expensive logic on data, not only network call
return list;
}
class LoremIpsumClass {
// use where you need `List<CustomObject> value = await _retrieveFilteredClient();`
}
I am dealing with the Futurebuilder in the Flutter, and I want to create a Future function that is going to take data from my Firebase. After that, I want to use that data inside of the text widget, so I tried to write this function to take data from firebase.
Future getData() async {
var fb = Firestore.instance;
DocumentSnapshot dr = (await fb.collection("records").document("the record").get());
return dr.toString();
}
But when I tried to use that data inside of the FutureBuilder, I am taking just Instance of 'DocumentSnapsot' text.
Here you can see my FutureBuilder codes
body: Center(
child: FutureBuilder(
future: getData(),
builder: (_ ,snapshot){
if(snapshot.connectionState==ConnectionState.waiting) {
return RaisedButton(
child: Text("loading" ),
onPressed: null,
);
}
if(snapshot.connectionState==ConnectionState.done) {
return RaisedButton(
child: Text(snapshot.data),
onPressed: () {},
);
}
return null;
}
And this is the result, after the running program:
Also, This my firebase. I just want to reach, and manipulate 0 value inside of the program.
The DocumentSnapshot toString method does not provide the data contained within the document you are trying to retrieve. To obtain the data in the document from a DocumentSnapshot, do dr.data, which returns a Map containing the fields and the associated data of those fields stored in the document. This should be done in your getData() method.
The toString method for DocumentSnapshot returns "Instance of 'DocumentSnapshot'", hence why your button is showing that instead of what you intend.
I'm retrieving data from firestore and setting the inital value of the textformfield with this data. I am able to get the data, However the data displayed is enclosed in braces and have tried to use regex patterns to work with this but to no avail. Any help will be appreciated
I have tried using regex expression to work around this but nothings working so far.
Widget _nextOfKinFirstName() {
return StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('farmer_profile').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snap) {
var name = snap.data.documents.map((DocumentSnapshot docs) {
return docs['first_name'];
}).toString();
return TextFormField(
initialValue: name,
validator: (name) {
Pattern pattern = r'^[a-zA-Z]+$';
RegExp exp = new RegExp(pattern);
if (!exp.hasMatch(name))
return 'enter name';
else
return null;
},
);
},
);
}
i expect the output to be a string value not enclosed in braces.
This is the output i have
output on textformfield
while this is the database am testing how to implement with.
simple database i'm trying this with