Get SharedPreferences before widget build - flutter

I want to get my userid before the build fires in my statefulwidget.
If I do this, then build will render before I get my id. If I put it in setstate, my build will use the empty string first and then rerenders it again with my id, but this will cause unnecessary behaviours.
So how do I solve this?
String _userid = '';
Future<Null> setUserid() async {
SharedPreferences pref = await SharedPreferences.getInstance();
_userid = pref.getString('FB_USER');
}
initState() {
super.initState();
setUserid();
}
Build
// Widget build
new Flexible(
child: new StreamBuilder<QuerySnapshot>(
stream: Firestore.instance
.collection('users')
.document(_userid)
.collection('rooms')
.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return new Text('Loading...');
return new ListView(
children: snapshot.data.documents
.map(
(DocumentSnapshot document) => new Text('lol'),
// )
//new OverviewPresentation(presentation: document),
)
.toList(),
);
},
),
),

You can use FutureBuilder
Future<String> setUserid() async {
SharedPreferences pref = await SharedPreferences.getInstance();
_userid = pref.getString('FB_USER');
return _userid;
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: setUserid(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
return ... // your widget
} else return CircularProgressIndicator();
});
Something like this

You can't, but you can guard against it being null.
Also it's easier to move the StreamBuilder to initState
String _userid = '';
dynamic _data;
Future<Null> setUserid() async {
SharedPreferences pref = await SharedPreferences.getInstance();
_userid = pref.getString('FB_USER');
_data = await Firestore.instance
.collection('users')
.document(_userid)
.collection('rooms')
.snapshots().first;
setState(() {});
}
initState() {
super.initState();
setUserid();
}
return new Flexible(
child:
if(_data == null) return new Text('Loading...');
return new ListView(
children: _data.documents
.map(
(DocumentSnapshot document) => new Text('lol'),
// )
//new OverviewPresentation(presentation: document),
)
.toList(),
);
},
),
),

Related

How to pull a data from firebase and use it for pulling another data to using it in StreamBuilder

I am trying to get all postID's of the currently logged in user from the left, and I want to use this information to get all posts which includes same postID. How can I achieve this?
So far I created a function that can pull all postID's of current user and put it in a list but I think I couldn't manage to work it.
https://i.stack.imgur.com/ThfSm.png
List<String> myFavoritePosts = [];
Future<List<String>> getFavorites() async {
final FirebaseAuth auth = FirebaseAuth.instance;
final User? user = auth.currentUser;
final userID = user!.uid;
var result = await FirebaseFirestore.instance
.collection('favorites')
.where("userID", whereIn: [userID]).get();
for (var res in result.docs) {
myFavoritePosts.add((res.data()['postID'] ?? ''));
}
return myFavoritePosts;
}
But I couldn't implement it into my StreamBuilder structure.
Widget build(BuildContext context) {
final Stream<QuerySnapshot> _postStream = FirebaseFirestore.instance
.collection('Posts')
.where('postID',
isEqualTo:
getFavorites())
.snapshots();
return StreamBuilder<QuerySnapshot>(
stream: _postStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Please try again');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return ListView(
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return ListTile(
title: Text(data['title']),
subtitle: Column(
children: <Widget>[
Text(data['details']),
],
),
);
}).toList(),
);
How can I connect those two database so I can show the posts that have the same ID?
Use This:-
`
List<String> myFavoritePosts = [];
Future<List<String>> getFavorites() async {
var favourite;
final FirebaseAuth auth = FirebaseAuth.instance;
final User? user = auth.currentUser;
final userID = user!.uid;
var result = await FirebaseFirestore.instance
.collection('favorites')
.where("userID", whereIn: [userID]).get();
for (var res in result.docs) {
myFavoritePosts.add((res.data()['postID'] ?? ''));
}
myFavoritePosts.forEach((element){
favourite = element;
});
return favourite;
}`
And then:-
'Widget build(BuildContext context) {
final Stream<QuerySnapshot> _postStream = FirebaseFirestore.instance
.collection('Posts')
.where('postID',
isEqualTo:
getFavorites())
.snapshots();
return StreamBuilder<QuerySnapshot>(
stream: _postStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Please try again');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return ListView(
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return ListTile(
title: Text(data['title']),
subtitle: Column(
children: <Widget>[
Text(data['details']),
],
),
);
}).toList(),
);'

