Fetching Data from a Realtime Firestore Document - firebase

I have seen similar questions and answers being solved with the StreamBuilder widget.
In my case when I am implementing it my code does not await to fetch the data and just moves on (in my case, the app jumps to the next page). Thus, do I need the build a StreamBuilder Widget or is there a simple method that could work and fetch the data in realtime?
I noticed that I did not use async* with the asterisc but if I do so, then the authentication is not working.
Clarification:
The code does not enter the following lines:
if (!snapshot.hasData)
return new Text('Loading...');
return new Text(
snapshot.data.data['name']
);
Also the print(test); statement prints the following:
StreamBuilder<DocumentSnapshot>
Here is the whole part:
onPressed: () async {
setState(() {
showSpinner = true;
});
try {
LoginScreen.user =
await _auth.signInWithEmailAndPassword(
email: email, password: password);
if (LoginScreen.user != null) {
// get the users data and save them
if (LoginScreen.user.user.uid !=
'IDVwQXAsZas213Va0OIH2IsoU5asdaTfraBJ2') {
Widget test = await StreamBuilder<DocumentSnapshot>(
stream: _firestore
.collection('Employees')
.document(LoginScreen.user.user.uid)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
if (!snapshot.hasData)
return new Text('Loading...');
return new Text(
snapshot.data.data['name']
);
},
);
print(test);
Navigator.pushReplacementNamed(
context, TabCreator.screenId);
} else {
}
}
} catch (e) {
print(e);
// when getting an erro stop spinner
setState(() {
showSpinner = false;
});
}
}
Update:
I created a new standard flutter project in order to see if there was something else within my code that was messing the StreamBuilder. I am still getting no output.
On a side note when I am implementing the following code within the onPressed method I am getting the wanted result:
Alternative Solution:
onPressed: () {
DocumentReference documentReference = await Firestore.instance
.collection('Employees')
.document('8nss0gppzNfOBMuRz9H44dv7gSd2');
documentReference.snapshots().listen((datasnapshot) {
if (datasnapshot.exists) {
print(datasnapshot.data['name'].toString());
} else {
print('Error!');
}
});
}
Here is the implemented StreamBuilder implemented in the standard Flutter project:
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _auth = FirebaseAuth.instance;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'Testing',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// DocumentReference documentReference = await Firestore.instance
// .collection('Employees')
// .document('8nss0gppzNfOBMuRz9H44dv7gSd2');
// documentReference.snapshots().listen((datasnapshot) {
// if (datasnapshot.exists) {
// print(datasnapshot.data['name'].toString());
// } else {
// print('Error!');
// }
// });
StreamBuilder<DocumentSnapshot>(
stream: Firestore.instance
.collection('Employees')
.document('8nss0gppzNfOBMuRz9H44dv7gSd2')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Text('Loading...');
default:
return new Text(snapshot.data.data['name']);
}
},
);
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

Change your code to the following:
builder: : (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
if (!snapshot.hasData){
return new Text('Loading...');
}
else{
print(snapshot);
Navigator.pushReplacementNamed(
context, TabCreator.screenId);
}
Add an else block so when you have data it will enter the else and navigate to the page.
Also you need to use the StreamBuilder inside the build method not inside the onPressed function which is used to handle data processing. Example you can do the following:
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
bool visible = false;
final firestoreInstance = Firestore.instance;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
Visibility(
child: StreamBuilder<DocumentSnapshot>(
stream: Firestore.instance
.collection('users')
.document('FIJbBBiplAGorYzdtUQF')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
print(snapshot);
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
else if (snapshot.hasData) {
print(snapshot.data.data);
return new Text(snapshot.data.data["age"].toString());
}
return new CircularProgressIndicator();
},
),
visible: visible,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
visible = true;
});
},
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
So here I also use the Visibility() widget to hide it, but when FAB button is clicked the data from firestore will appear.

Related

It seems that pthread_join() is not invoked or PTHREAD_ATTR_FLAG_DETACHED is not set Flutter Firebase

