Update Flutter UI after changing data on Firebase Firestore - firebase

I have an app with a profile page with username, email, and profile picture.
What I am trying to do is, the user click on a button to change the profile picture, opening a new page with a simple Circle Avatar Widget and a button, which will upload the image to Firebase Storage and Firebase Firestore, adding to the Firestore collection of that specific current user uid.
Everything works fine, I can see the changes on the Firebase storage, the image being added by the uid + the URL on the current user collection.
This is the code of the edit_user_image_screen:
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/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:tradie_app/scr/providers/authService.dart';
import 'package:tradie_app/scr/widgets/loading.dart';
class EditCompanyImageScreen extends StatefulWidget {
#override
_EditCompanyImageScreenState createState() => _EditCompanyImageScreenState();
}
class _EditCompanyImageScreenState extends State<EditCompanyImageScreen> {
// Keep track of the form for validation
final _formKey = GlobalKey<FormState>();
// Loading Icon
bool loading = false;
final AuthService _authService = AuthService();
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final FirebaseFirestore _firebaseFirestore = FirebaseFirestore.instance;
File _image;
Future getImage(bool gallery) async {
ImagePicker picker = ImagePicker();
PickedFile pickedFile;
// Let user select photo from gallery
if (gallery) {
pickedFile = await picker.getImage(
source: ImageSource.gallery,
);
}
setState(() {
if (pickedFile != null) {
_image = File(pickedFile.path);
uploadFile(_image); // Use if you only need a single picture
} else {
print('No image selected.');
}
});
}
Future<String> uploadFile(File image) async {
String downloadURL;
Reference ref = FirebaseStorage.instance
.ref()
.child("images/${_firebaseAuth.currentUser.uid}");
await ref.putFile(image);
downloadURL = await ref.getDownloadURL();
return downloadURL;
}
Future uploadToFirebase() async {
final CollectionReference users =
_firebaseFirestore.collection("Companies");
final String uid = _firebaseAuth.currentUser.uid;
String url = await uploadFile(
_image); // this will upload the file and store url in the variable 'url'
await users.doc(uid).update({'url': url});
final result = await users.doc(uid).get();
return result.data()["url"];
}
#override
Widget build(BuildContext context) {
return loading
? Loading()
: Scaffold(
appBar: AppBar(
title: Text("Update Profile Image"),
),
body: Form(
key: _formKey,
child: Column(
children: [
Container(
child: Stack(
children: <Widget>[
Align(
alignment: Alignment.center,
child: IconButton(
icon: Icon(Icons.camera_alt),
onPressed: () async {
getImage(true);
},
),
),
Container(
child: _image != null
? Container(
height: 200,
width: 200,
decoration: BoxDecoration(
image: DecorationImage(
image: FileImage(
_image,
),
fit: BoxFit.contain),
),
)
: Container(
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(50),
),
width: 100,
height: 100,
child: Icon(
Icons.camera_alt,
color: Colors.grey[800],
),
),
),
],
),
),
Row(
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.black,
),
child: Text(
"Submit",
style: TextStyle(color: Colors.white),
),
onPressed: () async {
uploadToFirebase();
Navigator.pop(context);
},
),
],
),
],
),
),
);
}
}
And on the company_profile_screen, I have this snippet of code:
Stream getCompanyImageData() async* {
final CollectionReference users =
_firebaseFirestore.collection("Companies");
final String uid = _firebaseAuth.currentUser.uid;
final result = await users.doc(uid).get();
yield result.data()["url"];
}
My problem:
When I go back from edit_user_image_screen to company_screen, the App UI is not updating, I can see the changes on Firebase, and if I reload Android Studio, I can see the changes on the UI, but not automatically.
Here is the code where I am displaying the image on the company_screen:
Column(
children: [
StreamBuilder(
stream: getCompanyImageData(),
builder: (BuildContext context,
AsyncSnapshot snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
return Image.network(
snapshot.data.toString(),
width: 100,
height: 100,
);
},
),
],
),

