Table_calendar not show event indicator and event when data load - flutter

I have load data in init state but the event indicator does not show and when I click on other date and come bak data gone.
Here is my code file

Since initState is not async, you normally wouldn't add code to it that executes asynchronously (like a fetch of data). Instead, I would suggest using a FutureBuilder (or StreamBuilder.periodic if you want to keep fetching your data periodically).
Say you have the function
Future<List<Event>> fetchEvents() async {
...
return events;
}
Which fetches the events from your local or online database and returns them as a list. You could then wrap your widget in a FutureBuilder:
FutureBuilder<List<Event>>(
future: fetchEvents,
builder: (BuildContext context, AsyncSnapshot<List<Event>> snapshot) {
List<Event> events = snapshot.data ?? [];
return MyAwesomeCalendarWidget(events: events);
}
);
This way, the Widget will build first with the empty List (no events fetched yet), showing you an empty calendar, and once the data is fetched it'll rebuild accordingly. You would no longer need to use a StatefulWidget in this case.

eventLoader: (day) {
for (var i = 0; i < listtasks.length; i++) {
if (day == listtasks[i].date) {
return [listtasks[i]];
}
}
return [];
},

Related

How to update the initial state on Flutter

I'm building a flutter page that shows to the users a list of the credit cards that are stored on the back-end and lets the user delete already existing cards.
To fetch the cards from the back-end I'm using initState(). Note that controller.getCreditCards() returns a Future<List<CreditCardSummary>>:
#override
void initState() {
super.initState();
_futureCreditCards = controller.getCreditCards();
}
This List is then rendered using a FutureBuilder, just like the documentation recommends:
Widget build(BuildContext context) {
return FutureBuilder<List<CreditCardSummary>>(
future: _futureCreditCards,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Scaffold(
// My page that renders all the cards goes here
// Inside this future builder I can access the cards list with snapshot.data
);
} else if (snapshot.hasError) {
return Text('ERROR');
}
// Show a loading screen while the data is beeing fetched:
return const CircularProgressIndicator();
});
}
This is all working fine, the problem only begins when I need to update this data. For example, when the user deletes a creditCard, I want to delete the card from the List and to re-render the page with the new version of the List, but I don't know a good way of doing that.
In order to get updated data/ refresh the FutureBuilder, you need to reassign the future variable.
For your case, when ever you like to update,
_futureCreditCards = controller.getCreditCards();
setState((){});

Firestore query returns only one document