I've followed a tutorial regarding 'Flutter Firebase Authentication.
I faced many issues due to it being in an old version of firebase, but I migrated and fixed all problems related to the null safety and others. It showed 0 problems/errors.
But on execution, the following error message appears multiple times.
D/libGLESv2(15242): STS_GLApi : DTS is not allowed for Package : com.example.realone
W/libc (15242): It seems that pthread_join() is not invoked or PTHREAD_ATTR_FLAG_DETACHED is not set.
W/libc (15242): pthread tid : 15281
W/libc (15242): pthread start_routine: 0xdeeecce
In the phone, it gives a place where we can write the email and password but when we click on the sign in button it doesn't do anything.
my Sign_in code :
class _LoginPageState extends State<LoginPage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
late String _email, _password;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(),
body: Form(
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
validator: (input) {
if (input!.isEmpty) {
return 'Provide an email';
}
},
decoration: InputDecoration(labelText: 'Email'),
onSaved: (input) => _email = input!,
),
TextFormField(
validator: (input) {
if (input!.length < 6) {
return 'Longer password please';
}
},
decoration: InputDecoration(labelText: 'Password'),
onSaved: (input) => _password = input!,
obscureText: true,
),
ElevatedButton(
onPressed: signIn,
child: Text('Sign in'),
),
],
)),
);
}
void signIn() async {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
try {
User? user = (await FirebaseAuth.instance
.signInWithEmailAndPassword(email: _email, password: _password))
.user;
// print(user);
Navigator.push(context,
MaterialPageRoute(builder: (context) => Home(user: user!)));
} catch (e) {
print(e);
}
}
}
}
my home code (the page where the app goes after signing in)
lass Home extends StatelessWidget {
const Home({Key? key, required this.user}) : super(key: key);
final User user;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home ${user.email}'),
),
body: StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('users')
.doc(user.uid)
.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else if (snapshot.hasData) {
return checkRole(snapshot.data!);
}
return LinearProgressIndicator();
},
),
);
}
Center checkRole(DocumentSnapshot snapshot) {
if (snapshot.data() == null) {
return Center(
child: Text('no data set in the userId document in firestore'),
);
}
if ((snapshot.data() as dynamic)['role'] == 'admin') {
return adminPage(snapshot);
} else {
return userPage(snapshot);
}
}
Center adminPage(DocumentSnapshot snapshot) {
return Center(
child: Text(
'${(snapshot.data() as dynamic)['role']} ${(snapshot.data() as dynamic)['name']}'));
}
Center userPage(DocumentSnapshot snapshot) {
return Center(child: Text((snapshot.data() as dynamic)['name']));
}
}
I have two other files main.dart and signUp.dart
Would be glad to know the issues in code.
Thanks in advance.

The getter 'uid' was called on null - but only half the time