Try it like this, and cancel your top function getCompanyImageData()
Column(children: [
StreamBuilder(
stream: _firebaseFirestore.collection("Companies").doc(_firebaseAuth.currentUser.uid).snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
return Image.network(
snapshot.data.data()["url"],
width: 100,
height: 100,
);
},
),

This would depend on your state management,
CollectionRef.users.doc(ViewModelUser.user.value.mobile).snapshots().listen((event) {
ViewModelUser.setUser(UserModel.fromJson(event.data()));
});
This is my code where I listen for changes in my profile, when there is a change firebase sends me the updated data and I update my userData, my state management does the rest of updating the UI.

Related

Add image from gallery to user document id using Flutter and Firebase

I am developing an app, and I want to create a user profile for each logged-in user. With my code now, I am able to get the user information from the cloud Firestore of each user uid document, but I want the user to be able to add an image to firebase storage, and then get this image, add to the specific user uid doc, and display on the app. Basically, I know how to get the data I have already, I just don't know how to update the user doc, especially with images.
Here is the code I have for the user profile:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:tradie_app/scr/providers/authService.dart';
import 'package:tradie_app/scr/screens/edit_company_email.dart';
import 'package:tradie_app/scr/widgets/loading.dart';
import 'home.dart';
class CompanyProfile extends StatefulWidget {
#override
_CompanyProfileState createState() => _CompanyProfileState();
}
class _CompanyProfileState extends State<CompanyProfile> {
// Keep track of the form for validation
final _formKey = GlobalKey<FormState>();
// Loading Icon
bool loading = false;
final AuthService _authService = AuthService();
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final FirebaseFirestore _firebaseFirestore = FirebaseFirestore.instance;
Future getCompanyNameData() async {
final CollectionReference users =
_firebaseFirestore.collection("Companies");
final String uid = _firebaseAuth.currentUser.uid;
final result = await users.doc(uid).get();
return result.data()["companyName"];
}
Future getCompanyEmailData() async {
final CollectionReference users =
_firebaseFirestore.collection("Companies");
final String uid = _firebaseAuth.currentUser.uid;
final result = await users.doc(uid).get();
return result.data()["companyEmail"];
}
#override
Widget build(BuildContext context) {
return loading
? Loading()
: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
iconTheme: IconThemeData(color: Colors.black),
backgroundColor: Colors.white,
title: Text(
"Create the company profile",
style: TextStyle(
color: Colors.black,
),
),
elevation: 0.0,
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
UserAccountsDrawerHeader(
accountName: FutureBuilder(
future: getCompanyNameData(),
builder: (_, AsyncSnapshot snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Text("Loading");
}
return Text(snapshot.data);
},
),
),
ListTile(
leading: Icon(Icons.logout),
title: Text(
"Log Out",
),
onTap: () async {
await _authService.signOut();
},
),
],
),
),
body: Padding(
padding: const EdgeInsets.all(18.0),
child: SingleChildScrollView(
child: Container(
child: Form(
key: _formKey,
child: Column(
children: [
Row(
children: [
Text(
"Company name: ",
style: TextStyle(
color: Colors.black,
fontSize: 20.0,
),
),
],
),
SizedBox(
height: 10.0,
),
Row(
children: [
FutureBuilder(
future: getCompanyNameData(),
builder: (_, AsyncSnapshot snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Text("Loading");
}
return Text(
snapshot.data,
style: TextStyle(
color: Colors.black, fontSize: 15.0),
);
},
),
],
),
SizedBox(
height: 20.0,
),
Row(
children: [
Text(
"Company email: ",
style: TextStyle(
color: Colors.black,
fontSize: 20.0,
),
),
],
),
SizedBox(
height: 10.0,
),
Row(
children: [
FutureBuilder(
future: getCompanyEmailData(),
builder: (_, AsyncSnapshot snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Text("Loading");
}
return Text(
snapshot.data,
style: TextStyle(
color: Colors.black,
fontSize: 15.0,
),
);
},
),
IconButton(
icon: Icon(Icons.edit),
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
EditCompanyEmailScreen(),
),
),
),
],
),
SizedBox(
height: 20.0,
),
Row(
children: [
Text(
"Company phone number: ",
style: TextStyle(
color: Colors.black,
fontSize: 20.0,
),
),
],
),
SizedBox(
height: 20.0,
),
// Here is where I want to add the image
Row(
children: <Widget>[],
),
],
),
),
),
),
),
);
}
}
To select the image from gallery add this plugin. And then call this function to select the image.
File _image;
selectImageFromGallery() async
{
final picker=ImagePicker();
setState(() {
inProcess=true;
});
final imageFile= await picker.getImage(source: ImageSource.gallery);
if(imageFile!=null)
{
_image=File(imageFile.path);
}
setState(() {
inProcess=false;
});
}
after selecting the image run this function to store image to firebase and get url of the image.
Future<String> uploadFile(File image) async
{
String downloadURL;
String postId=DateTime.now().millisecondsSinceEpoch.toString();
Reference ref = FirebaseStorage.instance.ref().child("images").child("post_$postId.jpg");
await ref.putFile(image);
downloadURL = await ref.getDownloadURL();
return downloadURL;
}
now lets upload and update data in firestore docs and storage.
uploadToFirebase()async
{
String url=await uploadFile(_image); // this will upload the file and store url in the variable 'url'
await users.doc(uid).update({ //use update to update the doc fields.
'url':url
});
}
To show the selected image add this in your Ui:-
Container(
height: 200,
width: 200,
decoration: BoxDecoration(image: DecorationImage(image: FileImage(_image,),fit: BoxFit.contain)),
)
After adding this image make a button for upload:-
RaisedButton()
{
onPressed:(){
uploadToFirebase();
},
child:Text("Upload"),
}
After selecting the image user will click on this button to upload and save sata to firebase.
You need to upload the image chosen from gallery to your firebase storage and then use the url in your firestore.
here is a samle code to upload the image to storage
import 'package:firebase_storage/firebase_storage.dart' as firebase_storage;
final ref = firebase_storage.FirebaseStorage.instance.ref().child("profile-pic/abc.jpg");
final imageFile = await ImagePicker.pickImage(source: ImageSource.gallery);
final uploadTask = ref.putFile(imageFile);
final snapshot = await uploadTask.whenComplete(() => null);
imageUrl = await snapshot.ref.getDownloadURL();
use the imageUrl and update it in your firestore collection for the user.
You can fetch the url and display image whenever you need.