How to display SharedPreferences value in Text widget in flutter?

The task is to get value from SharedPrefrences and display it in the Text widget.
Here I tried to store the value in the variable _userCurrency and display it in ListTile. The problem seems that the ListTile loads quickly with the value "null".
Even getUserCurrencySymbol() isn't returning any value. I have already tried
return MySharedPreferences.instance.getUserCurrencyValue('userCurrency'); and other possible solutions.
menu.dart
late String _userCurrency;
getUserCurrencySymbol().then((value) {_userCurrency = value.toString();});
return StatefulBuilder(
builder: (BuildContext context, void Function(void Function()) setState) {
return ListView(
children: [
ListTile(title: Text(_userCurrency)),
]
) //ListView
},
); //Stateful builder
controller.dart
Future<String> getUserCurrencySymbol() async {
return MySharedPreferences.instance.getUserCurrencyValue('userCurrency').then((value) {return value.toString();});
}
class MySharedPreferences {
MySharedPreferences._privateConstructor();
static final MySharedPreferences instance = MySharedPreferences._privateConstructor();
setUserCurrencyValue(String key, String value) async {
SharedPreferences instance = await SharedPreferences.getInstance();
instance.setString(key, value);
}
getUserCurrencyValue(String key) async {
SharedPreferences instance = await SharedPreferences.getInstance();
return instance.getString(key) ?? "Bitcoin";
}
You should use setState to update the ui when the data is loaded.
getUserCurrencySymbol().then((value) {
setState((){
_userCurrency = value.toString();
});
});
You can use FutureBuilder to load data and handle loading/error states
FutureBuilder<String>(
future: getUserCurrencySymbol(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if(snapshot.hasError) {
//fixme just to check an error
print("Error: ${snapshot.error}");
return Text("Error");//or what you want
}
if (!snapshot.hasData) {
return CircularProgressIndicator();//or what you want
}
return ListView(
children: [
ListTile(title: Text(snapshot.data)),
]
);
},
)
And try to change getUserCurrencySymbol();
Future<String> getUserCurrencySymbol() {
return MySharedPreferences.instance.getUserCurrencyValue('userCurrency');
}

BLoC is returning an empty snapshot. BLoC stream is from Sqflite

I'm trying to update my listview widget from BloC but it's always returning an empty snapshot.BLoC is using a stream from an async SQLite query using sqflite. The sqflite query appears to be working when accessed directly.
Here's a snippet of the UI portion:
ListBloc listBloc = ListBloc();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Expanded(child: getBoxList()),
FlatButton(
onPressed: () {
listBloc.queryBoxes();
},
child: Text('pull data'),
),
],
),
),
);
}
Widget getBoxList() {
return StreamBuilder(
stream: listBloc.boxes,
builder: (BuildContext context, AsyncSnapshot<List<Box>> snapshot) {
return getList(snapshot);
});
}
Widget getList(AsyncSnapshot<List<Box>> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Text(snapshot.data[index].boxName);
},
);
} else {
return Text('snapshot has no data');
}
}
Hers's a snippet of the BloC:
class ListBloc {
final _boxListController = StreamController<List<Box>>.broadcast();
final DatabaseHelper _db = DatabaseHelper.instance;
get boxes => _boxListController.stream;
ListBloc() {
getAllIBoxes();
}
getAllIBoxes() async {
_boxListController.sink
.add(await _db.queryAllBoxRows(DatabaseHelper.tableBoxes));
}
dispose() {
// _itemListController.close();
_boxListController.close();
}
}
_db.queryAllBoxRows(DatabaseHelper.tableBoxes) works just fine when tested outside the BLoC.
Thank you!
-- update --
here's the queryBoxes function in BLoC. I just added this to test if the query returns data.
queryBoxes() async {
DatabaseHelper _db = DatabaseHelper.instance;
final _boxes = await _db.queryAllBoxRows(DatabaseHelper.tableBoxes);
_boxes.forEach((element) {
print(element.boxName);
});
}
-- update 2 --
Source of the stream:
getAllIBoxes() async {
_boxListController.sink
.add(await _db.queryAllBoxRows(DatabaseHelper.tableBoxes));
}
is _db.queryAllBoxRows(DatabaseHelper.tableBoxes) which is a method in my DatabaseHelper that looks like this.
Future<List<Box>> queryAllBoxRows(table) async {
Database db = await instance.database;
final _result = await db.query(table);
List<Box> box = _result.map((e) => Box.fromDatabaseJson(e)).toList();
return box;
}
You are just printing the values in the queryBoxes function. You have to inform your stream that there is data available, for that you've to add the values to the sink.
Modify your function to:
queryBoxes() async {
DatabaseHelper _db = DatabaseHelper.instance;
final _boxes = await _db.queryAllBoxRows(DatabaseHelper.tableBoxes);
_boxes.forEach((element) {
print(element.boxName);
});
_boxListController.sink.add(boxes);
}

