How can I save messages on firestore and display the messages on screen? - flutter

I have been working on the messaging functionality of an app that I am currently working on. I have implemented the Firestore code however, it is storing the messages in the collection and as well as not displaying the messages on-screen. The message is still left on the Text Field. Below is the code and any assistance will be highly appreciated.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class MessageCenter extends StatefulWidget {
const MessageCenter({super.key});
#override
State<MessageCenter> createState() => _MessageCenterState();
}
class _MessageCenterState extends State<MessageCenter> {
final TextEditingController _text = TextEditingController();
final firestore = FirebaseFirestore.instance;
final auth = FirebaseAuth.instance;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
StreamBuilder(
stream: firestore
.collection('chatrooms')
.doc()
.collection('message')
.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
return snapshot.hasData
? Expanded(
child: ListView.builder(
reverse: true,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, i) {
return Card(
child: Text(
snapshot.data!.docs[i]['msg'],
),
);
}),
)
: Container();
}),
SizedBox(
height: 55,
child: Row(
children: [
Expanded(
flex: 5,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
decoration: BoxDecoration(
color: Colors.white30,
borderRadius: BorderRadius.circular(11),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _text,
decoration: const InputDecoration(
hintText: 'Write Message',
)),
),
),
),
),
Expanded(
flex: 2,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
firestore
.collection('chatrooms')
.doc('chatrooms')
.collection('message')
.add({
'sender': auth.currentUser!.email,
'time': DateFormat('hh:mm').format(DateTime.now()),
'msg': _text.text,
});
},
child: Container(
decoration: BoxDecoration(
color: Colors.deepOrange,
borderRadius: BorderRadius.circular(11),
),
child: const Center(
child: Icon(
Icons.send,
size: 30,
),
),
),
),
),
),
],
),
),
],
),
);
}
}

