flutter firebase get string from database using future - firebase

I want to get a string from my DB in Firebase, I'm very confused and I don't know how to do that!
I made a big search in the few past days about this idea but unf I don't get any useful result
what do I want? I want to make a Method that returns the 'Question' string.
DB:Collection / History/question
thank you for your time
the incorrect code :
Future loadData() async {
await Firebase.initializeApp();
if (snapshot.hasError) {
return Scaffold(
body: Center(
child: Text("Error: ${snapshot.error}"),
),
);
}
// Collection Data ready to display
if (snapshot.connectionState == ConnectionState.done) {
// Display the data inside a list view
return snapshot.data.docs.map(
(document) {
return method(
document.data()['question'].toString().toString(),
); //Center(
},
);
}
}

Here is the official documentation from Flutter Fire - https://firebase.flutter.dev/docs/firestore/usage/
Read data from Cloud firestore
Cloud Firestore gives you the ability to read the value of a collection or a document. This can be a one-time read or provided by real-time updates when the data within a query changes.
One-time Read
To read a collection or document once, call the Query.get or DocumentReference.get methods. In the below example a FutureBuilder is used to help manage the state of the request:
class GetUserName extends StatelessWidget {
final String documentId;
GetUserName(this.documentId);
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('users');
return FutureBuilder<DocumentSnapshot>(
future: users.doc(documentId).get(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data.exists) {
return Text("Document does not exist");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data = snapshot.data.data();
return Text("Full Name: ${data['full_name']} ${data['last_name']}");
}
return Text("loading");
},
);
}
}
To learn more about reading data whilst offline, view the Access Data Offline documentation.
Realtime changes
FlutterFire provides support for dealing with real-time changes to collections and documents. A new event is provided on the initial request, and any subsequent changes to collection/document whenever a change occurs (modification, deleted, or added).
Both the CollectionReference & DocumentReference provide a snapshots() method which returns a Stream:
Stream collectionStream = FirebaseFirestore.instance.collection('users').snapshots();
Stream documentStream = FirebaseFirestore.instance.collection('users').doc('ABC123').snapshots();
Once returned, you can subscribe to updates via the listen() method. The below example uses a StreamBuilder which helps automatically manage the streams state and disposal of the stream when it's no longer used within your app:
class UserInformation extends StatefulWidget {
#override
_UserInformationState createState() => _UserInformationState();
}
class _UserInformationState extends State<UserInformation> {
final Stream<QuerySnapshot> _usersStream = FirebaseFirestore.instance.collection('users').snapshots();
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _usersStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return new ListView(
children: snapshot.data.docs.map((DocumentSnapshot document) {
return new ListTile(
title: new Text(document.data()['full_name']),
subtitle: new Text(document.data()['company']),
);
}).toList(),
);
},
);
}
}
By default, listeners do not update if there is a change that only affects the metadata. If you want to receive events when the document or query metadata changes, you can pass includeMetadataChanges to the snapshots method:
FirebaseFirestore.instance
.collection('users')
.snapshots(includeMetadataChanges: true)

Related

Flutter Firestore: FirestoreBuilder with initial data

