can anybody show me how to replace the static list (_dataFromQuerySnapShot) with Firestore Firebase QuerySnapshot? Thank you!
`
import 'dart:async';
import 'package:flutter/material.dart';
class SearchWidget extends StatelessWidget {
const SearchWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: <Widget>[
const TextField(onChanged: _filter),
StreamBuilder<List<User>>(
stream: _stream,
builder: (context, snapshot) {
return StreamBuilder<List<User>>(
key: ValueKey(snapshot.data),
initialData: snapshot.data,
stream: _stream,
builder: (context, snapshot) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data?.length,
itemBuilder: (BuildContext context, int index) {
final data = snapshot.data?[index]?.name;
return Text(data!);
},
);
},
);
},
)
],
),
);
}
}
StreamController<List<MyUser>> _streamController = StreamController<List<MyUser>>();
Stream<List<MyUser>> get _stream => _streamController.stream;
_filter(String searchQuery) async {
List<MyUser> _dataFromQuerySnapShot = await getData();
List<MyUser> _filteredList = _dataFromQuerySnapShot.where((MyUser user) => user.firstName!.toLowerCase().contains(searchQuery.toLowerCase())).toList();
_streamController.sink.add(_filteredList);
}
Future<List<MyUser>> getData() async {
List<MyUser> dataList = [];
CollectionReference myRef = FirebaseFirestore.instance.collection('users');
QuerySnapshot querySnapshot = await myRef.get();
dataList.addAll(querySnapshot.docs.map((d) => MyUser.fromJson(d.data() as Map<String, dynamic>)));
return dataList;
}
`
I tried to get data by Future<List> getData() {...} but List _filteredList = getData(); doesn't work as well as a lot of other tries. I only need to see a simple solution of my example working with firebase (StreamController a.s.o.) please. The best solution will include instead of searching in name searching in a String of 'firstName', 'lastName', and 'nickName'...
Related
I'm attempting display data from two diffrent collections within firestore , I treied to nest both streambuilds so i can particulary display the data as one stream , however I keep on getting the error bad state field doesnt exist with doc snapshot how can i fixing thus error , or is there another much more effective method i can use to display data from two diffrent collections in one class?
below is screenshot of the data(s) i want to display:
class OrderStream extends StatelessWidget {
static const String route = "/Order";
final CollectionReference meal =
FirebaseFirestore.instance.collection("menu");
final CollectionReference profile =
FirebaseFirestore.instance.collection("users");
OrderStream({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: profile.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
return StreamBuilder(
stream: meal.snapshots(),
builder:
(context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (!streamSnapshot.hasData) {
return const SizedBox(
height: 250,
child: Center(
child: CircularProgressIndicator(),
),
);
} else {
return ListView.builder(
itemCount: streamSnapshot.data!.docs.length,
itemBuilder: (context, index) {
final DocumentSnapshot documentSnapshot =
streamSnapshot.data!.docs[index];
return Column(
children: [
Text( documentSnapshot['price'],)
Text( documentSnapshot['name'],)
]
),
),
}
This is probably happening due to similar name for both snapshots.
The best way to check this is by renaming the snapshot for individual Streambuilder().
StreamBuilder(
stream: profile.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> profileStreamSnapshot) {
return StreamBuilder(
stream: meal.snapshots(),
builder:
(context, AsyncSnapshot<QuerySnapshot> mealStreamSnapshot) {
if (!streamSnapshot.hasData) {
//modified (renamed snapshot variable) code here
}
You can merge those two streams into 1 using library like rxdart which has combineLatest2 method although you can also use something like StreamZip to get the same effect.
I have used rxdart combineLatest2 as follows:
import 'package:rxdart/rxdart.dart';//import ⇐
class MyHomePage extends StatelessWidget {
final CollectionReference profile =
FirebaseFirestore.instance.collection("users");
final CollectionReference meal =
FirebaseFirestore.instance.collection("menu");
MyHomePage({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: Rx.combineLatest2(profile.snapshots(), meal.snapshots(),
(QuerySnapshot profileSnapshot, QuerySnapshot mealSnapshot) {
return [...profileSnapshot.docs, ...mealSnapshot.docs];
}),
builder: (context, AsyncSnapshot<List<DocumentSnapshot>> snapshot) {
if (!snapshot.hasData) {
return const SizedBox(
height: 250,
child: Center(
child: CircularProgressIndicator(),
),
);
} else {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
final DocumentSnapshot documentSnapshot =
snapshot.data![index];
final Map<String, dynamic> data =
documentSnapshot.data() as Map<String, dynamic>;
if (data.containsKey("price") && data.containsKey("name")) {
return Column(
children: [Text(data["price"]), Text(data["name"])],
);
} else {
return Container();
}
},
);
}
}),
);
}
}
You can also use Stream.merge() as follows:
final Stream<QuerySnapshot> mealsStream = meal.snapshots();
final Stream<QuerySnapshot> profilesStream = profile.snapshots();
//.. All that Scaffold stuff
stream: Stream.merge([mealsStream, profilesStream]),
I'm trying to fetch documents from my firebase DB and use them to create a social media feed. Here I'm trying to get the length of the fetched collection but I cannot manage to call the variable. Any help would be appreciated. Example code
class LoadDataFromFirestore extends StatefulWidget {
#override
_LoadDataFromFirestoreState createState() => _LoadDataFromFirestoreState();
}
class _LoadDataFromFirestoreState extends State<LoadDataFromFirestore> {
#override
void initState() {
super.initState();
CollectionReference _collectionRef =
FirebaseFirestore.instance.collection('fish');
Future<void> getData() async {
// Get docs from collection reference
QuerySnapshot querySnapshot = await _collectionRef.get();
// Get data from docs and convert map to List
final allData = querySnapshot.docs.map((doc) => doc.data()).toList();
print(allData);
}
}
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: querySnapshot.docs.length,
itemBuilder: (BuildContext context, int index) {
return _postView();
},
),
);
}
}
First of all it is not ok to call future function in initstate, you need to use FutureBuilder like this:
class LoadDataFromFirestore extends StatefulWidget {
#override
_LoadDataFromFirestoreState createState() => _LoadDataFromFirestoreState();
}
class _LoadDataFromFirestoreState extends State<LoadDataFromFirestore> {
late CollectionReference _collectionRef;
#override
void initState() {
super.initState();
_collectionRef = FirebaseFirestore.instance.collection('fish');
}
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<QuerySnapshot>(
future: _collectionRef.get(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
QuerySnapshot? querySnapshot = snapshot.data;
return ListView.builder(
itemCount: querySnapshot?.docs?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
var data = querySnapshot?.docs?[index].data();
print("data = $data");
return _postView();
},
);
}
}
},
),
);
}
}
inside listview's builder you can use data to parse your data and use it.
You can use FutureBuilder like this:
class LoadDataFromFirestore extends StatefulWidget {
const LoadDataFromFirestore({super.key});
#override
State<LoadDataFromFirestore> createState() => _LoadDataFromFirestoreState();
}
class _LoadDataFromFirestoreState extends State<LoadDataFromFirestore> {
//TODO change Map<String, dynamic> with your data type with fromJson for example
Future<List<Map<String, dynamic>>> _getData() async {
final querySnapshot = await FirebaseFirestore.instance.collection('fish').get();
return querySnapshot.docs.map((doc) => doc.data()).toList();
}
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<List<Map<String, dynamic>>>(
future: _getData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return _postView(/* Ithink you have to pass here your item like snapshot.data[index]*/);
},
);
} else {
return const Center(child: CircularProgressIndicator());
}
},
),
);
}
}
i want to make a grid of image by 3 like instagram based on user upload. the streambuilder is not displaying any data from firestore.
i already separate the stream so its not inside the streambuilder.
this is the code
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
File? imageFile;
String? imageUrl;
final FirebaseAuth _auth = FirebaseAuth.instance;
String? myImage;
String? myName;
String buttonName = 'click';
int currentIndex = 0;
final icon = CupertinoIcons.chat_bubble_2;
final _constructed = FirebaseFirestore.instance
.collection('fotoupload')
.orderBy("createAt", descending: true)
.snapshots(); //construct stream first
//final Stream<QuerySnapshot> _constructed = FirebaseFirestore.instance
// .collection('fotoupload')
// .orderBy("createAt", descending: true)
// .snapshots();
void read_userInfo() async {
FirebaseAuth.instance.currentUser!;
FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.get()
.then<dynamic>((DocumentSnapshot snapshot) async {
myImage = snapshot.get('userImage');
myName = snapshot.get('name');
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
read_userInfo(); // refresh and read the user info both myName and myImage
}
Widget gridViewWidget(String docId, String img, String userImg, String name,
DateTime date, String userId, int downloads) {
return GridView.count(
primary: false,
padding: EdgeInsets.all(5),
crossAxisSpacing: 1,
crossAxisCount: 1,
children: [
GestureDetector(
onTap: () {
//createOwnerDetails
},
child: Center(
child: Text(date.toString()),
),
),
GestureDetector(
onTap: () {
//createOwnerDetails
},
child: Image.network(
img,
fit: BoxFit.cover,
),
),
Center(child: Text(userId)),
],
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<QuerySnapshot>(
stream: _constructed,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.data!.docs.isNotEmpty) {
return GridView.builder(
itemCount: snapshot.data!.docs.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
itemBuilder: (BuildContext context, int index) {
return gridViewWidget(
snapshot.data!.docs[index].id,
snapshot.data!.docs[index]['Image'],
snapshot.data!.docs[index]['userImage'],
snapshot.data!.docs[index]['name'],
snapshot.data!.docs[index]['createAt '].toDate(),
snapshot.data!.docs[index]['uid'],
snapshot.data!.docs[index]['downloads'],
);
},
);
} else {
return Center(
child: Text(
'There is no tasks',
style: TextStyle(fontSize: 20),
),
);
}
}
return Center(
child: Text(
'Something went wrong',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30),
),
);
},
),
);
}
}
does anyone have suggestion? the streambuilder is not displaying the data. and its shows "There is no task".
You are reconstructing the stream again after every build, I recommend you to construct the stream in a private variable then use it in the stream in place of
FirebaseFirestore.instance.collection('fotoupload').orderBy("createAt", descending: true).snapshots()
as documented here It is implemented like this
class _HomePageState extends State<HomePage> {
final _constrcted = FirebaseFirestore.instance
.collection('fotoupload')
.orderBy("createAt", descending: true)
.snapshots();
//....
#override
Widget build(BuildContext context) {
return Scaffold(body: StreamBuilder<QuerySnapshot>(
stream: _constrcted,
builder: (context, snapshot) {
//...
}
stream: FirebaseFirestore.instance
.collection('fotoupload')
.orderBy("createAt", descending: true)
.snapshots(),
There's your prooblem. Do not create the stream in the stream: parameter of a StreamBuilder. It will get re-created on each call to build(), up to 60 times per second if there's an animation on the page. This doesn't give time for any data to appear... in fact it throws it away on each rebuild.
Follow the advice from the FutureBuilder and StreamBuilder documentation: create and initialize the stream in the state and initState of your widget. The value must remain constant for StreamBuilder to do its job.
I have more details and a demonstration at https://youtu.be/sqE-J8YJnpg.
I want to fetch a field value from firestore and assign it to String variable.
So how can I do this?
When I fetch data it comes as a map.
So in which way I can fetch data as string
screena.dart
class ScreenA extends StatefulWidget {
const ScreenA({Key? key}) : super(key: key);
#override
State<ScreenA> createState() => _ScreenAState();
}
class _ScreenAState extends State<ScreenA> {
List<String> docIDs = [];
Future getDocId() async {
final userId = AuthService.firebase().currentUser!.id;
await FirebaseFirestore.instance
.collection('user')
.where('user_id', isEqualTo: userId)
.get()
.then(
(snapshot) => snapshot.docs.forEach(
(document) {
docIDs.add(document.reference.id);
},
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: getDocId(),
builder: (context, snapshot) {
return ListView.builder(
shrinkWrap: true,
itemCount: docIDs.length,
itemBuilder: (context, index) {
return ListTile(title: Gettt(docId: docIDs[index]));
},
);
},
),
);
}
}
gettt.dart
class Gettt extends StatelessWidget {
final String docId;
Gettt({Key? key, required this.docId}) : super(key: key);
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('user');
return FutureBuilder<DocumentSnapshot>(
future: users.doc(docId).get(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
return Text('Locker Id: ${data['first_name']}');
}
return const Text('Loading');
},
);
}
}
here's my code
sorry for the last time
I can get a list of items
But I want to without getting a widget I need to get a String
How to do it?
after get document, you can get field example[dart]:
I'm a beginner in Flutter and BLOC, i have a problem when trying to fetch Country List data.
Error was NoSuchMethodError: The getter 'bloc' was called on null.
below was my code. I'm just follow this tutorial https://www.youtube.com/watch?v=2DP3SEHucEk
this is Country List widget
class CountryList extends StatefulWidget {
#override
_CountryListState createState() => _CountryListState();
}
class _CountryListState extends State<CountryList> {
#override
void initState() {
Future.delayed(Duration.zero, () async {
final bloc = CountryRepo.of(context);
bloc.fetchCountry();
});
super.initState();
}
#override
Widget build(BuildContext context) {
return CountryRepo(child: Builder(
builder: (context) {
final bloc = CountryRepo.of(context);
return StreamBuilder<List<CountryModel>>(
stream: bloc.countries,
builder: (context, snapshot) {
if (!snapshot.hasData)
return Center(child: CircularProgressIndicator());
return ListView.separated(
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Row(
children: <Widget>[
Image.asset(snapshot.data[index].flag, width: 20),
SizedBox(width: 10),
Text(
"${snapshot.data[index].name} (+${snapshot.data[index].callingCodes})"),
],
),
onTap: () {
Navigator.pop(context);
},
);
},
separatorBuilder: (context, index) {
return Divider();
},
);
},
);
},
));
}
}
this is Country provider
import 'package:bpg/bloc/country_block.dart';
import 'package:flutter/material.dart';
class CountryRepo extends InheritedWidget {
final CountryBloc bloc;
CountryRepo({Key key, Widget child})
: bloc = CountryBloc(),
super(key: key, child: child);
static CountryBloc of(BuildContext context) {
return (context.dependOnInheritedWidgetOfExactType() as CountryRepo).bloc;
}
bool updateShouldNotify(_) => true;
}
and this Country BLOC
import 'package:bpg/model/country_model.dart';
import 'package:rxdart/rxdart.dart';
import 'package:http/http.dart' as http;
import 'dart:convert' as convert;
class CountryBloc {
final _countries = BehaviorSubject<List<CountryModel>>();
//Get Data
Stream<List<CountryModel>> get countries => _countries.stream;
//Set Data
Function(List<CountryModel>) get changeCountry => _countries.sink.add;
dispose() {
_countries.close();
}
Future<void> fetchCountry() async {
var response = await http.get("https://restcountries.eu/rest/v2/all");
var jsonResponse = convert.jsonDecode(response.body);
var countryJson = jsonResponse as List;
List<CountryModel> countries = List<CountryModel>();
countryJson
.forEach((country) => {countries.add(CountryModel.fromJson(country))});
changeCountry(countries);
countries.forEach((country) => print(country.name));
}
}