I am trying to get set of encrypted data documents from firebase and display them on a list view on flutter.
I used a stream builder for obtaining data and started displaying it on the list view. But I cant perform the decryption operation on each data item as it is an async operation. What is the best way to do this?
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance
.collection(ScopedModel.of<User>(context).userId)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(
child: new Container(
child: CircularProgressIndicator(),
));
default:
if (snapshot.data.documents.length == 0) {
return Container(
padding: EdgeInsets.all(16.0),
child: Row(
children: <Widget>[
Text('Empty',),
],
),
);
}
final docs = snapshot.data.documents;
return ScrollConfiguration(
behavior: ScrollBehavior(),
child: ListView.builder(
itemCount: len,
scrollDirection: Axis.horizontal,
itemBuilder: (context, position) {
// Where should I decrypt the below data?
// let decrypted = await myDecryptionFunction(docs[position]['myDataKey']) ;
// the above is not working
// this will show the encrypted text
return Text(docs[position]['myDataKey']);
}
....
For your situation you can use a StreamController with a helper class to hold the information.
The following is just an example but adapt it to your own needs.
// Helper classes
// adapt it to your own business case
class Notes {
String title;
String description;
Notes({this.title, this.description});
}
class NotesFromDb {
String connectionState;
bool hasError;
String error;
List<Notes> notes;
NotesFromDb({this.connectionState, this.hasError, this.error, this.notes});
}
// The Streambuilder
StreamBuilder<NotesFromDb>(
stream: sController.stream,
builder: (BuildContext context, AsyncSnapshot<NotesFromDb> snapshot) {
// Here you can check for errors
if (snapshot.data.hasError == true) {
return Container(
color: Colors.red,
child: Text(snapshot.data.error),
);
}
// Here you can check for your connection state
if (snapshot.data.connectionState == 'Loading') {
return Container(
color: Colors.yellow,
child: CircularProgressIndicator(),
);
}
// Here you can show your data
var info = snapshot.data.notes.map((doc) {
return Text(doc.title);
});
return Center(
child: Container(
color: Colors.deepOrange,
child: Column(
children: info.toList(),
),
));
})
// Here is how you can handle the decrypt data
// using a FloatingActionButton for loading the data (for example)
FloatingActionButton(
onPressed: () async { // you would need to add the async
List<Notes> theNotes; //just to hold the information
// Use this to allow to show the CircularProgressIndicator
sController.sink.add(NotesFromDb(connectionState: 'Loading'));
var snapshots = Firestore.instance.collection('notes').snapshots();
snapshots.listen((QuerySnapshot data) {
theNotes = data.documents.map((DocumentSnapshot doc) {
// Build your data
return Notes(
title: doc.data['title'],
description: doc.data['description']);
}).toList();
}, onError: (err, stack) {
// If an error happend then send the error to the stream
sController.sink
.add(NotesFromDb(hasError: true, error: err.error));
});
// Here you can to decrypt the documents with your function
var decryptDocuments = await Future.delayed(Duration(seconds: 2)); //Whatever future function
// Once you have the decrypt documents, you would need to send that to the stream.
// set the connectionState to Done, so the spinner is not showed anymore.
sController.sink.add(NotesFromDb(
hasError: false, connectionState: 'Done', notes: decryptDocuments));
},
child: Icon(Icons.arrow_forward),
)
Take the example just for illustration on how to do it.
Hope this help.
Related
I make a chat using firebase firestore.
So. I tried express not read count in the Chat list.
But, Initially the number appears, but it changes to null data.
I don't know Why data chage null data?
body: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('chatRooms')
.where('emails', arrayContainsAny: [user?.email]).snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
var chatLists = snapshot.data?.docs;
if (snapshot.hasError) {
return Text('Something error...');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text('is Loading...');
}
return ListView.builder(
itemCount: chatLists?.length,
itemBuilder: (context, index) {
if (chatLists?[index]['currentMsg'] != null &&
chatLists?[index]['currentMsg'] != "") {
var list = List.from(chatLists?[index]['members']);
var member = '';
if (loginUser['userName'] != null) {
for (int i = 0; i < list.length; i++) {
if (list[i] != loginUser['userName']) {
member += list[i];
}
}
}
return ListTile(
title: Row(
children: [
Text(member),
const SizedBox(
width: 20.0,
),
ChatLength(docId: chatLists![index].id, uid: user!.uid),
],
),
subtitle: SizedBox(
height: 40.0,
child: Text(
chatLists[index]['currentMsg'],
overflow: TextOverflow.ellipsis,
)),
trailing: Text(
tmFormmater(chatLists?[index]['currentTm']),
style: const TextStyle(
color: Color(0xff999999),
),
),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChatScreen(
docId: chatLists![index].id,
title: member,
),
)),
);
} else {
return Container();
}
},
);
return Container();
},
),
class ChatLength extends StatelessWidget {
const ChatLength({super.key, required this.docId, required this.uid});
final String docId;
final String uid;
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection('message_read')
.doc(docId)
.collection('message')
.where('userId', isNotEqualTo: uid)
.where('read', isEqualTo: false)
.snapshots(),
builder: (context, snapshot) {
print(snapshot.data?.size);
if (snapshot.data?.size != null) {
return Container(
child: Text('${snapshot.data?.size}'),
);
} else {
return Container();
}
},
);
}
}
===== Debug console ====
flutter: 1
flutter: null // Data changes to null immediately
message_read collection structure
message_read -> documentId -> message -> documentId -> field(userId(chat writer), read)
I'm trying get a snapshot data from firestore.
And I put the imported data into the text.
But Data changes to null immediately.
Since you use snapshots() the code is not just reading the data once, but then continues listening for changes to the data. So the two values you see mean that the snapshots() stream was triggered twice, the first time with a single message - and the second time without any messages.
My educated guess is that your code changes the read value of the document after it gets it, since it has now displayed that message to the user. But doing so means the document no longer meets the criteria of your query, so the snapshots stream gets a new event without that message in it.
Consider using a different mechanism for the query, for example I usually use a timestamp to determine what messages to show. Step by step:
Ensure each message document has a timestamp field.
For each user store (either in the database or in local storage of the app) when they last started the app.
Then request from the database the messages since they last started the app.
Make sure to start the query before you update the timestamp value, otherwise you'll never get any results.
I am working on app to import a lit from google drive in the form of JSON file, the App will read the JSON file only, the reason is I am not using Firestore Database is because it delays the build of the App & whatever I tried still I have errors & I can't build the APP on IOS device or simulator, so every time I will try to import the data the App will show error. my code is as the following
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Samer'),
),
body: FutureBuilder<List<User>>(
future: UsersApi.getApps(),
builder: (context, snapshot) {
final users = snapshot.data;
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(child: CircularProgressIndicator());
default:
if (snapshot.hasError) {
return const Center(child: Text('error'));
} else {
return buildUsers(users!);
}
}
},
),
);
}
Widget buildUsers(List<User> users) => ListView.builder(
physics: BouncingScrollPhysics(),
itemCount: users.length,
itemBuilder: (BuildContext context, int index) {
final user = users[index];
return Padding(
padding: const EdgeInsets.all(15.0),
child: Container(
color: kOurAppsMainScreenCards,
child: ListTile(
title: Text(user.appName),
// leading: CircleAvatar(
// backgroundImage: NetworkImage(user.imageName),
// ),
// subtitle: Platform.isIOS
// ? Text(user.paidFreeIOS)
// : Text(user.paidFree),
),
),
);
},
);
}
and I am using http package as following:
class UsersApi {
static Future<List<User>> getApps() async {
final url = Uri.parse(
'https://drive.google.com/file/d/1tAxO2kRD0NVBhefA3srbj1SKQ2l8u9Wc/view?usp=sharing');
final response = await http.get(url);
final body = json.decode(response.body);
return body.map<User>(User.fromJson).toList();
}
}
the stage thing is I inserted the same JSON file in firestore storage and the App read it....
Can somebody please help me.
Regards,
I finally found a solution to this, the main issue is in the link itself, we have to do some modification to the link & then we can use it freely, for example:
if this is the file link, we will use the bold lines in the modified link
https://drive.google.com/file/d/1-T9OTLTZXTB7ydqV3tcgG4T2FQckMooB/view?usp=sharing
and we have to add few words to make the link workable 100%,
the following code to be added
uc?export=view&id=
the new link will be
https://drive.google.com/uc?export=view&id=1-T9OTLTZXTB7ydqV3tcgG4T2FQckMooB
Solution ref (it was for photos but it worked in JSON files also)
https://youtu.be/0ZHqrf0mzrI
I am new with flutter,
I would be grateful if you could help me with this problem:
I have a huge set of firestore data that I have to display i a listview, so I had to use Lazy Loading when I used it, the problem appeared, and I can't search or filter in all the data, only the data shown
so I need to use lazy Load or pagination and in the same time can search or filter all the firestore data
enter image description here
and
enter image description here
void initState() {
_chatScrollController = ScrollController()
..addListener(() {
if (_chatScrollController!.position.atEdge) {
if (_chatScrollController!.position.pixels == 0)
;
else {
setState(() {
loadMoreMsgs = loadMoreMsgs + a;
});
}
}
});
super.initState();
}
stream: FirebaseFirestore.instance
.collection(kProductsCollection)
.limit(loadMoreMsgs)
.snapshots(),
builder: (context, snapshots) {
return (snapshots.connectionState == ConnectionState.waiting)
? const Center(
child: CircularProgressIndicator(),
)
: ListView.builder(
controller: _chatScrollController,
itemCount: snapshots.data!.docs.length,
itemBuilder: (context, index) {
var data = snapshots.data!.docs[index].data()
as Map<String, dynamic>;
List<Product> products = [];
for (var doc in snapshots.data!.docs) {
products.add(
Product(
pId: doc.id,
pName: doc.get(kProductName),
pPrice: doc.get(kProductPrice),
pUrl: doc.get(kProductUrl),
pIngredients: doc.get(kProductIngredients),
pCompany: doc.get(kProductCompany),
pDose: doc.get(kProductDose),
pCode: doc.get(kProductCode),
pClass: doc.get(kProductClass),
pCountry: doc.get(kProductCountry),
pDescription: doc.get(kProductDescription),
pPregnancyCategory:
doc.get(kProductPregnancyCategory),
),
);
}
if (products[index]
.pName
.toString()
.toLowerCase()
.contains(name.toLowerCase())) {
return Padding(
padding: const EdgeInsets.fromLTRB(20, 5, 20, 5),
child: GestureDetector(
onTap: () {
Navigator.pushNamed(context, ProductDetalis.id,
arguments: products[index]);
},
child: CardWidget(
width: width, height: height, data: data)),
);
}
I'm a beginner in flutter and want to program a app with Bluetooth LE. I use flutter_blue. My App works if i only want to read the value of one characteristic. Now i need to read a second characteristic and that is the problem. I'm using a example from the esp32 dust sensor. The main is similar to the flutter blue example. The step to the next site is work with the following code:
StreamBuilder<List<ScanResult>>(
stream: FlutterBlue.instance.scanResults,
initialData: [],
builder: (c, snapshot) => Column(
children: snapshot.data
.map(
(r) => ScanResultTile(
result: r,
onTap: () => Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
r.device.connect();
return SensorPage(device: r.device);
})),
),
That works ok. At the Sensor-Page there is the code for the Page which starts with:
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
appBar: AppBar(
title: Text(' Connect'),
),
body: Container(
// color: Colors.purple,
decoration: BoxDecoration(
image: DecorationImage(
// image: AssetImage("lib/Images/knaus.jpg"),
image: AssetImage("lib/Images/innen.jpg"),
fit: BoxFit.cover,
),
),
child: !isReady
? Center(
child: Text(
"Warte auf Verbindung...",
style: TextStyle(fontSize: 24, color: Colors.red),
),
)
: Container(
child: StreamBuilder<List<int>>(
stream: wt_Ist_stream,
builder: (BuildContext context,
AsyncSnapshot<List<int>> snapshot) {
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
if (snapshot.connectionState ==
ConnectionState.active) {
_DataParser(snapshot.data);
The characteristics i generate with:
List<BluetoothService> services = await widget.device.discoverServices();
services.forEach((service) {
if (service.uuid.toString() == SERVICE_UUID) {
service.characteristics.forEach((characteristic) {
if (characteristic.uuid.toString() == CHARACTERISTIC_UUID_WT_RcvWater) {
characteristic.setNotifyValue(!characteristic.isNotifying);
characteristic1=characteristic;
// characteristic1.setNotifyValue(!characteristic.isNotifying);
wt_Ist_stream = characteristic1.value;
// wt_Soll_stream = service.characteristic.value;
setState(() {
isReady = true;
});
}
if (characteristic.uuid.toString() == CHARACTERISTIC_UUID_WT_Soll) {
characteristic.setNotifyValue(!characteristic.isNotifying);
characteristic2=characteristic;
// characteristic2.setNotifyValue(!characteristic2.isNotifying);
wt_Soll_stream = characteristic2.value;
// characteristic2.value.listen((InputString2)
// wt_Soll_stream = service.characteristic.value;
}
//Updating characteristic to perform write operation.
if (characteristic.uuid.toString() == CHARACTERISTIC_UUID_WT_TxWater) {
characteristic_Write=characteristic;
}
});
}
});
if (!isReady) {
_Pop();
}
}
Both reading-Characteristics are send Data to the app and i get the notification, that new data available [onCharacteristicChanged] uuid: 456e869c-d393-4cec-9f43-cef5382eab72].
but my Dataparser will only start if the value for uuid ...b72(wt_Ist_stream) changed. Then he gets the right string from snapshot.data.
If i changed the Streambuilder-Stream
child: StreamBuilder<List<int>>(
stream: wt_Ist_stream,
to
child: StreamBuilder<List<int>>(
stream: wt_Soll_stream,
my dataparser gets the value from characteristic 2. But how can i change my app that my parser starts automatic if one stream (wt_Ist_Stream or wt_Soll_stream) changed and then gets the right value.
The Streambuilder sends the right data to the dataparser but only the stream, which is called in stream:
How can i changed the code, that my parser starts on a stream-change and gets the right values from snapshot.data?
The dataparser-code:
_DataParser(List<int> dataFromDevice) {
String InputString = utf8.decode(dataFromDevice);
if (InputString.length>6) {.....}
I'm right now trying to make a Quiz like application in flutter for learning this Framework.
I've implemented Firebase Firestore to manage the application data.
From this flutter plugin documentations I read about binding a CollectionReference to a ListView and it was pretty easy.
My problem is the following.
I've got some categories to display in the Home page, I want the user to be able to select which one he wants and then store this information in a List.
With this code I can display the list:
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('Categorie').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Center(
child: Column(
children: <Widget>[
SizedBox(
child: CircularProgressIndicator(),
height: 50.0,
width: 50.0,
),
Text('Dowload delle categorie...')
],
),
);
default:
_loadListOfCategories(snapshot);
return new ListView(
children:
snapshot.data.documents.map((DocumentSnapshot document) {
var nome = document['Nome'];
print('doc: $nome');
return new CategoryWidget(
id: document.documentID,
tap: onCategoryTap,
nome: document['Nome'],
count: document['Count'],
checked: false,
);
}).toList(),
);
}
},
);
}
CategoryWidget is just a simple stateless widget which act as a ListTile.
The result is the following:
Now, how can I save a List full of Category models, which one implementing a "checked/unchecked" property, and how can I keep this List updated?
I tried using "_loadListOfCategories()" method exacly inside the builder:
void _loadListOfCategories(AsyncSnapshot<QuerySnapshot> snapshot) {
var temporalList = new List<CategoryModel>();
for (DocumentSnapshot doc in snapshot.data.documents) {
temporalList.add(new CategoryModel(
id: doc.documentID,
count: doc['Count'],
nome: doc['Nome']));
}
setState(() {
_listOfCategories = temporalList;
});
}
But I couldn't call setState() here becouse I'm actually inside the building method.
use a map, your key will be 'document.documentID' and the value a boolean.
Map map = Map() // assuming this a string document.documentID
checked: map.get(document.documentID),
in your checkbox you call
setState()=>
map.put(document.documentID, !map.get(document.documentID));