to save message to firestore, you need first of all create model class, firestoreService class, chatRoomRepository class, chatRepository class, chatService class, and controller class`. this is for proper separation of concern and organizing business logic. But for the purposes of this demo, here is the basic function for saving your message to firestore.
Future<void> saveMessage() async {
final chatRoomId = 'your_chatRoom_id_here';
final messageId = 'your_message_id_here';
final path = 'chatRooms/$chatRoomId/messages/$messageId';
final _service = FirebaseFirestore.instance.doc(path);
final sender = auth.currentUser!.email;
final time = DateFormat('hh:mm').format(DateTime.now());
final msg = _text.text;
try{
await _service
.set({
"id": messageId,
'sender': sender,
'time': time,
'msg': msg
});
} on FirebaseException catch(e){
print(e);
}
}

Related

How to optimize Firebase Firestore using Flutter Package in StreamBuilder?

I would like to reduce the number of read usage in FirebaseFirestore. As I am trying to load the FireStore Data in StreamBuilder. StreamBuilder connects with FireStore everytime whenever the app restarts, it is increasing the number of read usage. Is there a way to optimize the Firestore read usage?
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'LoginScreen.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FlutterFire Samples',
debugShowCheckedModeBanner: false,
theme:
ThemeData(primarySwatch: Colors.indigo, brightness: Brightness.dark),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
//Initializing
final CollectionReference _products =
FirebaseFirestore.instance.collection('products');
final _productsStream = _products.snapshots();
// Source can be CACHE, SERVER, or DEFAULT.
final TextEditingController _nameController = TextEditingController();
final TextEditingController _priceController = TextEditingController();
Future<void> _create([DocumentSnapshot? documentSnapshot]) async {
await showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (BuildContext ctx) {
return Padding(
padding: EdgeInsets.only(
top: 20,
left: 20,
right: 20,
bottom: MediaQuery.of(ctx).viewInsets.bottom + 20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: _nameController,
decoration: const InputDecoration(labelText: 'Name'),
),
TextField(
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: _priceController,
decoration: const InputDecoration(
labelText: 'Price',
),
),
const SizedBox(
height: 20,
),
ElevatedButton(
child: const Text('Create'),
onPressed: () async {
//var i = 0;
//for (i = i; i < 10000; i++) {
// final String name;
// final double? price;
double.tryParse(_priceController.text);
// if (price != null) {
await _products.add({
"name": _nameController.text,
"price": _priceController.text
});
// }
_nameController.text = '';
_priceController.text = '';
Navigator.of(context).pop();
//}
},
)
],
),
);
});
}
Future<void> _update([DocumentSnapshot? documentSnapshot]) async {
if (documentSnapshot != null) {
_nameController.text = documentSnapshot['name'];
_priceController.text = documentSnapshot['price'].toString();
}
await showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (BuildContext ctx) {
return Padding(
padding: EdgeInsets.only(
top: 20,
left: 20,
right: 20,
bottom: MediaQuery.of(ctx).viewInsets.bottom + 20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: _nameController,
decoration: const InputDecoration(labelText: 'Name'),
),
TextField(
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: _priceController,
decoration: const InputDecoration(
labelText: 'Price',
),
),
const SizedBox(
height: 20,
),
ElevatedButton(
child: const Text('Update'),
onPressed: () async {
final String name = _nameController.text;
final double? price =
double.tryParse(_priceController.text);
if (price != null) {
await _products
.doc(documentSnapshot!.id)
.update({"name": name, "price": price});
_nameController.text = '';
_priceController.text = '';
Navigator.of(context).pop();
}
},
),
],
),
);
});
}
Future<void> _delete(String productId) async {
await _products.doc(productId).delete();
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('You have successfully deleted a product')));
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
// stream: _products.snapshots(),
stream: _productsStream,
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (streamSnapshot.hasData) {
return ListView.builder(
itemCount: streamSnapshot.data!.docs.length,
itemBuilder: (context, index) {
final DocumentSnapshot documentSnapshot =
streamSnapshot.data!.docs[index];
return Card(
margin: EdgeInsets.all(10),
child: ListTile(
title: Text(documentSnapshot['name']),
subtitle: Text(documentSnapshot['price'].toString()),
trailing: SizedBox(
width: 100,
child: Row(children: [
IconButton(
onPressed: () => _update(documentSnapshot),
icon: Icon(Icons.edit),
),
IconButton(
icon: const Icon(Icons.delete),
onPressed: () => _delete(documentSnapshot.id)),
]),
),
),
);
},
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => _create(),
child: const Icon(Icons.add),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
}
You're calling final _productsStream = _products.snapshots(); every time the app starts, so that will have to connect to the server to check for snapshots. Even if you have disk caching enabled and there are documents in the local cache, the SDK will connect to the server to check if there are updates to those documents. If there are no updates, the number of document reads caused by this should be fairly minimal though.
The only alternative is to get (not listen) the content from the cache, as shown in getting data from the cache. But in that case you won't know if the QuerySnapshot you get it stale, as that would require to check with the server - which causes documents to be possibly read there and thus be charged.
If you regularly find many clients readying a bunch of the same documents, you can consider shipping that bundle of document to clients outside of the client-to-server SDK by using data bundles. For a more elaborate example of this, see the Firebase documentation on serving bundled Firestore content from a CDN.

Flutter Cubit fetching and displaying data

I'm trying to fetch data from Genshin API, code below is working, but only with delay (in GenshinCubit class), it looks weard, because I don't know how much time to set for delay. I think, there is a problem in code, cause it must not set the GenshinLoaded state before the loadedList is completed. Now, if I remove the delay, it just sets the GenshinLoaded when the list is still in work and not completed, await doesn't help. Because of that I get a white screen and need to hot reload for my list to display.
class Repository {
final String characters = 'https://api.genshin.dev/characters/';
Future<List<Character>> getCharactersList() async {
List<Character> charactersList = [];
List<String> links = [];
final response = await http.get(Uri.parse(characters));```
List<dynamic> json = jsonDecode(response.body);
json.forEach((element) {
links.add('$characters$element');
});
links.forEach((element) async {
final response2 = await http.get(Uri.parse(element));
dynamic json2 = jsonDecode(response2.body);
charactersList.add(Character.fromJson(json2));
});
return charactersList;
}
}
class GenshinCubit extends Cubit<GenshinState> {
final Repository repository;
GenshinCubit(this.repository) : super(GenshinInitial(),);
getCharacters() async {
try {
emit(GenshinLoading());
List<Character> list = await repository.getCharactersList();
await Future<void>.delayed(const Duration(milliseconds: 1000));
emit(GenshinLoaded(loadedList: list));
}catch (e) {
print(e);
emit(GenshinError());
}
}
}
class HomeScreen extends StatelessWidget {
final userRepository = Repository();
HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocProvider<GenshinCubit>(
create: (context) => GenshinCubit(userRepository)..getCharacters(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(body: Container(child: const CharactersScreen())),
),
);
}
}
class CharactersScreen extends StatefulWidget {
const CharactersScreen({
Key? key,
}) : super(key: key);
#override
State<CharactersScreen> createState() => _CharactersScreenState();
}
class _CharactersScreenState extends State<CharactersScreen> {
#override
Widget build(BuildContext context) {
return Column(
children: [
BlocBuilder<GenshinCubit, GenshinState>(
builder: (context, state) {
if (state is GenshinLoading) {
return Center(
child: CircularProgressIndicator(),
);
}
if (state is GenshinLoaded) {
return SafeArea(
top: false,
child: Column(
children: [
Container(
color: Colors.black,
height: MediaQuery.of(context).size.height,
child: ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: state.loadedList.length,
itemBuilder: ((context, index) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 50.0, horizontal: 50),
child: GestureDetector(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CharacterDetailsPage(
character: state.loadedList[index],
),
),
),
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.blueAccent.withOpacity(0.3),
borderRadius: const BorderRadius.all(
Radius.circular(
30,
),
)),
child: Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(
right: 30.0, bottom: 30),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
state.loadedList[index].name
.toString(),
style: TextStyle(
color: Colors.black,
fontSize: 50),
),
RatingBarIndicator(
itemPadding: EdgeInsets.zero,
rating: double.parse(
state.loadedList[index].rarity
.toString(),
),
itemCount: int.parse(
state.loadedList[index].rarity
.toString(),
),
itemBuilder: (context, index) =>
Icon(
Icons.star_rate_rounded,
color: Colors.amber,
))
],
),
),
),
),
),
);
})),
),
],
),
);
}
if (state is GenshinInitial) {
return Text('Start');
}
if (state is GenshinError) {
return Text('Error');
}
return Text('Meow');
}),
],
);
}
}
I found a solution!
I've got that problem because of forEach. How to wait for forEach to complete with asynchronous callbacks? - there is a solution.

