I've tried to to fetch data from my firebase realtime database and it worked for antoher app with the same source code. But this time i think something doesn't work with my .then function. I do not receive data for building, but then 5 seconds later there is the data in my console. I know that this is asynchronous and that normally it would work, it shloud build after the init State method. Is there sth i've missed? Thx for help :)
class _HomeState extends State<Home> {
ScrollController _scrollController = new ScrollController();
List<Map<dynamic, dynamic>> _lists = [];
Future fetchList() async {
dbRef.once().then((DataSnapshot snapshot) {
Map<dynamic, dynamic> values = snapshot.value;
print(values);
_lists.clear();
values.forEach((key, values) {
_lists.add(values);
});
print(_lists);
});
return _lists;
}
final dbRef = FirebaseDatabase.instance.reference().child("spieler");
final dbRefpreise = FirebaseDatabase.instance.reference().child("preise");
#override
void initState() {
fetchList().then((value) {
setState(() {
_lists.addAll(value);
});
});
super.initState();
}
You need to await the method but since you cant await and async in iniState youll have to do this.
#override
void initState() {
getDataList();
super.initState();
}
void getDataList() async {
await fetchList().then((value) {
setState(() {
_lists.addAll(value);
});
});
}
Future fetchList() async {
await dbRef.once()..... //some code
}
Related
i am looking to have each Showing in the list of showings upload to Firestore. As of now only the first Showing in the list of Showings uploads to Firestore and the rest of the list is printed in the console but not uploaded. I appreciate any help around this. Code is below.
ShowingList Model:
class ShowingsList extends Equatable {
final List<Showing>? showingsList;
ShowingsList({required this.showingsList});
Map<String, Object> toDocument() {
for (var showing in showingsList!) {print(showing);}
for (var showing in showingsList!) {
return {
'address': showing.address!.toString(),
'showingDate': showing.showingDate!.toString(),
'client': showing.client!.toString(),
};
}throw Exception('Something went wrong');
}
#override
List<Object> get props => [showingsList!];
}
Repository
class ShowingRepository extends BaseShowingRepository {
final FirebaseFirestore _firebaseFirestore;
ShowingRepository({
FirebaseFirestore? firebaseFirestore,
}) : _firebaseFirestore = firebaseFirestore ?? FirebaseFirestore.instance;
#override
Future<void> addShowing(ShowingsList showingsList) {
return _firebaseFirestore
.collection('showings')
.add(showingsList.toDocument());
}
}
I have a model and I want to use my services file to fill it from Firebase but I don't know how to do that ?
I am filling it with FutureBuilder that's okey. But it is exhausting me.
Here is my model:
class ReviewModel {
String? uid;
String? userID;
String? comment;
dynamic rate;
ReviewModel({
this.uid,
this.userID,
this.comment,
this.rate,
});
Map<String, dynamic> toMap() {
return {
'uid': uid,
'userID': userID,
'comment': comment,
'rate': rate,
};
}
factory ReviewModel.fromMap(Map<String, dynamic> map) {
return ReviewModel(
uid: map['uid'],
userID: map['userID'],
comment: map['comment'],
rate: map['rate'],
);
}
factory ReviewModel.fromDatabase(
DocumentSnapshot snapshot, Map<String, dynamic> map) {
return ReviewModel(
uid: snapshot['uid'],
userID: map['userID'],
comment: map['comment'],
rate: map['rate'],
);
}
}
Code is Following below,
Future<ReviewModel> getSalonReviews(String salonUID) async {
CollectionReference aRef = FirebaseFirestore.instance
.collection("salons")
.doc(salonUID)
.collection('bucket')
.doc('reviewbox')
.collection('reviews');
dynamic _doc;
var snapshot;
try {
await aRef.get().then((querySnapshot) => {
for (var dummyDoc in querySnapshot.docs)
{
_doc = dummyDoc.data(),
print(_doc),
}
});
return ReviewModel.fromMap(_doc);
} on FirebaseException catch (e) {
Get.snackbar("Hata", e.code);
rethrow;
}
}
This code is not returning my ReviewModel.
Also I am using GetX and this is my GetX code:
final Rx<ReviewModel> _reviewModel = ReviewModel().obs;
ReviewModel get reviewModel => _reviewModel.value;
set reviewModel(ReviewModel value) => _reviewModel.value;
Future fillReviewModel(String uid) async {
SalonController.instance.reviewModel =
await FakeService().getSalonReviews(uid);
}
it return me this:
And this is my Firebase docs:
How do I achive my ReviewModel with Obx. If I try it, it returns null.
You don't have to return a model you'll do something like this in your prvoider file:
List _reviews = [];
List get reviews => [..._reviews];
// IN your future void function
Future<void> myFunction () async{
myReviews = ...result of forEach;
// now update _reviews
_reviews = [...myReviews];
//And then notify listeners
notifylisteners;
}
And then in your futurebuilder
FutureBuilder(future: Provider.of<myClass>(context, listen:false).myFunction(),
builder:(context, snapshot){
// check the state like the following
if(snapshot.connectionState == ConnectionState.done){
final myValues = Provider.of<myClass>(context, listen:false).reviews;
...do something
return your_values}
if(snapshot.connectionState == ConnectionState.waiting){return progressIndicator}
})
I am using firebase dynamic links to open the email verification link in my app, but unfortunetly the link doesn't launch the app when tapped.
What I've done so far
When a new user is created, a link is sent by email to be verified :
if(firebaseUser != null && !firebaseUser.emailVerified){
await createUserInDatabaseIfNew(firebaseUser);
var actionCodeSettings = auth.ActionCodeSettings(
url: 'https://muslimcoloc.page.link/?email=${firebaseUser.email}',
dynamicLinkDomain: "muslimcoloc.page.link",
androidInstallApp: true,
androidMinimumVersion: "12",
androidPackageName: "com.app.muslim_coloc",
iOSBundleId: "com.muslim_coloc.ios",
handleCodeInApp: true,
);
await firebaseUser.sendEmailVerification(actionCodeSettings);
}
I got the dynamicLinkDomain in the firebase console :
Then, I handle the reception of the link in my main.dart file, with the firebase dynamic links package :
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MyApp(),
);
}
class MyApp extends StatelessWidget {
MyApp({Key key, }) : super(key: key);
#override
Widget build(BuildContext context) {
return AppView();
}
}
class AppView extends StatefulWidget {
const AppView({
Key key,
}) : super(key: key);
#override
_AppViewState createState() => _AppViewState();
}
class _AppViewState extends State<AppView> with WidgetsBindingObserver {
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
this.initDynamicLinks();
}
}
void initDynamicLinks() async {
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLink) async {
final Uri deepLink = dynamicLink?.link;
FirebaseAuth auth = FirebaseAuth.instance;
//Get actionCode from the dynamicLink
var actionCode = deepLink.queryParameters['oobCode'];
try {
await auth.checkActionCode(actionCode);
await auth.applyActionCode(actionCode);
// If successful, reload the user:
auth.currentUser.reload();
} on FirebaseAuthException catch (e) {
if (e.code == 'invalid-action-code') {
print('The code is invalid.');
}
}
if (deepLink != null) {
Navigator.pushNamed(context, deepLink.path);
}
},
onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
}
);
final PendingDynamicLinkData data =
await FirebaseDynamicLinks.instance.getInitialLink();
final Uri deepLink = data?.link;
if (deepLink != null) {
Navigator.pushNamed(context, deepLink.path);
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(...)
}
When I tap the link of the email, the app doesn't start nor does the browser. Here's what happens :
It tries to launch something on the browser, but then comes back to gmail.
However if I click on the link in a desktop browser, it works fine, the email is validated.
I'm having a hard time understanding what it going on. Is there something wrong about how I did things ?
You should write a function to handle your dynamic links, as per the documentation, and this is working for me in an app being used currently:
void handleDynamicLinks() async {
///To bring INTO FOREGROUND FROM DYNAMIC LINK.
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLinkData) async {
await _handleDeepLink(dynamicLinkData);
},
onError: (OnLinkErrorException e) async {
print('DynamicLink Failed: ${e.message}');
return e.message;
},
);
final PendingDynamicLinkData data =
await FirebaseDynamicLinks.instance.getInitialLink();
_handleDeepLink(data);
}
// bool _deeplink = true;
_handleDeepLink(PendingDynamicLinkData data) async {
final Uri? deeplink = data.link;
if (deeplink != null) {
print('Handling Deep Link | deepLink: $deeplink');
}
}
and in initState:
#override
void initState() {
handleDynamicLinks();
super.initState();
}
write this logic in your home page. Not in void(main..etc)
But in your first widget after that, and it should work.
Also, be sure to double check your package name, i.e com.example.yourAwesomeApp123, it's what lets the whole system know what app is to be opened when the dynamic link is pressed.
In my StatefulWidget in initState i have a method:
#override
void initState() {
super.initState();
getMyChannels();
}
getMyChannels method run a http method to get data from service and store data into database:
void getMyChannels() async {
// get data from servise and store them
_myChannel = await MyToolsProvider()
.getChannelMe("df6b88b6-f47d****");
getToolsRelToChannels(); // get data from database
setState(() {});
}
As you can see i have getToolsRelToChannels method. This method fetch data from local database. This data must be stored by await MyToolsProvider()
.getChannelMe("df6b88b6-f47d****"); method into database.
This is .getChannelMe method:
Future<ProgramsByActiveToolsModel> getChannelMe(String auth) async {
Map<String, dynamic> header = {
'Content-Type': "application/json",
"Authorization": 'Bearer $auth'
};
try {
var result = await NetworkCLient()
.getRequest(url: '$URL/api/me', header: header);
if (result != null) {
var programsByActiveToolsModel =
ProgramsByActiveToolsModel.fromJson(result);
if (programsByActiveToolsModel.responseCode == 200) {
programsByActiveToolsModel.data.forEach((item) async {
await DBProvider.db.addMyTools(item);
saveToolsbyChannelId(header, item.id);
});
return programsByActiveToolsModel;
} else
return null;
}
} catch (e) {
throw e;
}
}
In addMyTools method i store each data in one table of my database and i call saveToolsbyChannelId method for each item. This is main data that I need too.
Future<void> saveToolsbyChannelId(Map header, int channelId) async {
header["Authorization"] = 'Bearer 92122926-****';
try {
var count = await DBProvider.db.getCountToolsbyChannelId(channelId);
if (count == 0) {
var result = await NetworkCLient().getRequest(
url: '$URL/api/channel/$channelId', header: header);
if (result != null) {
var logToolsRunModel = LogTools.LogToolsRunModel.fromJson(result);
if (logToolsRunModel.responseCode == 200) {
logToolsRunModel.data.forEach((item) {
DBProvider.db.addTools(item);
});
}
}
}
} catch (e) {
throw e;
}
}
After fetching data from my service i sore these data into sqlite database .Now it's await MyToolsProvider().getChannelMe job is done!
It's time to explain getToolsRelToChannels();:
void getToolsRelToChannels() async {
_toolsRun =
await MyToolsProvider().getToolsRelatedMyChannel(_selectedChannel);
setState(() {});
}
getToolsRelatedMyChannel this method must be wait till all data in this method DBProvider.db.addTools(item) added into database and after inserting my widget must be recreated.
Future<List<ToolsByChannelIdDbModel>> getToolsRelatedMyChannel(
int channelId) async {
List<ToolsByChannelIdDbModel> list = List<ToolsByChannelIdDbModel>();
try {
var result = await DBProvider.db.getToolsById(channelId);
result.forEach((item) {
list.add(ToolsByChannelIdDbModel.fromJson(item));
});
return list;
} catch (e) {
print(e);
}
}
}
but my code is wrong because after running await MyToolsProvider().getChannelMe(***) getToolsRelToChannels method is executed and nothing is stored into database to fetching yet!!!
How could i notify my main widget after finishing database inserting???
I can not to use FutureBuilder because when run for first time, my database is empty !!!
You should await saveToolsbyChannelId in getChannelMe and await DBProvider.db.addTools(item); in saveToolsbyChannelId, otherwise you are trying to read from the database before the data has been written to it. This is assuming the rest of your code is correct, which we cannot tell for sure because there are lots of variables such as _selectedChannel that we know nothing about.
UPDATED - Check below.
What you want is to await ALL async operations. In your case
#override
void initState() async {
super.initState();
await getMyChannels();
}
and
await saveToolsbyChannelId(header, item.id);
and if DBProvider.db.addTools is asynchronous, then
await DBProvider.db.addTools(item);
UPDATE:
Since its not possible to make initState() async, you can use a callback in the future:
#override
void initState() {
super.initState();
var channelsFuture = getMyChannels();
channelsFuture.then((resp){
setState(() {});
});
}
I'd suggest that you reconsider your whole approach (from a clean architecture point of view). Take a look at the BLoC pattern. It basically boils down to this :
There's another class (called the BLoC) which handles the business logic (getting data from network and adding to the database). The widget class only handles the UI.
You kick off your asynchronous processing in the BLoC class from the widget's initState(). There's a stream listener in the widget that listens to the completion of the task by BLoC.
As soon as the BLoC completes the async task, it notifies the widget by using a stream. The stream listener in the widget knows that the data is ready and it updates the UI by calling setState();
Done !!
I have two collections in my Firestore database.
From first collection('schools'), I need to get current user school. I did it and it is Ok. But I also need to use this school and get array of his school's available courses.
This array is situated in collection('students'). And as document I use schoolNameString which I took in previous collection. So in document(schoolNameString).data['available'], available is array of swings and I need
setState(){
myarrayvar = document(schoolNameString).data['available']
}
I can not do that and always see
error /flutter (25070): Tried calling: []("available")
This is my code:
class _Coirses_PageState extends State<Coirses_Page> with TickerProviderStateMixin
{
final _firesore = Firestore.instance;
String appBarName;
String studentSchoolString = 'mal';
String aseetRoadforCorcs;
List availableCorsces = [];
#override
void initState() {
FirebaseUser user;
super.initState();
getCurrentUser();
getCouses();
try {
var studentSchool = _firesore.collection('schools');
var docSchool = studentSchool.document(loggedInUser.uid);
docSchool.get().then((documents) {
setState(() {
studentSchoolString = documents.data['schoolName'];
print(studentSchoolString);
});
print(studentSchoolString);
});
} catch (e) {
print(e);
}
tabs = [
Tab(text: 'Barlyǵy',),
Tab(text: 'Oqyp jatyrmyn',),
Tab(text: 'Aıaqtadym',)
];
pages = [Progress_Bar()];
_controller = TabController(length: tabs.length, vsync: this);
}
Future<List<dynamic>> getCouses() async {
var document = Firestore.instance.collection("students").document(studentSchoolString). get();
return await document.then((doc) {
setState(() {
availableCorsces = doc.data['available'];
});
print('dsasdasdjnasndklasndasdkasld' + doc.data['available']);
return availableCorsces;
});
}
void getCurrentUser() async {
try {
final user = await _auth.currentUser();
if (user != null) {
loggedInUser = user;
print(loggedInUser.email);
}
} catch (e) {
print(e);
}
}
}
I expected that I can use it in my UI like Text: ('available[0]' ......)
But I can't.