why sometime the imageUrl get saved inside cloud firestore database & sometime it does not get saved

I am creating a flutter application which will store Image-url inside cloud firestore database and that url get fetched in the form of image and display inside my flutter application. The problem is with image-url sometime it is get saved inside the database and sometime it does not. When it get saved the fetching process work properly and when it does not saved or unsaved it will reture a error value with null msg which is shown in the image.
I don't know why this is happening sometime the data get saved and sometime it is unsaved.
Pls see the below code for saving of Image inside the cloud firestore database.
import 'package:intl/intl.dart';
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mi_card/duplicate.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mi_card/widget/provider_widget.dart';
import 'package:path/path.dart' as path;
import 'package:firebase_storage/firebase_storage.dart';
import '../sec.dart';
class EditProductScreen extends StatefulWidget {
#override
_EditProductScreenState createState() => _EditProductScreenState();
}
class _EditProductScreenState extends State<EditProductScreen> {
//for selecting picture from galary of phone
var sampleImage;
Future captureImage() async {
var tempImage = await ImagePicker.pickImage(source: ImageSource.camera);
setState(() {
sampleImage = tempImage;
});
String fileName = path.basename(sampleImage.path);
final StorageReference firebaseStorageRef =
FirebaseStorage.instance.ref().child('entry/student entry/'+fileName);
final StorageUploadTask task =
firebaseStorageRef.putFile(sampleImage);
var ImageUrl= await(await task.onComplete).ref.getDownloadURL();
url=ImageUrl.toString();
print("Image Url="+url);
//saveToDatabase(url);
}
void saveToDatabase(url){
}
//for camera opening and capturing the picture
Future getImage() async {
var tempImage = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
sampleImage = tempImage;
});
String fileName = path.basename(sampleImage.path);
final StorageReference firebaseStorageRef =
FirebaseStorage.instance.ref().child('entry/student entry/'+fileName);
final StorageUploadTask task =
firebaseStorageRef.putFile(sampleImage);
var ImageUrl= await(await task.onComplete).ref.getDownloadURL();
url=ImageUrl.toString();
print("Image Url="+url);
saveToDatabase(url);
}
final _priceFocusNode = FocusNode();
final _formKey = GlobalKey<FormState>();
final _firestore = Firestore.instance;
String url;
var _initValues = {
'title': '',
'description': '',
'price': '',
'imageUrl': '',
};
var _isInit = true;
#override
void didChangeDependencies() {
if (_isInit) {
final productId = ModalRoute.of(context).settings.arguments as String;
}
_isInit = false;
super.didChangeDependencies();
}
#override
void dispose() {
_priceFocusNode.dispose();
super.dispose();
}
void _saveForm() async{
final isValid = _formKey.currentState.validate();
if (!isValid) {
return;
}
_formKey.currentState.save();
var dbTimeKey = new DateTime.now();
var formatDate=new DateFormat('dd/MMMM/yyyy');
var formatTime=new DateFormat('dd/MMMM/yyyy &'' hh:mm aaa, EEEE');
String date = formatDate.format(dbTimeKey);
String time = formatTime.format(dbTimeKey);
final uid = await Provider.of(context).auth.getCurrentUID();
// collection reference for every user
DocumentReference Collection = Firestore.instance.collection(' entry').document();
Collection.setData({
"Entry-time": time,
'image': url,
});
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MyApp()),
);
}
List<String> _locations = ['NA','1st year', '2 year', '3 year', '4 year']; // Option 2
String _selectedLocation;
#override
Widget build(BuildContext context) {
var _blankFocusNode = new FocusNode();
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
title: Text('ENTRY'),
centerTitle: true,
leading: new
IconButton(
icon: Icon(Icons.arrow_back),
color: Colors.white,
onPressed: () {
Navigator.pop(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
},
),
actions: <Widget>[
FlatButton(
textColor: Colors.white,
onPressed: _saveForm,
child: Text("Save",),
),
],
),
backgroundColor: Colors.blueAccent,
body: GestureDetector (
onTap: () {
FocusScope.of(context).requestFocus(_blankFocusNode);
},
child: Form(
key: _formKey,
child: ListView(
scrollDirection: Axis.vertical,
children: <Widget>[
SizedBox(
height: 20.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Align(
alignment: Alignment.center,
child: CircleAvatar(
radius:73 ,
backgroundColor: Colors.white,
child: ClipOval(
child: new SizedBox(
width: 125,
height:125,
child: sampleImage != null
? Image.file(
sampleImage,
height: 108,
fit: BoxFit.fill,
)
: IconButton(
icon: Icon(
Icons.person,
color: Colors.grey[400],
),
iconSize: 80.0,
//onPressed:_takePicture
),
),
),
),
),
Padding(
padding: EdgeInsets.only(top: 0.0),
child: IconButton(
icon: Icon(
Icons.camera_alt,
color: Colors.black,
size: 30.0,
),
onPressed: captureImage,
),
),
Padding(
padding: EdgeInsets.only(top: 00.0),
child: IconButton(
icon: Icon(
Icons.folder,
color: Colors.orangeAccent[100],
size: 30.0,
),
onPressed: getImage,
),
),
],
),
],
),
),
),
);
}
}
How to handle this error ?
Here is a better approach to uploading images to storage and getting url back
final StorageReference storageReference =
FirebaseStorage().ref().child("path/$name");
final StorageUploadTask uploadTask =
storageReference.putFile(imaeFile);
final StreamSubscription<StorageTaskEvent> streamSubscription =
uploadTask.events.listen((event) {
// You can use this to notify yourself or your user in any kind of way.
// For example: you could use the uploadTask.events stream in a StreamBuilder instead
// to show your user what the current status is. In that case, you would not need to cancel any
// subscription as StreamBuilder handles this automatically.
print('EVENT ${event.type}');
});
// Cancel your subscription when done.
await uploadTask.onComplete;
streamSubscription.cancel();
String url =
await (await uploadTask.onComplete).ref.getDownloadURL();
saveToDatabase(url);