flutter check if data exists in firestore

I've been trying to check if user selected data matches with my firestore data.
onPressed: () async{
await Navigator.of(context).push(MaterialPageRoute(builder: (context) => Checker (
from: fromSel,
to: toSel,
)));
},
and in the second page i used
StreamBuilder(
stream: Firestore.instance
.collection('Schedules')
.where('from', isEqualTo: from)
.where('to', isEqualTo: to)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
if (!snapshot.hasData)
return Center(child: CircularProgressIndicator());
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index){
DocumentSnapshot power = snapshot.data.documents[index];
print(power['from']);
print(power['to']);
return Container(
height: 200,
width: MediaQuery.of(context).size.width,
child: Column(
children: [
Text(power['from']),
Text(power['to'])
],
),
);
}
);
}),
so the problem i'm getting is it's not displaying the value when i use .where('from', isEqualTo: from) but it works when i use .where('from', isEqualTo: 'Adama'). and also works with .where('from', isEqualTo: from) when i instantiate from value manually like String from = 'Adama'
can you please tell me what the problem is?
and below is my firestore structure
below is the whole code for the checker (renamed to search)
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:demo/BookingPages/budget.dart';
import 'package:demo/Lists/trip.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Search extends StatefulWidget {
final from, to, seat, adult, child, infant;
final DateTime arrive, depart;
Search(
{Key key, this.from, this.to, this.seat, this.arrive, this.depart, this.adult, this.infant, this.child})
: super(key: key);
#override
_SearchState createState() => _SearchState(from: from, to: to, seat: seat, adult: adult, child: child, infant: infant, arrive: arrive, depart: depart);
}
class _SearchState extends State<Search> {
// Stream<QuerySnapshot> comparision;
var from, to, seat, adult, child, infant;
final DateTime arrive, depart;
_SearchState(
{Key key, this.from, this.to, this.seat, this.arrive, this.depart, this.adult, this.infant, this.child});
Stream<QuerySnapshot> comparision(BuildContext context) async* {
try{
yield* Firestore.instance
.collection('Schedules')
.where('from', isEqualTo: from.toString())
.where('to', isEqualTo: to.toString())
// .where('dates', arrayContains: widget.depart.day)
.snapshots();
}catch(e){
print(e);
}
}
// #override
// void initState() {
// // TODO: implement initState
// comparision = Firestore.instance
// .collection('Schedules')
// .where('from', isEqualTo: from)
// .where('to', isEqualTo: to)
// .snapshots();
//
// super.initState();
// }
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
child: StreamBuilder(
stream: Firestore.instance
.collection('Schedules')
.where('from', isEqualTo: from)
.where('to', isEqualTo: to)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
if (!snapshot.hasData)
return Center(child: CircularProgressIndicator());
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index){
DocumentSnapshot power = snapshot.data.documents[index];
print(power['from']);
print(power['to']);
return Container(
height: 200,
width: MediaQuery.of(context).size.width,
child: Column(
children: [
Text(power['from']),
Text(power['to'])
],
),
);
}
);
}),
)
// Expanded(
// child: StreamBuilder<QuerySnapshot>(
// stream: comparision,
// builder: (BuildContext context, AsyncSnapshot<QuerySnapshot>snapshot) {
// if (!snapshot.hasData)
// return Center(child: CircularProgressIndicator());
// return ListView.builder(
// itemCount: snapshot.data.documents.length,
// itemBuilder: (context, index){
// DocumentSnapshot power = snapshot.data.documents[index];
// print(power['from']);
// print(power['to']);
// return Container(
// height: 200,
// width: MediaQuery.of(context).size.width,
// child: Column(
// children: [
// Text(power['from']),
// Text(power['to'])
// ],
// ),
// );
// }
// );
// },
// ),
// ),
],
)
);
}
}
class TripCard extends StatefulWidget {
#override
_TripCardState createState() => _TripCardState();
}
class _TripCardState extends State<TripCard> {
#override
Widget build(BuildContext context) {
return Container();
}
}
below is my first page code which includes the values
import 'package:flutter/material.dart';
import 'search.dart';
class Other extends StatefulWidget {
#override
_OtherState createState() => _OtherState();
}
class _OtherState extends State<Other> {
var from = [
'Addis Ababa', 'Adama', 'Dire Dawa', 'Ali Sabieh', 'Djibouti'
];
var fromSel = 'Addis Ababa';
var to = [
'Addis Ababa', 'Adama', 'Dire Dawa', 'Ali Sabieh', 'Djibouti'
];
var toSel = 'Djibouti';
#override
Widget build(BuildContext context) {
return Container(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
child: Container(
//height: 203,
child: Column(
children: [
SizedBox(height: 15,),
Container(
//decoration: BoxDecoration(border: Border.all(color: Colors.grey)),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
width: MediaQuery.of(context).size.width/2-19,
height: 60,
padding: EdgeInsets.symmetric(horizontal: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('From', style: TextStyle(
fontSize: 18,
color: Colors.grey
),),
SizedBox(height: 0,),
Expanded(
child: DropdownButton<String>(
underline: Container(color: Colors.transparent),
items: from.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value, style: TextStyle(
fontSize: 18
),),
);
}).toList(),
isExpanded: true,
isDense: false,
elevation: 5,
hint: Text('From'),
value: fromSel,
onChanged: (String newValue){
setState(() {
this.fromSel = newValue;
});
}),
),
],
),
),
Container(height: 50, child: VerticalDivider(color: Colors.grey)),
Container(
width: MediaQuery.of(context).size.width/2-19,
height: 60,
padding: EdgeInsets.symmetric(horizontal: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('To', style: TextStyle(
fontSize: 18,
color: Colors.grey
),),
SizedBox(height: 0,),
Expanded(
child: DropdownButton<String>(
underline: Container(color: Colors.transparent),
items: to.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value, style: TextStyle(
fontSize: 18
),),
);
}).toList(),
isExpanded: true,
isDense: false,
elevation: 5,
hint: Text('to'),
value: toSel,
onChanged: (String newValue){
setState(() {
this.toSel = newValue;
});
}),
),
],
),
),
],
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 10),
child: MaterialButton(
onPressed: () async{
await Navigator.of(context).push(MaterialPageRoute(builder: (context) => Search (
from: fromSel,
to: toSel,
depart: _startDate,
arrive: _endDate,
seat: _options[_selectedIndex],
adult: adultSel.toString(),
child: childSel.toString(),
infant: infantSel.toString(),
)));
},
minWidth: MediaQuery
.of(context)
.size
.width - 80,
height: 45,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
color: Colors.lightGreen,
splashColor: Colors.green,
child: Text(
"Search",
style: TextStyle(color: Colors.white, fontSize: 18),
),
),
)
],
),
),
),
);
}
}