I'm making my first Flutter app and I encounter a problem and doesn't found any solution for it.
I have a view where I render a Firestore document, and there is two ways of getting there:
From a list where I already loaded my documents
From Dynamic Links with uid attached as arguments (args)
So in order to listen document changes and loading the data when arriving from the link I used FirestoreBuilder like this:
return FirestoreBuilder<EventDocumentSnapshot>(
ref: eventsRef.doc(args.uid),
builder: (context, AsyncSnapshot<EventDocumentSnapshot> snapshot, Widget? child) {
if (!snapshot.hasData) {
return Container();
}
Event? event = snapshot.requireData.data;
return Scafold(); //Rest of my rendering code
}
);
How I could avoid first call to Firebase when I already have the data but still listen to changes? The main problem is that my hero animation doesn't work because of this.
I tried with a StreamBuilder and initialDataparam but since it's expecting stream I didn't know how to cast my data.
Okay, so I found the solution myself after many tries, so I added my Model object that can be null as initialData, but the thing that makes me struggle with is how you get the data in the builder. You have to call different methods depending on where the data is coming from.
return StreamBuilder(
initialData: args.event
ref: eventsRef.doc(args.uid),
builder: (context, AsyncSnapshot<dynamic> snapshot) {
// Here is the trick, when data is coming from initialData you only
// need to call requireData to get your Model
Event event = snapshot.requireData is EventDocumentSnapshot ? snapshot.requireData.data : snapshot.requireData;
return Scafold(); //Rest of my rendering code
}
);
Reading through cloud_firestore's documentation you can see that a Stream from a Query can be obtained via snapshots()
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('books').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return new Text('Loading...');
return new ListView(
children: snapshot.data.documents.map((DocumentSnapshot document) {
return new ListTile(
title: new Text(document['title']),
subtitle: new Text(document['author']),
);
}).toList(),
);
},
);
This won't help you, but with GetX it's simple to implement like this: You don't need StreamBuilder anymore.
//GetXcontroller
class pageController extends GetXcontroller {
...
RxList<EventModel> events = RxList<EventModel>([]);
Stream<List<EventModel>> eventStream(User? firebaseUser) =>
FirebaseFirestore.instance
.collection('events')
.snapshots()
.map((query) =>
query.docs.map((item) => UserModel.fromMap(item)).toList());
#override
void onReady() async {
super.onReady();
events.bindStream(
eventStream(controller.firebaseUser)); // subscribe any change of events collection
}
#override
onClose() {
super.onClose();
events.close(); //close rxObject to stop stream
}
...
}
You can use document snapshots on StreamBuilder.stream. You might want to abstract the call to firebase and map it to an entity you defined.
MyEntity fromSnapshot(DocumentSnapshot<Map<String, dynamic>> snap) {
final data = snap.data()!;
return MyEntity (
id: snap.id,
name: data['name'],
);
}
Stream<MyEntity> streamEntity(String uid) {
return firebaseCollection
.doc(uid)
.snapshots()
.map((snapshot) => fromSnapshot(snapshot));
}
return StreamBuilder<MyEntity>(
// you can use firebaseCollection.doc(uid).snapshots() directly
stream: streamEntity(args.uid),
builder: (context, snapshot) {
if (snapshot.hasData) {
// do something with snapshot.data
return Scaffold(...);
} else {
// e.g. return progress indicator if there is no data
return Center(child: CircularProgressIndicator());
}
},
);
For more complex data models you might want to look at simple state management or patterns such as BLoC.

How to retreive data from firestore flutter