Flutter: Firebase storage download url

I am new to Flutter and I am trying to make a wallpaper app where I use Firebase to store my images. The app retrieves images from Firebase and the user can share and download the images to the device. I'm using image_gallery_saver package but I wasn't able to get the url of the images so I can add it to the image_gallery_saver function, is there a simple way to get the url of an image from firebase after the user clicks on a specific image?
The following is the home page:
import 'package:cardstest2/Screens/ImageScreen.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:async';
class FirestoreListViewPage extends StatefulWidget {
#override
_FirestoreListViewPageState createState() => _FirestoreListViewPageState();
}
class _FirestoreListViewPageState extends State<FirestoreListViewPage> {
Future _data;
Future getPosts() async {
var firestore = Firestore.instance;
QuerySnapshot qn = await firestore.collection("gallery").getDocuments();
return qn.documents;
}
#override
void initState() {
super.initState();
_data = getPosts();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: Text('Subcategories'),
),
body: Container(
child: FutureBuilder(
future: _data,
builder: (_, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: Text('Waiting...'),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (_, index){
return Card(
child: ListTile(
title: Image.network(snapshot.data[index].data['GalleryUrl']),
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => imageScreenPage(snapshot.data[index].data['GalleryUrl']),
),
);
},
),
);
});
}
}),
),
);
}
}
The following is the imageScreen page:
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:share/share.dart';
import 'package:dio/dio.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'dart:ui';
class imageScreenPage extends StatefulWidget {
String cardPath;
imageScreenPage(this.cardPath);
#override
_imageScreenPageState createState() => _imageScreenPageState();
}
class _imageScreenPageState extends State<imageScreenPage> {
final LinearGradient backgroundGradient = new LinearGradient(
colors: [new Color(0x10000000), new Color(0x30000000)],
begin: Alignment.topLeft,
end: Alignment.bottomRight);
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new SizedBox.expand(
child: new Container(
decoration: new BoxDecoration(gradient: backgroundGradient),
child: new Stack(
children: <Widget>[
new Align(
alignment: Alignment.center,
child: new Hero(
tag: widget.cardPath,
child: new Image.network(widget.cardPath),
),
),
new Align(
alignment: Alignment.topCenter,
child: new Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new AppBar(
elevation: 0.0,
backgroundColor: Colors.transparent,
leading: new IconButton(
icon: new Icon(
Icons.close,
color: Colors.black,
),
onPressed: () => Navigator.of(context).pop()),
)
],
),
),
],
),
),
),
persistentFooterButtons: <Widget>[
IconButton(
icon: Icon(Icons.wallpaper), onPressed: () {},
),
IconButton(
icon: Icon(Icons.file_download), onPressed: () {_save();},
),
IconButton(
icon: Icon(Icons.share), onPressed: () {Share.share(widget.cardPath);},
),
],
);
}
_save() async {
var response = await Dio().get("<insert url>", options: Options(responseType: ResponseType.bytes));
final result = await ImageGallerySaver.saveImage(Uint8List.fromList(response.data));
print(result);
}
}
To get the downloadUrl, then do the following:
StorageTaskSnapshot snapshot = await storage
.ref()
.child("images/$imageName")
.putFile(file)
.onComplete;
if (snapshot.error == null) {
final String downloadUrl =
await snapshot.ref.getDownloadURL();
}
use putFile to add the file to Firebase Storage, then you can use snapshot.ref.getDownloadURL() to get the url.
For all the people using flutter in 2021
This worked for me
import 'package:firebase_storage/firebase_storage.dart' as firebase_storage;
Future<String> uploadImage(imageFile) async {
firebase_storage.Reference ref = storage.ref().child('post_$postId.jpg');
firebase_storage.UploadTask uploadTask = ref.putFile(imageFile);
print('File Uploaded');
var imageUrl = await (await uploadTask).ref.getDownloadURL();
String url = imageUrl.toString();
return url;
}
uploadImage() is a function which takes in an imageFile
You have to call it using await uploadImage(imageFile) inside an asynchronous function