Use StreamBuilder to display data for current user (not all users' data) from Firestore

I'm trying to use StreamBuilder in flutter to return a list, but only for the current user logged in. When I tried
StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('meals').where('email', isEqualTo: loggedInUser.email).orderBy('date', descending: true).snapshots(),
I keep getting the following error:
The getter 'email' was called on null.
Receiver: null
Tried calling: email
It works fine when I take the .where part out, but then it just returns all data from every single user, and I only want it to return the data linked to that current user.
Any ideas would be really helpful so thank you in advance.
Jason
EDIT:
Here's some more code for a bit more context:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/widgets.dart';
import 'package:intl/intl.dart';
import 'package:less_food/utilities/add_edit_meal_log.dart';
import 'package:less_food/utilities/constants.dart';
final _firestore = Firestore.instance;
FirebaseUser loggedInUser;
class MealLog extends StatefulWidget {
#override
_MealLogState createState() => _MealLogState();
}
class _MealLogState extends State<MealLog> {
final _auth = FirebaseAuth.instance;
FirebaseUser loggedInUser;
#override
void initState() {
super.initState();
getCurrentUser();
}
void getCurrentUser() async {
try {
FirebaseUser user = await _auth.currentUser();
if (user != null) {
setState(() {
loggedInUser = user;
print('this is: ${loggedInUser.email}');
});
}
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
MealsStream(),
],
),
),
);
}
}
class MealsStream extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('meals').where('email', isEqualTo: loggedInUser.email).orderBy('date', descending: true).snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent,
),
);
}
final meals = snapshot.data.documents.reversed;
List<MealBubble> mealBubbles = [];
for (var meal in meals) {
final mealDate = meal.data['date'].toDate();
print(DateFormat('dd-MM-yyyy').format(mealDate));
final mealSelection = meal.data['mealSelection'];
final mealDietChoice = meal.data['dietChoice'];
final mealBubble = MealBubble(
date: mealDate,
selection: mealSelection,
dietChoice: mealDietChoice,
);
mealBubbles.add(mealBubble);
}
return Expanded(
child: ListView(
reverse: false,
padding: EdgeInsets.symmetric(vertical: 20.0),
children: mealBubbles,
),
);
},
);
}
}
class MealBubble extends StatelessWidget {
MealBubble({this.date, this.selection, this.dietChoice});
final DateTime date;
final String selection;
final String dietChoice;
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Container(
color: Colors.white,
child: Column(
children: <Widget>[
Material(
borderRadius: BorderRadius.circular(10.0),
elevation: 5.0,
color: Colors.lightBlueAccent,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 13.0),
child: Text(
'${DateFormat('dd-MM-yyyy').format(date)}',
style: TextStyle(
color: Colors.white,
fontSize: 15.0,
),
),
),
Text(
selection,
textAlign: TextAlign.start,
style: TextStyle(
color: Colors.white,
fontSize: 15.0,
),
),
SizedBox(
width: 2.0,
),
Text(
dietChoice,
textAlign: TextAlign.start,
style: TextStyle(
color: Colors.white,
fontSize: 15.0,
),
),
ButtonTheme(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
minWidth: 0,
child: FlatButton(
child: Text(
'Edit',
style: kMealLogEditButtonTextStyle,
),
onPressed: () {
showModalBottomSheet(
context: context,
builder: (context) => AddEditMealLog(),
isScrollControlled: true,
);
},
),
),
],
),
),
),
],
),
),
);
}
}
The problem is that your global variable loggedInUser is never initialized. Still, you do have another variable with the exact same name being initialized in another class and I think this may be causing you confusion.
As you can see, you declare two FirebaseUser objects with the name loggedInUser, one with a file scope (it exists globally on the file) and another within a class, thus having class scope (it only exists inside that class!). When you initialize loggedInUser in the function _MealLongState.getCurrentUser() you are using the class variable, not the global one. Then, when you access loggedInUser from the method MealsStream.build() you are getting the global variable, which hasn't been initialized.
The solution would be to remove the declaration of the variable loggedInUser from inside the class _MealLogState.

