Related
I am populating a list of orders from firestore database in flutter. but order screen is doubling the documents at every refresh. Kindly help.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class FullOrder extends StatelessWidget {
const FullOrder({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
// it enable scrolling on small device
return Center(
child: FutureBuilder(
future: FirebaseFirestore.instance.collection('orders').get(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done ||
snapshot.hasData) {
List<QueryDocumentSnapshot> docs = (snapshot.data! as QuerySnapshot).docs;
return ListView.builder(
shrinkWrap: true,
itemCount: docs.length,
itemBuilder: (context, index) {
var data = docs[index].data();
var dataOrders = data['orders'] as List<dynamic>;
var mappedDataOrders = dataOrders.map((o) => (o as Map<String, dynamic>)).toList();
return Card(
child: Container(
padding: const EdgeInsets.all(4),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Order Id: ${data['orderId']}'),
],
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Order Date: ${DateFormat('dd/MM/yyyy').format(data['createdAt'].toDate())}'),
Text('Order Time: ${DateFormat('hh:mm a').format(data['createdAt'].toDate())}'),
],),
),
Padding(
padding: const EdgeInsets.all(4.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Name: ${data['name']}', style: const TextStyle(fontWeight: FontWeight.bold),),
Text('Contact: ${data['phoneNumber'].toString()}', style: const TextStyle(fontWeight: FontWeight.bold),),
],
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Delivery Slot', style: TextStyle(fontWeight: FontWeight.bold),),
Text('${data['deliverySlot']}'),
],
),
Column(
children: [
Text('Address: ${data['addressType']}', style: const TextStyle(fontWeight: FontWeight.bold),),
Text('${data['address']}'),
Text('${data['area']}'),
Text('${data['city']}')
],
)
],
),
...List.generate(
mappedDataOrders.length,
(index) {
var order = mappedDataOrders[index] as Map<String, dynamic>;
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(order['title']),
Text(order['unit'].toString()),
Text(order['quantity'].toString()),
Text(order['price'].toString()),
],
),
],
);
}
)
]
),
)
);
}
);
}
return const Center(
child: SizedBox(
width: 100,
height: 100,
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.black),
strokeWidth: 5
)
)
);
}
),
);
}
}
When you convert the Database list to map instead of converting to toList() USE toSet(), this helps you out to filter out the duplicating item.
{
// use toSet() to filter the duplicated items.
var mappedDataOrders = dataOrders.map((o) => (o as Map<String, dynamic>)).toSet();
}
Use the .clear(); Because everytime you load youre map instead of restarting the counter id adds to it.
Where you create youre load mappedDataOrdes. You migth have it like this.
Future<List<Database>> searchDatabase() async{
final url= Uri.https(_baseUrl, 'Database.json');
final resp = await http.get(url);
final Map<String,dynamic> databaseMap= jsonDecode(resp.body);
////This is where you migth use the .clear() this will reset the counter for the index and eliminate duplicate
database.clear();
database.forEach((key, value) {
final tempDatabase= Database.fromMap(value);
tempDatabase.id=key;
database.add(tempDatabase);
});
return negocio;
}
The code below is a StreamBuilder that prints several fields from a stream. Is it possible to add a ternary or if operator to the Text widget that checks if the result is above zero, and if so, add a + prefix to the output? Would it also be possible to change the color of the text to green if it is above zero and red if it is below?
Text(
data['pointsfrom'].toString(),
style: GoogleFonts.poppins(
color: Colors.white70,
fontSize: 27,
),
),
Full Code
import 'package:appforpoints/dashboardPages/adminPage/listCards/admin_notif_card.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:google_fonts/google_fonts.dart';
class PointsHistory extends StatefulWidget {
final String username;
const PointsHistory({Key? key, required this.username}) : super(key: key);
#override
_PointsHistoryState createState() => _PointsHistoryState();
}
class _PointsHistoryState extends State<PointsHistory> {
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xff2F3439),
body: SizedBox(
height: double.infinity,
child: Padding(
padding: const EdgeInsets.only(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: ListView(
scrollDirection: Axis.vertical,
children: [
Padding(
padding: const EdgeInsets.only(
top: 80,
left: 23,
),
child: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("pointslogs")
.where("uid",
isEqualTo:
FirebaseAuth.instance.currentUser!.uid)
.where('username', isEqualTo: widget.username)
.orderBy('timestamp', descending: true)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState ==
ConnectionState.waiting) {
return const Text("Loading");
}
return Column(
children: snapshot.data!.docs.map(
(DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return Column(
children: [
Row(
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
Text(
data['pointsfrom'].toString(),
style: GoogleFonts.poppins(
color: Colors.white70,
fontSize: 27,
),
),
Padding(
padding: const EdgeInsets.only(
left: 12, right: 12),
child: Text(
'>',
style: GoogleFonts.poppins(
color: Colors.white70,
fontSize: 27,
),
),
),
Text(
data['pointsto'].toString(),
style: GoogleFonts.poppins(
color: Colors.white70,
fontSize: 27,
),
),
Padding(
padding:
const EdgeInsets.only(left: 20),
child: Text(
data['pointschange'].toString() +
" ",
style: GoogleFonts.poppins(
color: Colors.white70,
fontSize: 27,
),
),
),
],
),
Row(
children: [
Text(
data['date'].toString(),
),
Text(
data['time'].toString(),
),
],
)
],
);
},
).toList(),
);
},
),
),
],
),
),
],
),
),
),
);
/*Scaffold(
backgroundColor: const Color(0xff1e272c),
body: ListView(
scrollDirection: Axis.horizontal,
children: [
StreamBuilder<QuerySnapshot>(
stream: userLog,
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}
return Row(
children: snapshot.data!.docs.map(
(DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return Column(
children: [
Text(data['pointschange']),
Text(data['pointsto']),
],
);
},
).toList(),
);
},
),
],
),
);*/
}
}
You mean like this:
Text(
(data['pointsfrom'] >= 0 ? '+' : '') + data['pointsfrom'].toString(),
style: GoogleFonts.poppins(
color: data['pointsfrom'] >= 0 ? Colors.green : Colors.red,
fontSize: 27,
),
),
Suggestion: I noticed you were repeating the style over and over (GoogleFonts.poppins); you could either set it once at the top of the build method or create a separate utility class where you can have all these values there. You could create styles per condition, which you could also extract as a separate method that generates the proper styles for you based on the desired condition.
Also you could move the prefixing logic to a separate method so the ternary operation doesn't look too messy.
I have just completed this basic chat app based on a tutorial on YouTube. NO user name needed for using it. But my messages are not ordering correctly. It is sorting as auto generated ID on FireStore. There are total two field in Firebase "message" collection that I have created
"username" {this user name randomly generated by FireBase as user ID. example: 1Mrayhz7EKL7MklHXUxv}
"messagetext"
I don't understanding what should I do now
//Here's the code
class MyApp extends StatelessWidget {
final TextEditingController messaingTextBox = TextEditingController();
final CollectionReference cr =
FirebaseFirestore.instance.collection('messages');
#override
Widget build(BuildContext context) {
body:
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Expanded(
child: StreamBuilder(
stream: cr.snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) return CircularProgressIndicator();
return ListView.builder(
reverse: true,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(18.0),
child: Column(
children: [
Row(
children: [
Container(
child: Padding(
padding: const EdgeInsets.all(6.0),
child: Text(
snapshot.data.documents[index]
.data()['username'],
),
),
),
SizedBox(
width: 10,
),
Container(
// width: 38.0,
width: MediaQuery.of(context).size.width / 1.6,
child: Padding(
padding: const EdgeInsets.all(9.0),
child: Column(
children: [
Text(
snapshot.data.documents[index]
.data()['messagetext'],
),
],
),
),
),
],
),
],
),
);
},
);
},
),
),
Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: TextFormField(
cursorColor: HexColor("#003F51"),
style: TextStyle(color: Colors.black),
controller: messaingTextBox,
),
),
),
FloatingActionButton(
onPressed: () {
Message m = Message(
username: "unkown ",
messagetext: messaingTextBox.text.toString());
cr.add(m.mapMessages());
},
child: Icon(Icons.send),
),
],
),
],
);
}
}
class Message {
final String username;
final String messagetext;
Message({this.messagetext, this.username});
mapMessages() {
return {'messagetext': messagetext, 'username': username};
}
}
Please help me for solving this isue
You need to add third field to store time.
Then in the code try to add orderBy operator.
FirebaseFirestore.instance.collection('messages')
.orderBy('time_field', descending: true);
It's quite simple just add a timestamp with each message lets say createdAt, and the use
NOTE: The type of createdAt must be Date
FirebaseFirestore.instance.collection('messages').orderBy('createdAt', descending: true)
I am building a Flutter app and I have a screen with 2 ListViews. The data source is a firestore database and it is the same for both list however one list presents only an image while the other list presents the image PLUS other information.
I have managed to found a solution to display both List however it does not seem the most effective because I believe I am downloading the same data 2 time. Do you have any suggestion on how to make it better???
See my code below
final _firestore = Firestore.instance;
class ItemPage extends StatefulWidget {
#override
_ItemPageState createState() => _ItemPageState();
}
class _ItemPageState extends State<ItemPage> {
bool showSpinner = false;
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
heroTag: 'itemppage',
transitionBetweenRoutes: false,
middle: Text(
appData.categoryName,
style: kSendButtonTextStyle,
),
),
child: Scaffold(
backgroundColor: kColorPrimary,
body: ModalProgressHUD(
inAsyncCall: showSpinner,
child: Column(
children: <Widget>[
Expanded(
child: ItemList(),
),
Container(
height: 80.0,
child: ItemListBottomScroll(),
)
],
),
),
),
);
}
}
class ItemList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _firestore
.collection('books')
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Text(
'Loading...',
style: kSendButtonTextStyle,
);
default:
return new PageView(
scrollDirection: Axis.horizontal,
children:
snapshot.data.documents.map((DocumentSnapshot document) {
return SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.all(10.0),
child: Stack(
children: <Widget>[
CachedNetworkImage(
imageUrl: document['url'],
placeholder: (context, url) =>
new CircularProgressIndicator(),
errorWidget: (context, url, error) =>
new Icon(Icons.error),
width: MediaQuery.of(context).size.width - 20,
height:
(MediaQuery.of(context).size.width - 20),
fit: BoxFit.cover),
Positioned(
bottom: 0.0,
right: 0.0,
child: IconButton(
color: kColorAccent.withOpacity(0.8),
iconSize: 50.0,
icon: Icon(Icons.add_circle),
onPressed: () {
print(document['name'] +
document.documentID +
' clicked');
},
),
),
],
),
),
Padding(
padding: const EdgeInsets.only(
left: 10.0, right: 10.0, bottom: 10.0),
child: Row(
children: <Widget>[
Expanded(
child: Text(
document['name'],
style: kSendButtonTextStyle,
),
flex: 3,
),
Expanded(
child: Text(
appData.currency + document['price'],
textAlign: TextAlign.right,
style: kDescriptionTextStyle,
),
flex: 1,
),
],
),
),
Padding(
padding: const EdgeInsets.only(left: 10.0, right: 10.0),
child: Container(
child: Text(
document['description'],
style: kDescriptionTextStyle,
),
width: double.infinity,
),
)
],
),
);
}).toList(),
);
}
},
);
}
}
class ItemListBottomScroll extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _firestore
.collection('books')
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Text('Loading...');
default:
return new ListView(
scrollDirection: Axis.horizontal,
children:
snapshot.data.documents.map((DocumentSnapshot document) {
return Stack(
children: <Widget>[
Container(
height: 80.0,
width: 90.0,
padding: EdgeInsets.only(left: 10.0),
child: GestureDetector(
onTap: () {
print(document['name'] +
document.documentID +
' bottom clicked');
},
child: new CachedNetworkImage(
imageUrl: document['url'],
placeholder: (context, url) =>
new CircularProgressIndicator(),
errorWidget: (context, url, error) =>
new Icon(Icons.error),
fit: BoxFit.cover),
),
),
],
);
}).toList(),
);
}
},
);
}
}
In order to read both info from the same Stream you need to expose it as a broadcast stream first:
final streamQuery = _firestore
.collection('books')
.snapshots()
.asBroadcastStream();
return StreamBuilder<QuerySnapshot>(
stream: streamQuery,
builder: ...
I am wondering how to get data from firestore to flutter app using the streambuilder. I created the necessary Boilerplate code I have the widget built and working and in the below code
headimageassetpath is nothing but a URL string which exists in the firestore.
#override
Widget build(BuildContext context) {
return Scaffold(
body:
new StreamBuilder(
stream: Firestore.instance.collection('Items').snapshots(),
builder: (_, AsyncSnapshot<QuerySnapshot> snapshot) {
var items = snapshot.data?.documents ?? [];
return new Lost_Card(
headImageAssetPath : snapshot.data.documents.map()(['url'],)
);
},
)
My firestore:
full code:
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class LostPage extends StatefulWidget {
#override
_LostPage createState() => new _LostPage();
}
class _LostPage extends State<LostPage> {
//temp vars
final String firebasetest = "Test";
//firestore vars
final DocumentReference documentReference =
Firestore.instance.document("Items/Rusty");
//CRUD operations
void _add() {
Map<String, String> data = <String, String>{
"name": firebasetest,
"desc": "Flutter Developer"
};
documentReference.setData(data).whenComplete(() {
print("Document Added");
}).catchError((e) => print(e));
}
#override
Widget build(BuildContext context) {
return Scaffold(
body:
new StreamBuilder(
stream: Firestore.instance.collection('Items').snapshots(),
builder: (_, AsyncSnapshot<QuerySnapshot> snapshot) {
var items = snapshot.data?.documents ?? [];
return new Lost_Card(
headImageAssetPath : snapshot.data.documents.map()(['url'],)
);
},
)
/*new Lost_Card(
headImageAssetPath: "https://i.imgur.com/FtaGNck.jpg" ,
title: "Mega Dish",
noro: "old",
)*/,
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.add),
onPressed: _add),
);
}
}
class Lost_Card extends StatelessWidget
{
//All the card variables
final String headImageAssetPath;
final IconData icon;
final Color iconBackgroundColor;
final String title;
final String noro;
final int price;
final ShapeBorder shape;
Lost_Card({
this.headImageAssetPath, //used
this.icon,
this.iconBackgroundColor,
this.title, //used
this.noro, //used
this.price,
});
#override
Widget build(BuildContext context) {
// TODO: implement build
return GridView.count(
shrinkWrap: true,
crossAxisCount: 2,
children: <Widget>[
Card(
child: Column(
// mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height / 4,
width: MediaQuery.of(context).size.height / 2.5,
child: DecoratedBox(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
headImageAssetPath),
fit: BoxFit.cover),
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Align(
alignment: FractionalOffset.topLeft,
child: CircleAvatar(
backgroundColor: Colors.redAccent,
radius: 15.0,
child: Text(
noro,
textScaleFactor: 0.5,
),
),
),
),
Align(
alignment: FractionalOffset.topRight,
child: Container(
color: Colors.blueAccent,
height: 35.0,
width: 35.0,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.account_circle),
Text(
"1P",
textScaleFactor: 0.5,
),
],
),
),
),
),
],
),
),
Center(
child: Container(
padding: const EdgeInsets.all(8.0),
alignment: FractionalOffset.bottomCenter,
child: Text(
title,
style: TextStyle(
fontWeight: FontWeight.w700,
),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
FlatButton(
child: Text(
"Add To Cart",
style: TextStyle(color: Colors.grey[500]),
),
onPressed: () => null,
),
Text(
"\$5",
style: TextStyle(color: Colors.grey[500]),
)
],
)
],
),
),
],
);
}
}
Actual App
Please shed some light on this. Tks.
This should work for one item
body: new StreamBuilder(
stream: Firestore.instance.collection("collection").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text(
'No Data...',
);
} else {
<DocumentSnapshot> items = snapshot.data.documents;
return new Lost_Card(
headImageAssetPath : items[0]["url"]
);
}
If you want to create list builder from many documents use it like this
return new ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot ds = snapshot.data.documents[index];
return new Lost_Card(
headImageAssetPath : ds["url"];
);
Accessing documents using StreamBuilder in Flutter 2
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection('products').snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot doc = snapshot.data!.docs[index];
return Text(doc['name']);
});
} else {
return Text("No data");
}
},
)
As per new changes 2021 in Firebase FireStore you can retrieve data from collection using StreamBuilder as below
final _mFirestore = FirebaseFirestore.instance;
return StreamBuilder<QuerySnapshot>(
stream:
_mFirestore.collection(kFirebaseCollectionName).snapshots(),
builder: (context, snapshots) {
if (!snapshots.hasData) {
return Center(
child: Text('Data not available',),
);
}
final messages = snapshots.data.docs;
List<Text> textWidgets = [];
messages.forEach((element) {
final messageText = element['text'];
final messageSender = element['sender'];
final textWidget = Text('$messageText, $messageSender');
textWidgets.add(messageBubbleWidget);
});
},
);
Card buildItem(DocumentSnapshot doc) {
return Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'name: ${doc.data['name']}',
style: TextStyle(fontSize: 24),
),
Text(
'todo: ${doc.data['todo']}',
style: TextStyle(fontSize: 20),
),
Text(
'Age: ${doc.data['age']}',
style: TextStyle(fontSize: 10),
),
SizedBox(
height: 12,
),
],
)
],
),
),
); }
For other persons who will face the same problem, the card and stream builder will represent a solution. The Widget has the Card just before it declaration and has inside the body the next part:
body: ListView(
padding: EdgeInsets.all(8),
children: <Widget>[
Form(
key: _formKey,
child: buildTextFormField(),
),
StreamBuilder<QuerySnapshot>(
stream: db
.collection('CRUD')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: snapshot.data.documents
.map((doc) => buildItem(doc))
.toList());
} else {
return SizedBox();
}
},
)
],
),
Update for 2022, Flutter 2.10, cloud_firestore: ^3.1.11. You can retrieve data from collection using StreamBuilder
Stream collectionStream = FirebaseFirestore.instance.collection('users').snapshots();
StreamBuilder<QuerySnapshot>(
builder: (context, snapshot) {
if (snapshot.hasData) {
final messages = snapshot.data!.docs;
List<Text> messageWidgets = [];
for (var element in messages) {
final messageText = element['text'];
final messageSender = element['sender'];
final messageWidget =
Text('$messageText from $messageSender');
messageWidgets.add(messageWidget);
}
return Column(
children: messageWidgets,
);
}
return const Text('Error');
},
stream:collectionStream),
StreamBuilder<List<UData>>(
stream: AdminData().getDrivers,
builder: (context, snapshot) {
return ListView(
children: snapshot.data.map((document) {
return hadCard(
widget: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
hadText(title: document.name),
hadText(title: document.phone),
hadText(title: document.Driver),
],
),
);
}).toList(),
);
}),