I'm new into flutter and firebase integrations and I'm having some troubles to retreive all the data from the firebase collection.
I have tried this method:
getCollection() {
CollectionReference coleccion =
FirebaseFirestore.instance.collection('materias');
return Container(
child: StreamBuilder(
stream: coleccion.doc('aprobadas').snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
return Text(snapshot.data.data()['codigo'],
style: TextStyle(fontSize: 50, color: Colors.white));
} else {
return CircularProgressIndicator();
}
},
),
);
}
Now I'm a little bit frustrated because I have tried a differents methods and doesn't work.
I really appreciate all the help.
Best regards
Data can be retrieved using the below code from firestore to flutter.
One-time Read
call the Query.get or DocumentReference.get methods
class GetUserName extends StatelessWidget {
final String documentId;
GetUserName(this.documentId);
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('users');
return FutureBuilder<DocumentSnapshot>(
future: users.doc(documentId).get(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {
return Text("Document does not exist");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data = snapshot.data!.data() as Map<String, dynamic>;
return Text("Full Name: ${data['full_name']} ${data['last_name']}");
}
return Text("loading");
},
);
}
}
Realtime changes
FlutterFire provides support for dealing with real-time changes to collections and documents. A new event is provided on the initial request, and any subsequent changes to collection/document whenever a change occurs (modification, deleted or added).
Both the CollectionReference & DocumentReference provide a snapshots() method which returns a Stream:
Stream collectionStream = FirebaseFirestore.instance.collection('users').snapshots();
Stream documentStream = FirebaseFirestore.instance.collection('users').doc('ABC123').snapshots();
Please refer official documentation here
You can use a StreamBuilder. That will be easy to understand.
StreamBuilder(
stream: FirebaseFirestore.instance.collection("collection").snapshot,
builder: (BuildContext context,snapshot) {
if(snapshot.hasdata!=true) {
return CircularProgressIndicator();
} else {
return ListView.builder(
itemcount:snapshot.data.docs.length,
builder(context,index) {
return Text(snapshot.data.docs[index].data()["filedname"]);
}
}
)

Not Showing Placeholder Image even when url in networkimage become's null

GetPhotoUrlStream provide a stream of Url of Profile Photo (data['profilePhoto']) Stored in my Cloud Firebase FireStore. which then being utilized by networkimage to show the profilephoto(circular Avatar)
class GetUserPhotoUrlStream extends StatelessWidget {
final String documentId; // This is your User UID
GetUserPhotoUrlStream(this.documentId);
#override
Widget build(BuildContext context) {
DocumentReference users = FirebaseFirestore.instance.collection('users').doc(documentId);
return StreamBuilder<DocumentSnapshot>(
stream: users.snapshots(),
builder: (BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Image.asset('assets/images/NouserImage.png');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
Map<String, dynamic> data = snapshot.data.data();
return CircleAvatar(
maxRadius: 80,
backgroundColor: Colors.grey,
child: ClipOval(child: FadeInImage(placeholder: AssetImage('assets/images/NouserImage.png'),image: NetworkImage("${data['profilePhoto']}"),),),
);
},
);
}
}
removeUserPhotoUrl update's the 'profilePhoto' to null which is being used by GetUserPhotoUrlStream.
Future<void> removeUserPhotoUrl(BuildContext context) async
{
var user = _auth.currentUser;
DocumentReference users = FirebaseFirestore.instance.collection('users').doc(user.uid);
users.update({'profilePhoto':null}).then((_){
Navigator.pop(context);
});
await deleteUserImage(context);
notifyListeners();
}
when the value of data['profilePhoto'] is made null using removeUserPhotoUrl it should show me placeholder image which is Provides an assetImage rather it gives an error
Error Message
====================================================================================================
======== Exception caught by image resource service ================================================
Invalid argument(s): No host specified in URI file:///null
====================================================================================================
Also When app is HotReload or HotRestart The Error is Gone and it start Showing Me the PlaceHolder(Asset Image)
Please Help.I want to show Placeholder(Asset Image) as soon as the 'profilePhoto' becomes null
Try this and also make sure that Your images is places inside pubspec.yaml
if (!snapshot.hasData) {
return Image.asset('assets/images/NouserImage.png');
}

Flutter /Firebase - Accessing the realtime database

In my flutter project, I need to access the realtime database to get the name of the user when they create a post. This is the code I'm working with:
class PostScreen extends StatelessWidget {
static const String idScreen = 'post';
final String name1 = FirebaseDatabase.instance
.reference()
.child('users')
.child(FirebaseAuth.instance.currentUser.uid)
.child('name1')
.toString();
final String name2 = FirebaseDatabase.instance
.reference()
.child('users')
.child(FirebaseAuth.instance.currentUser.uid)
.child('name2')
.toString();
#override
Widget build(BuildContext context) {
return Scaffold(
body: FlatButton(
child: Text('Create Post'),
onPressed: () {
MainScreen.posts.add(Post(
name1: name1,
name2: name2,
));
Navigator.pushNamed(context, MainScreen.idScreen);
},
),
);
}
}
class Post extends StatelessWidget {
String name1 = '';
String name2 = '';
Post({#required name1, #required name2});
#override
Widget build(BuildContext context) {
return Card(
child: Row(
children: [
Text(name1),
Text(" and "),
Text(name2),
],
),
);
}
}
What happens though is that the name is left blank and just creates a card that says " and ". What could I be doing wrong?
Your code doesn't read anything from the database yet. For that to happen you need to call the once() stream, or listen to the onValue or onChild... streams.
I also recommend simplifying your problem before continuing. So instead of reading the data for the current user (which requires that you have a current user), simply write some hard-coded data at a known location in the database and read that first. That should look something like this:
final DatabaseReference ref = FirebaseDatabase.instance
.reference()
.child('test');
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: ref.onValue,
builder: (BuildContext context, snapshot) {
if(snapshot.hasData) => return Text(snapshot.value);
else if(snapshot.hasError) => return Text("Error");
else return Text("No data (yet)");
}
);
}
There may be syntax errors in this code, so treat it as an example of an approach instead of a copy/paste solution please. If you find any of such errors, please try to solve them on your own - and edit the answer with any fixes.
Also see:
How To Use Firebase RTDB with Flutter Stream
more of these search results
You should be able to do something like this:
DatabaseReference myRef = FirebaseDatabase.instance
.reference()
.child('users')
.child(FirebaseAuth.instance.currentUser.uid)
.child('name1');
StreamBuilder(
stream: myRef.onValue,
builder: (context, AsyncSnapshot<Event> snap) {
if (snap.hasData && !snap.hasError && snap.data.snapshot.value != null) {
// Handle snapshot data
}
}
If you don't need to continue getting changes from the location you can probably use a future builder and .once() method. Don't have experience with that myself though.

Flutter firebase auth doesn't persist after app is killed

I am trying to persist the firebase auth state in a flutter app by using this code from the documentation but when I kill the app in the emulator and open it again it doesn't recognize a user.
I can use sharedpreferences but I want to use only firebase, what am I doing wrong here?
main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// Create the initialization Future outside of `build`:
final Future<FirebaseApp> _initialization = Firebase.initializeApp();
final FirebaseAuth auth = FirebaseAuth.instance;
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return FutureBuilder(
// Initialize FlutterFire:
future: _initialization,
builder: (context, snapshot) {
// Check for errors
if (snapshot.hasError) {
return (MaterialApp(
home: Warning(
warning: 'Error',
),
));
}
// once complete show your app
if (snapshot.connectionState == ConnectionState.done) {
print('CONNECTED');
if (AuthService().user() == null) {
return MaterialApp(
home: LoginPage(),
);
} else {
return MaterialApp(
home: HomePage(),
);
}
}
// if nothing happens return loading
return MaterialApp(
home: //LoginPage()
Warning(
warning: 'Loading',
),
);
},
);
}
}
AuthService class
import 'package:firebase_auth/firebase_auth.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// auth change user stream
User user() {
// ignore: deprecated_member_use
_auth.authStateChanges().listen((User user) {
if (user == null) {
return null;
} else {
return user;
}
});
}
}
I hope you can help me to understand the problem and solve it, thank you.
Since authStateChanges returns a Stream, you'll want to use a StreamBuilder in your code to wrap that asynchronous operation too.
Something like this:
// once complete show your app
if (snapshot.connectionState == ConnectionState.done) {
print('CONNECTED');
return StreamBuilder(
stream: FirebaseAuth.instance. authStateChanges(),
builder: (BuildContext context, snapshot) {
if (snapshot.hasData) {
return MaterialApp(
home: LoginPage(),
);
} else {
return MaterialApp(
home: HomePage(),
);
}
}
)
}
Unrelated: you're repeated the code to create a MaterialApp quite frequently, which is not needed. For example, in the above snippet we could have only one mention of MaterialApp and get the same result with:
// once complete show your app
if (snapshot.connectionState == ConnectionState.done) {
print('CONNECTED');
return StreamBuilder(
stream: FirebaseAuth.instance. authStateChanges(),
builder: (BuildContext context, snapshot) {
return MaterialApp(
home: snapshot.hasData && snapshot.data != null ? HomePage() : LoginPage(),
)
}
)
}
If you do this for all mentions of MaterialApp and other duplication, you can reduce the code significantly, making it less error prone and easier to maintain.
It does persist. You are just not using the auth state listener correctly. The return statements from your authStateChanges listener are not actually escaping the call to user(). On top of that, the listener could return null the first time. It's not until some time later that the Firebase SDK determines that the user is actually valid and signed in. Your listener will get a second callback at that time. Your code need to be ready for this to happen - it can't just blindly take the first value, as the auth state might change over time.
I suggest adding some debug logging in your auth state listener to see how this actually works. Also I suggest reading this blog to understand how auth state listeners work in more detail.
You can use my code, You can use userChanges() instead of authStateChanges()
final Stream<User?> firebaseUserChanges = firebaseAuth.userChanges();

Resources