How to access data stored in Future<QuerySnapshot> in FutureBuilder

This is repository where I'm accessing data using getDocumenent() which return Future<QuerySnapshot>
Future<QuerySnapshot> fetchOrderSummery() async {
var user = await FirebaseAuth.instance.currentUser();
return firestoreInstance
.collection("placeOrder")
.document(user.uid)
.collection("subPlaceOrder")
.getDocuments();
}
This is my FutureBuilder
Future<QuerySnapshot> order = state.orderSummery; //Using bloc so data return by state.orderSummery
return FutureBuilder(
future: order,
builder: (context, snapshot) {
return Column(
children: <Widget>[
Text('${order.data.documents}'),
],
);
},
);
1.Declare a variable
2. Assign during execution
3. Use the variable in the Future
Future<QuerySnapshot> _order;
_futureSearch = fetchOrderSummery()
return FutureBuilder(
future: _order,

The argument type Future<bool> cannot be assigned to the parameter type 'bool'

Hi I'm trying to create a Visiblity widget that displays if a user is on a Firebase database Array. Looks like this (members Array):
As you can see, I have created a StreamBuilder that should return the Visibility widget if the current user's username is on this Array:
StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('chats').snapshots(),
builder: (context, snapshot){
if(!snapshot.hasData){
}else {
final chats = snapshot.data.documents;
List<ChatCard> chatCards = [];
for (var chat in chats) {
final String nameOfChat = chat.data['name'];
final String lastMessageSent = chat.data['lastMessageSent'];
final List members = chat.data['members'];
final ChatCard chatCard = ChatCard(
nameOfChat: nameOfChat ?? '',
lastMessageSent: lastMessageSent ?? '',
);
chatCards.add(chatCard);
}
return Visibility(
visible: checkIfOnAnyChats(),
child: Expanded(
child: ListView(
children: chatCards,
),
),
);
}
}
),
This is what the 'checkIfOnAnyChats()' Future looks like:
Future<bool> checkIfOnAnyChats() async {
FirebaseUser user = await _auth.currentUser();
final QuerySnapshot result = await _firestore
.collection('chats')
.where('members', arrayContains: _username)
.getDocuments();
final List<DocumentSnapshot> documents = result.documents;
if(documents.length > 0) {
setState(() {
return true;
});
}else{
setState(() {
return false;
});
}
}
I'm trying to assign this value to the 'visible' boolean but it doesn't work because it is a Future bool and not a normal bool.
Any suggestions?
Thanks.
If you want to use bool that returned from a Future, you need to use FutureBuilder;
StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('chats').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
} else {
final chats = snapshot.data.documents;
List<ChatCard> chatCards = [];
for (var chat in chats) {
final String nameOfChat = chat.data['name'];
final String lastMessageSent = chat.data['lastMessageSent'];
final List members = chat.data['members'];
final ChatCard chatCard = ChatCard(
nameOfChat: nameOfChat ?? '',
lastMessageSent: lastMessageSent ?? '',
);
chatCards.add(chatCard);
}
return FutureBuilder<bool>(
future: checkIfOnAnyChats(),
builder: (context, fSnapshot) {
if (fSnapshot.hasData)
return Visibility(
visible: fSnapshot.data,
child: Expanded(
child: ListView(
children: chatCards,
),
),
);
return Center(child: CircularProgressIndicator());
},
);
}
},
),
and you don't need to use setState inside Future functions;
Future<bool> checkIfOnAnyChats() async {
FirebaseUser user = await _auth.currentUser();
final QuerySnapshot result = await _firestore
.collection('chats')
.where('members', arrayContains: _username)
.getDocuments();
final List<DocumentSnapshot> documents = result.documents;
return documents.length > 0;
}