Firebase Query not working with Flutter, How to solve this?

I am trying to add a search functionality to my Flutter App, Everything works fine in the code, no errors are shown but when I search for username on my app screen no results are showing but sometime it throws an error saying "failed assertion: line 269 pos 10: 'data != null' The relevant error-causing widget was FutureBuilder" Especially when I use capital letter and has an empty space in the text.
I have attached the code for search.dart and user.dart file. Please help me out, I'm stuck here.
search.dart File
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:social_share/models/user.dart';
import 'package:social_share/pages/home.dart';
import 'package:social_share/widgets/progress.dart';
class Search extends StatefulWidget {
#override
_SearchState createState() => _SearchState();
}
class _SearchState extends State<Search> {
TextEditingController searchController = TextEditingController();
Future<QuerySnapshot> searchResultsFuture;
handleSearch(String query) {
Future<QuerySnapshot> users = usersRef
.where("displayName", isGreaterThanOrEqualTo: query)
.getDocuments();
setState(() {
searchResultsFuture = users;
});
}
clearSearch() {
searchController.clear();
}
AppBar buildSearchField() {
return AppBar(
backgroundColor: Colors.white,
title: TextFormField(
controller: searchController,
decoration: InputDecoration(
hintText: "Search for a user...",
filled: true,
prefixIcon: Icon(
Icons.account_box,
size: 28.0,
),
suffixIcon: IconButton(
icon: Icon(Icons.clear),
onPressed: clearSearch,
),
),
onFieldSubmitted: handleSearch,
),
);
}
Container buildNoContent() {
final Orientation orientation = MediaQuery.of(context).orientation;
return Container(
child: Center(
child: ListView(
shrinkWrap: true,
children: <Widget>[
SvgPicture.asset(
'assets/images/search.svg',
height: orientation == Orientation.portrait ? 300.0 : 200.0,
),
Text(
"Find Users",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w600,
fontSize: 60.0,
),
),
],
),
),
);
}
buildSearchResults() {
return FutureBuilder(
future: searchResultsFuture,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
List<Text> searchResults = [];
snapshot.data.documents.forEach((doc) {
User user = User.fromDocument(doc);
searchResults.add(Text(user.username , style: TextStyle(color: Colors.black, fontSize: 20.0),));
});
return ListView(
children: searchResults,
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).primaryColor.withOpacity(0.8),
appBar: buildSearchField(),
body:
searchResultsFuture == null ? buildNoContent() : buildSearchResults(),
);
}
}
class UserResult extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text("User Result");
}
}
user.dart File
import 'package:cloud_firestore/cloud_firestore.dart';
class User {
final String id;
final String username;
final String email;
final String photoUrl;
final String displayName;
final String bio;
//Constructor
User({
this.id,
this.username,
this.email,
this.photoUrl,
this.displayName,
this.bio,
});
factory User.fromDocument(DocumentSnapshot doc){
return User(
id: doc['id'],
email: doc['email'],
username: doc['username'],
photoUrl: doc['photoUrl'],
displayName: doc['displayName'],
bio: doc['bio'],
);
}
}
Firebase Database Data ScreenShots
Firebase Users Data Screenshot
if looks like if the actual data is null you would get an error.
try:
if(!snapshot.hasData || snapshot.data?.documents == null)
instead of:
if (!snapshot.hasData)
reference - https://github.com/flutter/flutter/issues/22199
Solved
After I changed the if statement to the following line it's working.
if(!snapshot.hasData || snapshot.data.documents == null)