Cloud firestore documentation for flutter

I am trying to write an app with flutter using cloud firestore but in the examples page I do not see a flutter/dart option for the example code, am I missing something?
Here is where I am looking at https://firebase.google.com/docs/firestore/query-data/get-data
Any help would be great. Thanks
There are indeed no examples for Flutter in the Firebase documentation. What I do is that I read the Firebase documentation on the topic I'm trying to learn more about, and then use the FlutterFire documentation for Firestore to construct the corresponding Flutter example myself.
To navigate to the Flutter example, but step-wise it (currently) is:
Go to the home page of the cloud_firestore plugin.
Click on the Example tab
I also frequently use the FlutterFire reference documentation for Firestore to look up API signatures for Flutter, based on the examples in the Firebase documentation for Firestore.
Sorry for late answer,
I was resolving my own project issue.
By the way I have Implemented CRUD Operation with cloud_firestore plugin.
SEE_CRUD_OPREATION_OUTPUT_VIDEO
Here you can analyse my
full code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class CRUDoperation extends StatefulWidget {
#override
_CRUDoperationState createState() => _CRUDoperationState();
}
class _CRUDoperationState extends State<CRUDoperation> {
Firestore firestore = Firestore.instance;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("CRUD"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.group_add),
onPressed: (){
showDialog(
context: context,
child: ShowCustomDialogBox(oprationName: "Add",)
);
}
)
],
),
body: Container(
padding: const EdgeInsets.all(10),
alignment: Alignment.center,
child: StreamBuilder<QuerySnapshot>(
stream: firestore.collection('Employee').snapshots(),
builder: (BuildContext context,AsyncSnapshot<QuerySnapshot> snapshot){
if (snapshot.hasError){
return new Center(
child:Text('Error: ${snapshot.error}')
);
}
if(!snapshot.hasData){
return new Center(
child:CircularProgressIndicator()
);
}
else{
var documents = snapshot.data.documents;
if(documents.length>0){
return ListView.builder(
itemCount:documents.length ,
itemBuilder: (context, index){
return Card(
child: ListTile(
leading: IconButton(
icon: Icon(Icons.edit,color: Colors.blue,),
onPressed: (){
showDialog(
context: context,
child: ShowCustomDialogBox(
documentSnapshot:documents[index],
oprationName: "Edit",
)
);
}
),
title: Text(documents[index].data['Name']),
subtitle: Text(documents[index].data['Post']),
trailing: IconButton(
icon: Icon(Icons.delete,color: Colors.red,),
onPressed: (){
firestore.collection('Employee').document(documents[index].documentID)
.delete().then((onValue){ //delete user
print("Deleted successfully");
});
}
),
),
);
}
);
}else{
return Center(
child: Text("Add Emlopyee list"),
);
}
}
}
),
),
);
}
}
//ADD OR EDIT USER DIALOG BOX
class ShowCustomDialogBox extends StatefulWidget {
final DocumentSnapshot documentSnapshot;
final String oprationName;
ShowCustomDialogBox({ this.documentSnapshot, this.oprationName});
#override
State<StatefulWidget> createState() => ShowCustomDialogBoxState();
}
class ShowCustomDialogBoxState extends State<ShowCustomDialogBox>with SingleTickerProviderStateMixin {
TextEditingController nameController;
TextEditingController postController ;
Firestore firestore = Firestore.instance;
#override
void initState() {
super.initState();
nameController = widget.oprationName == "Edit" ? TextEditingController(text: widget.documentSnapshot.data['Name'])
: TextEditingController();
postController = widget.oprationName == "Edit"? TextEditingController(text:widget.documentSnapshot.data['Post'])
: TextEditingController();
}
launchOpration(){
if(widget.oprationName == "Edit"){
editEmployee();
}else{
addEmployee();
}
}
addEmployee(){ //Create user
if(nameController.text.isNotEmpty && postController.text.isNotEmpty){
firestore.collection("Employee").add({
'Name':nameController.text,
'Post':postController.text
})
.then((doc){
print("employee added successfully documentID :${doc.documentID}");
nameController.clear();
postController.clear();
Navigator.of(context).pop();
});
}
else{
print("Please all fields");
}
}
editEmployee(){ //Update User
firestore.collection('Employee').document(widget.documentSnapshot.documentID).updateData({
'Name':nameController.text,
'Post':postController.text
}).then((onValue){
print("employee Edited successfully");
nameController.clear();
postController.clear();
Navigator.of(context).pop();
});
}
#override
void dispose() {
nameController.dispose();
postController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Center(
child: Material(
color: Colors.transparent,
child: Container(
margin: EdgeInsets.all(20.0),
padding: EdgeInsets.all(8.0),
height: MediaQuery.of(context).size.height/2.5,
width: MediaQuery.of(context).size.width,
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0))),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("${widget.oprationName} Employee"),
SizedBox(height:10),
TextField(
controller: nameController,
decoration: InputDecoration(
hintText: "Enter Name",
border: OutlineInputBorder()
),
),
SizedBox(height:10),
TextField(
controller: postController,
decoration: InputDecoration(
hintText: "Enter Post",
border: OutlineInputBorder()
),
),
],
),
),
Padding(
padding: const EdgeInsets.only(
left: 20.0, right: 10.0, top: 0.0,),
child: ButtonTheme(
height: 35.0,
minWidth: MediaQuery.of(context).size.width/3.5,
child: RaisedButton(
color: Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0)),
splashColor: Colors.white.withAlpha(40),
child: Text(
widget.oprationName,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 13.0),
),
onPressed: () {
launchOpration();
},
)
)
),
],
)
),
),
);
}
}