So I have this screen in my Flutter app that is supposed to show all the notes for a particular user. I have the Firestore structured so that there is a collection of notes and each user has one document named their uid. Then all their notes are store in a collection (usernotes) under their document.
The problem I am having here is that when you try to access the notes page in the app, you get the error
The getter 'uid' was called on null.
Receiver: null
Tried calling: uid
But when I simply click run from the Flutter app, everything works just fine. You can see all the notes on the screen. Here is my note screen.
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'welcome_screen.dart';
class NoteScreen extends StatefulWidget {
static const String id = 'note_screen';
#override
_NoteScreenState createState() => _NoteScreenState();
}
class _NoteScreenState extends State<NoteScreen> {
final _auth = FirebaseAuth.instance;
User loggedInUser;
#override
void initState() {
super.initState();
getCurrentUser();
}
void getCurrentUser() async {
try {
final user = await _auth.currentUser;
if (user != null) {
loggedInUser = user;
}
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Field Notes'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.chat),
tooltip: 'Messages',
onPressed: () {},
),
IconButton(
icon: const Icon(Icons.exit_to_app),
tooltip: 'Log Out',
onPressed: () {
_auth.signOut();
Navigator.pushNamed(context, WelcomeScreen.id);
},
),
],
),
body: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('notes').doc(loggedInUser.uid).collection('usernotes')
.snapshots(),
builder: (ctx, streamSnapShot) {
if(!streamSnapShot.hasData) return const Text('Loading...');
if (streamSnapShot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
final noteData = streamSnapShot.data.docs;
return ListView.builder(
itemCount: noteData.length,
itemBuilder: (ctx, index) => Container(
padding: EdgeInsets.all(8),
child: Text(noteData[index]['text']),
),
);
},
),
floatingActionButton:
FloatingActionButton(child: Icon(Icons.add), onPressed: () {
FirebaseFirestore.instance.collection('notes').doc(loggedInUser.uid).collection('usernotes').add({
'text' : 'This was added by clicking the button!'
});
}),
);
}
}
currentUser is of type User therefore you do not need to use await since it doesn't return a Future. You can do the following:
FirebaseFirestore.instance.collection('notes').doc(_auth.currentUser.uid).collection('usernotes').snapshots(),
And:
FloatingActionButton(child: Icon(Icons.add), onPressed: () {
FirebaseFirestore.instance.collection('notes').doc(_auth.currentUser.uid).collection('usernotes').add({'text' : 'This was added by clicking the button!'});
});

The getter 'uid' was called on null error appears between screens

I am getting an error
The getter 'uid' was called on null.
Receiver: null
Tried calling: uid
this is the code I am using, the problem is auth.currentUser.uid which is returning null when first called, from what I understand the auth.currentUser is null at first then it return the current user
but I don't get how to handle this situation
the error show on the emulator when between the login screen and home screen after I log in, it appear for a second then it's gone and the home screen appears normally
class HomePage extends StatelessWidget {
final FirebaseAuth auth = FirebaseAuth.instance;
static int count;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
GestureDetector(
child: Icon(Icons.logout),
onTap: () async {
await auth.signOut();
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return LoginPage();
}));
},
),
],
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
print('add button pressed');
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return NewNote();
}));
},
),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Notes',
style: TextStyle(
fontSize: 18,
),
),
// Text(count != null ? count.toString() : '0'),
NotesCount(
auth1: auth,
),
Text(auth.currentUser.uid != null ? auth.currentUser.uid : 'No user id'),
SizedBox(
height: 20,
),
NotesGrid(
auth1: auth,
),
],
),
),
),
);
}
}
class NotesCount extends StatelessWidget {
final FirebaseAuth auth1;
NotesCount({this.auth1});
#override
Widget build(BuildContext context) {
CollectionReference notes = FirebaseFirestore.instance.collection('users').doc(auth1.currentUser.uid).collection('notes');
return StreamBuilder<QuerySnapshot>(
stream: notes.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return new Text(snapshot.data.docs.length.toString());
},
);
}
}
class NotesGrid extends StatelessWidget {
final FirebaseAuth auth1;
NotesGrid({this.auth1});
#override
Widget build(BuildContext context) {
CollectionReference notes = FirebaseFirestore.instance.collection('users').doc(auth1.currentUser.uid).collection('notes');
return StreamBuilder(
stream: notes.snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: const Text('Loading events...'));
}
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return GridView.builder(
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemBuilder: (BuildContext context, int index) {
return NoteCard(
note: Note(
title: snapshot.data.documents[index]['title'],
content: snapshot.data.documents[index]['content'],
datetime: snapshot.data.documents[index]['datetime'].toDate(),
id: snapshot.data.documents[index].documentID,
),
);
},
itemCount: snapshot.data.documents.length,
);
},
);
}
}
It's not a good idea to use currentUser as your primary way to find the signed in user account. It will always be null when the app is first launched. The previously signed in user object doesn't become available until some time later. It's better to follow the instructions in the documentation and set up an auth state listener so you can find out when the user object first becomes available.
FirebaseAuth.instance
.authStateChanges()
.listen((User user) {
if (user == null) {
print('User is currently signed out!');
} else {
print('User is signed in!');
}
});
Use this listener to respond to changes in auth state and trigger an update of your UI accordingly.
I tried adding if else before accessing to current user and the error is gone and the user data shows normally after loading it
Text(auth.currentUser != null ? auth.currentUser.uid : 'Loading...')