Cloud Firestore not updating correctly when record is added in Flutter

I am developing a Flutter app and I am using the cloud_firestore plugin. I have a collection of submissions and I am using the StreamBuilder to display them (which I am assuming will update when the stream changes). I literally took the example from the plugin examples as there is not much documentation on how to do things using the plugin. When I added a record, the list of documents that I am displaying gets longer, but it seems to be copying one of the submissions instead of inserting the new submission. The new submission does not show after it is added. Here is the code for how I am displaying the list:
// At the top of the class home.dart.
final submissions = Firestore.instance.collection('submissions');
// This is in submission-list.dart and the above submissions
// is passed in to the contructor
Widget build(BuildContext context) {
return new StreamBuilder<QuerySnapshot>(
stream: submissions
.where('owner_uid', isEqualTo: this.user.uid)
.orderBy('timestamp', descending: true)
.snapshots,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return new Text('Loading...');
return new ListView(
children: snapshot.data.documents.map((DocumentSnapshot document) {
var date = _formatDate(document['timestamp']);
String body = _constructCardBody(document['weight'],
bodyFat: document['bodyFat']);
String id = document.documentID;
return new SubmissionCard(id: id, title: date, body: body, submissions: submissions);
}).toList(),
);
},
);
}
Here is submission-card.dart in full:
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import '../utils/logger.dart';
import './block-button.dart';
class SubmissionCard extends StatefulWidget {
final String id;
final String title;
final String body;
final CollectionReference submissions;
SubmissionCard({this.id, this.title, this.body, this.submissions});
#override
State<StatefulWidget> createState() =>
new _SubmissionCardState(id: this.id, title: this.title, body: this.body, submissions: this.submissions);
}
class _SubmissionCardState extends State<SubmissionCard> {
final String id;
final String title;
final String body;
bool showActionButtons = false;
final CollectionReference submissions;
_SubmissionCardState({this.id, this.title, this.body, this.submissions});
void _showEditScreen() {}
void _showActionButtons() {
setState(() {
showActionButtons = true;
});
}
void _hideActionButtons() {
setState(() {
showActionButtons = false;
});
}
Future<Null> _deleteSubmission() async {
try {
await submissions.document(id).delete();
await Logger.log('error', 'stackTrace');
} catch (error, stackTrace) {
await Logger.log(error, stackTrace);
}
}
void _closeDialog() {
Navigator.of(context).pop();
_hideActionButtons();
}
Future<Null> _warnAboutDeletion() async {
return showDialog(
context: context,
child: new SimpleDialog(
title: new Text('Are you sure?'),
children: <Widget>[
new SimpleDialogOption(
onPressed: () {
this._deleteSubmission();
this._closeDialog();
},
child: new Text("I'm sure. Delete it."),
),
new SimpleDialogOption(
onPressed: _closeDialog,
child: new Text("Nope. Take me back."),
),
],
)
);
}
#override
Widget build(BuildContext context) {
return new GestureDetector(
onLongPress: _showActionButtons,
onTap: _hideActionButtons,
child: new Card(
elevation: showActionButtons ? 8.0 : 2.0,
key: new GlobalKey(),
child: new Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new ListTile(
trailing: showActionButtons
? new Row(
children: <Widget>[
new IconButton(
padding: const EdgeInsets.all(0.0),
icon: const Icon(Icons.edit),
onPressed: _showEditScreen,
color: Colors.black12,
splashColor: Colors.black26,
highlightColor: Colors.black12,
),
new IconButton(
padding: const EdgeInsets.all(0.0),
icon: const Icon(Icons.delete),
onPressed: _warnAboutDeletion,
color: Colors.redAccent,
splashColor: Colors.black26,
highlightColor: Colors.black12,
),
],
)
: new Container(),
isThreeLine: true,
title: new Text(title),
subtitle: new Text(
body,
style: new TextStyle(height: 3.0),
),
),
],
),
),
);
}
}
Link to repo: https://github.com/dericgw/bodwatch
Before, when I have worked with Firebase, this collection would automatically update. I have never seen this weird behavior before. Now, I am new to Flutter and Dart, so I could be missing something for sure.
You need to add the indexing in firebase console.
In your case, you need to a multiple indexes.
1. owner_uid, ascending
2. timestamp, descending
And the problem should solve.

Resources