I have an app where admin can delete all documents in the firebase collection and add an x number of new documents, this works beautifully, but my streambuilder isn't updating properly,
the stream builder is getting back only one document everytime you delete all documents and create new ones, it only returns one, and like when you leave the app and come back, it fetches the proper amount of documents, all I can find online is that it's wrong to use a loop when querying and I've removed my for loop and am now using the map method, still, it is the same, here is my stream builder code
StreamBuilder<QuerySnapshot>(
stream: _store.collection("picks").snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<PickCard> pickCards = [];
final documentSnapshots = snapshot.data!.docs;
debugPrint(documentSnapshots.length.toString());
if (documentSnapshots.isNotEmpty) {
documentSnapshots.map((e) {
pickCards.add(
PickCard(
pickerPosition: e["pickerPosition"],
pickerName: e["pickerName"],
isPicked: e["isPicked"],
pickerEmail: e["pickerEmail"],
),);
}).toList();
dHelp.setCards(
context,
pickCards,
);
dHelp.setContributors(context, documentSnapshots.length);
}
} else {
}
the print document snapshot length is always 1 when they get created, but after refresh, the actual length updates, but in the firebase console, everything works perfectly, the documents update effectively,
here is a video of the problem https://www.dropbox.com/s/25qqnh0ttgemgf1/2022-08-16%2010-26-46.mp4?dl=0
I found that passing the stream directly to the streamBuilder was causing the stream to restart each time the build method rebuilt, which was supposed to be whenever the stream returns new data, so, it was kinda knotting over itself,
I instantiated the stream in the state then passed it to the streamBuilder, so now it's only created once in the lifetime of the page
// created this variable
late Stream<QuerySnapshot> _stream;
#override
initState() {
// gave it a value in iniState
_stream = _store.collection("picks").snapshots();
super.initState();
}
StreamBuilder<QuerySnapshot>(
stream: _stream, // then added this here
builder: (context, snapshot) {
if (snapshot.hasData) {
List<PickCard> pickCards = [];
final documentSnapshots = snapshot.data!.docs;
debugPrint(documentSnapshots.length.toString());
if (documentSnapshots.isNotEmpty) {
documentSnapshots.map((e) {
pickCards.add(
PickCard(
pickerPosition: e["pickerPosition"],
pickerName: e["pickerName"],
isPicked: e["isPicked"],
pickerEmail: e["pickerEmail"],
),);
}).toList();
dHelp.setCards(
context,
pickCards,
);
dHelp.setContributors(context, documentSnapshots.length);
}
} else {
}

Update list item periodically using stream with Flutter

I'm writing an app that communicates with a server. The app will have a listview with items inside that need to be updated periodically (every x seconds) and I'm trying to figure out the best way to accomplish this.
Let's say I have a Stream that sends a request to a server every 5 seconds. I yield the result, but how can I receive this data inside of a view and update it?
for example:
Stream:
Stream<double> progress(int id) async* {
while (true) {
await Future.delayed(const Duration(seconds: 5));
double progress = await api.getProgressFor(id: id);
yield progress;
}
}
How could I create a widget, say a LinearProgressIndicator that will listen for yields from this stream and update when they are sent.
The best way is to use a StreamBuilder. Here is a sample showing where you call your stream and where you display your ListView or similar.
#override Widget build(BuildContext context) {
return StreamBuilder <int>(
stream: callProgressStream ,
builder: (context, snapshot) {
if (!snapshot.hasData) return CircularProgressIndicator();
else {
// Your code here
return ListView();}
});
}
Let me know if this does not help.

How to set local state with flutter bloc

I have a Flutter app that uses Bloc pattern for state management.
I have a local variable in a page that stores state.
PageA:
// Outside build function
List<String> _names = []; // local variable to show the changes visually
.
.
.
// Inside build function
return Scaffold(
body: BlocConsumer<PageABloc, PageAState>(
builder: (context, state) {
if (state is PageASuccessState) {
_names = state.names; // here is problem
return _body();
} else if (state is PageALoadingState ||
state is PageAInitState) {
return Center(child: CircularProgressIndicator());
} else
return Center(child: Text("Something went wrong"));
},
listener: (context, state) {},
),
);
I need to only update state of state.names when user clicks on a save button that is in _body(), but to show the visual changes to user, I'm using _names local variable.
How to load the initial values of state.names to _names?
The code that I tried doesn't work as it resets any changes to _names(local) on every frame.
I tried something like,
if (state is PageASuccessState) {
final List<String> _n = state.names;
_names.addAll(_n);
return _body(_width);
}
But this just adds state.names repeatedly to _names, infinite number of times.
Help!
This is happening because your state is containing the names list every time builder of BlocBuilder is called.
You should replace List<String> _names = [] with LinkedHashSet<String> _names = LinkedHashSet<String>();
LinkedHashSet will work exactly like list but it won't allow duplicates. You can change it to list by calling
_names.iterator.toList(); anytime you want.

How to inform FutureBuilder that database was updated?

I have a group profile page, where a user can change the description of a group. He clicks on the description, gets on a new screen and saves it to Firestore. He then get's back via Navigator.pop(context) to the group profile page which lists all elements via FutureBuilder.
First, I had the database request for my FutureBuilder inside the main build method (directly inside future builder 'future: request') which was working but I learnt it is wrong. But now I have to wait for a rebuild to see changes. How do I tell FutureBuilder that there is a data update?
I am loading Firestore data as follows within the group profile page:
Future<DocumentSnapshot> _future;
#override
void initState() {
super.initState();
_getFiretoreData();
}
Future<void> _getFiretoreData() async{
setState(() {
this._future = Firestore.instance
.collection('users')
.document(globals.userId.toString())
.get();});
}
The FutureBuilder is inside the main build method and gets the 'already loaded' future like this:
FutureBuilder(future: _future, ...)
Now I would like to tell him: a change happened to _future, please rebuild ;-).
Ok, I managed it like this (which took me only a few lines of code). Leave the code as it is and get a true callback from the navigator to know that there was a change on the second page:
// check if second page callback is true
bool _changed = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ProfileUpdate(userId: globals.userId.toString())),
);
// if it's true, reload future data
_changed ? _getFiretoreData() : Container();
On the second page give the save button a Navigator.pop(context, true).
i would advice you not to use future builder in this situation and use future.then() in an async function and after you get your data update the build without using future builder..!
Future getData() async {
//here you can call the function and handle the output(return value) as result
getFiretoreData().then((result) {
// print(result);
setState(() {
//handle your result here.
//update build here.
});
});
}
How about this?
#override
Widget build(BuildContext context) {
if (_future == null) {
// show loading indicator while waiting for data
return Center(child: CircularProgressIndicator());
} else {
return YourWidget();
}
}
You do not need to set any state. You just need to return your collection of users in your GetFirestoreData method.
Future<TypeYouReturning> _getFirestoreData() async{
return Firestore.instance
.collection('users')
.document(globals.userId.toString())
.get();
}
Inside your FutureBuilder widget you can set it up something like Theo recommended, I would do something like this
return FutureBuilder(
future: _getFirestoreData(),
builder: (context, AsyncSnapshot<TypeYouReturning> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else {
if (snapshot.data.length == 0)
return Text("No available data just yet");
return Container();//This should be the desire widget you want the user to see
}
},
);
Why don't you use Stream builder instead of Future builder?
StreamBuilder(stream: _future, ...)
You can change the variable name to _stream for clarity.