Flutter/Firebase - Error in fetching currently logged in user data

I want to fetch currently logged in user data. There is a field in fire_store 'useremail'. When a user logs in, I get his ID and using 'where class' I fetch the animal's data against his ID shown below:
Widget _buildBody(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('booking_tbl').where("useremail", isEqualTo: _firebaseUser.email.toString()).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return _buildList(context, snapshot.data.documents);
},
);
}
Although it is working but it shows error on as well. I need some help to fix this issue or an alternate suggestion for this (Any suggestion or kind help would be highly appreciated):
════════ Exception caught by widgets library
══════════════════════
NoSuchMethodError was thrown building UserBookingHistoryModel(dirty,
state: _UserBookingHistoryModelState#2d8c2):
The getter 'email' was called on null.
Receiver: null
Tried calling: email
Probably the problem is caused by this snippet in Firebase Auth:
void initState() {
super.initState();
widget.auth.getCurrentUser().then((firebaseUserId) {
setState(() {
authStatus = firebaseUserId == null
? AuthStatus.notSignedIn
: AuthStatus.signedIn;
});
});
}
The full code of bookings.dart is here:
class _UserBookingHistoryModelState extends State<UserBookingHistoryModel> {
FirebaseAuth _auth;
FirebaseUser _firebaseUser;
#override
void initState() {
super.initState();
_auth = FirebaseAuth.instance;
_getCurrentUser();
}
_getCurrentUser () async {
_firebaseUser = await FirebaseAuth.instance.currentUser();
setState(() {
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _buildBody(context),
);
}
Widget _buildBody(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('booking_tbl').where("useremail", isEqualTo: _firebaseUser.email.toString()).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return _buildList(context, snapshot.data.documents);
},
);
}
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return ListView(
padding: const EdgeInsets.only(top: 5.0),
children: snapshot.map((data) => _buildListItem(context, data)).toList(),
);
}
Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
final record = Record.fromSnapshot(data);
return Padding(
key: ValueKey(record.animal),
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
),
child: new ListTile(
title: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Text(
"${record.animal} Slaughtering",
style: new TextStyle(fontWeight: FontWeight.bold, color: Colors.black),
),
],
),
)
),
);
}
}
class Record {
final String animal;
final String user;
final DocumentReference reference;
Record.fromMap(Map<String, dynamic> map, {this.reference})
: assert(map['animal'] != null),
assert(map['user'] != null),
animal = map['animal'],
user = map['user'];
Record.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data, reference: snapshot.reference);
#override
String toString() => "Record<$animal:$user>]";
}
You need to do the following:
Stream<QuerySnapshot> getData() async*{
FirebaseUser firebaseUser = await FirebaseAuth.instance.currentUser();
yield* Firestore.instance.collection('booking_tbl').where("useremail", isEqualTo: firebaseUser.email.toString()).snapshots();
}
Then inside the StreamBuilder use getData():
return StreamBuilder<QuerySnapshot>(
stream: getData(),
builder: (context, snapshot) {
//....
The getData() method is asynchronous, since you are using a StreamBuilder then you need to return a Stream therefore you use the async* keyword, and you emit the result using yield*

Flutter - Fetch Data from firestore and display it in a dropdown list

I'm trying to fetch data from firestore and display it in a dropdown menu. I tried declaring the list like the following:   List makes = [''] but I can’t view the data until I click on another field and the dropdown gets populated at multiple occasions. I have it in a method because eventually, I would like to create a second dropdown where there’s a condition in the database query.
ex. If Toyota is selected display all the models for that particular make.
new StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection("makesModels").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return new Text("Please wait");
return new DropdownButton(
items: snapshot.data.documents.map((DocumentSnapshot document) {
return DropdownMenuItem(
value: document.data["make"],
child: new Text(document.data["make"]));
}).toList(),
value: category,
onChanged: (value) {
setState(() {
category = value;
});
},
hint: new Text("Makes"),
style: TextStyle(color: Colors.black),
);
}),
new StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection("makesModels").where('make', isEqualTo: category).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return new Text("Please wait");
return new DropdownButton(
items: snapshot.data.documents.map((DocumentSnapshot document) {
for(int i = 0; i < document.data['models'].length; i++){
print(document.data['models'][i]);
return new DropdownMenuItem(
value: document.data['models'][i],
child: new Text(document.data['models'][i].toString()),
);
}
}).toList(),
value: models,
onChanged: (value) {
print(value);
setState(() {
models = value;
});
},
hint: new Text("Models"),
style: TextStyle(color: Colors.black),
);
}),
Checking the snippet you've provided, it seems that the app displays two DropdownButton. To display a default selected item on the dropdown, an item should be set on value. In my approach, I've set a boolean to check if there's a need to set a default value.
The Firestore data used in this sample app accesses two Firestore collections: carMake and cars (contains 'makeModel').
carMake collection
cars collection containing 'makeModel'
Complete app
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Firebase
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var carMake, carMakeModel;
var setDefaultMake = true, setDefaultMakeModel = true;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
debugPrint('carMake: $carMake');
debugPrint('carMakeModel: $carMakeModel');
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
children: [
Expanded(
flex: 1,
child: Center(
child: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('carMake')
.orderBy('name')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
// Safety check to ensure that snapshot contains data
// without this safety check, StreamBuilder dirty state warnings will be thrown
if (!snapshot.hasData) return Container();
// Set this value for default,
// setDefault will change if an item was selected
// First item from the List will be displayed
if (setDefaultMake) {
carMake = snapshot.data.docs[0].get('name');
debugPrint('setDefault make: $carMake');
}
return DropdownButton(
isExpanded: false,
value: carMake,
items: snapshot.data.docs.map((value) {
return DropdownMenuItem(
value: value.get('name'),
child: Text('${value.get('name')}'),
);
}).toList(),
onChanged: (value) {
debugPrint('selected onchange: $value');
setState(
() {
debugPrint('make selected: $value');
// Selected value will be stored
carMake = value;
// Default dropdown value won't be displayed anymore
setDefaultMake = false;
// Set makeModel to true to display first car from list
setDefaultMakeModel = true;
},
);
},
);
},
),
),
),
Expanded(
flex: 1,
child: Center(
child: carMake != null
? StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('cars')
.where('make', isEqualTo: carMake)
.orderBy("makeModel").snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
debugPrint('snapshot status: ${snapshot.error}');
return Container(
child:
Text(
'snapshot empty carMake: $carMake makeModel: $carMakeModel'),
);
}
if (setDefaultMakeModel) {
carMakeModel = snapshot.data.docs[0].get('makeModel');
debugPrint('setDefault makeModel: $carMakeModel');
}
return DropdownButton(
isExpanded: false,
value: carMakeModel,
items: snapshot.data.docs.map((value) {
debugPrint('makeModel: ${value.get('makeModel')}');
return DropdownMenuItem(
value: value.get('makeModel'),
child: Text(
'${value.get('makeModel')}',
overflow: TextOverflow.ellipsis,
),
);
}).toList(),
onChanged: (value) {
debugPrint('makeModel selected: $value');
setState(
() {
// Selected value will be stored
carMakeModel = value;
// Default dropdown value won't be displayed anymore
setDefaultMakeModel = false;
},
);
},
);
},
)
: Container(
child: Text('carMake null carMake: $carMake makeModel: $carMakeModel'),
),
),
),
],
),
);
}
}
Demo

Resources