Flutter - Provider's value change does not update PageView's item - flutter

I have a custom widget with a PageView in it's build().
PageView pageview = PageView.builder(
...
itemCount : pages.length,
itemBuilder : (context, index) => pages[index]
);
The PageView's item built with a function with the StatefulWidget's context passed to it.
pages.add(Builder(builder : (BuildContext context) => build(this.context)));
And the function read Provider's value with given context.
(BuildContext context){
...
bool v = context.watch<Provider>().value();
...
}
The problem is Provider's notifyListeners with the same context as above does not trigger PageView's item update.
I assumed that the function call was passed so it shouldn't cause this issue. But it looks like the update need to run it again but it's impossible.
I just want the provider value change notifying to call the function again if necessary, to update the targeted widget. But as for now, the function only being called once during the page swipe.

Related

Flutter, reload an item in List View

This should be common but I can't find much information about this. I have a lazy loading list with Cards to show data for each record fetched from http request. How do I refresh the data of a single record when a button or action in a Card is being clicked or done?
Structure is something like:
Stateful Widget
loadData() //http request by page loading
Widget build
TextButton onPressed(){loadData}
ListView.builder(
itemCount: fullList.length,
itemBuilder: (BuildContext context, int index) {
return dataCard(context, fullList, index);
}),
dataCard(context, dl, i)
Text(dl[i].Name)
Text(dl[i].Price)
setIcon(dl[i].type)
...
setIcon(type)
switch type
case 1
return Icon A
case 2
return Icon B
...
How can I detect action in dataCard or setIcon and reload the corresponding item in the list?
By adding a function in stateful class, update the data list by index, then setState().
void refreshRecord(index) async{
dataList[index].name = new value
setState()
}

How to Stop the flutter rebuilds from ValueListenableBuilder?

I'm having an issue of the widget subtree constantly rebuilding from inside the ValueListenableBuilder. It is supposed to run a rebuild on change, and in this case it is listening to a table on a Flutter Hive Database.
Things I've tired:
I had all of my Hive Boxes open in the main method, so that I have access to each box from anywhere in the app. I tired only opening the Hive box when something is changed, then promptly closing this box. Didn't work
Things I think it could be, but not sure:
Mixing ChangeNotifierProvider with the ValueListenableBuilder - Because some of the subtree also utilizes changenotifier, but with ValueListenableBuilder constantly rebuilding the subtree, any changes I pass into the provider get wiped out.
Is there anyway of only rebuilding on a change only?
#override
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable:
Hive.box<Manifest>(HiveTables.manifestBox).listenable(),
child: assignmentWidgets,
builder: (context, Box<Manifest> manifestBox, child) {
if (manifestBox.isNotEmpty)
return child!;
},
);
}
the Hive provides listening to a whole Box, so every time something happens inside that Box, the builder will be called :
ValueListenableBuilder<Box>(
valueListenable: Hive.box('settings').listenable(),
builder: (context, box, widget) {
// build widget
},
),
but you can also specify a List<String> of keys that you want only them to be listenable by the ValueListenableBuilder with the keys property like this:
ValueListenableBuilder<Box>(
valueListenable: Hive.box('settings').listenable(keys: ['firstKey', 'secondKey']),
builder: (context, box, widget) {
// build widget
},
),
now for keys other than firstKey and secondKey, every operation that's executed over them will not update the ValueListenableWidget, but operation on firstKey and secondKey will update it only.

How to refresh ListView after Sorting in Flutter?

I have a ListView which contains my customers. This ListView currently is a Stateless Widget. After sorting the list of customers inside the parent, I need to refresh the list.
parent where the List is displayed (Parent is stateful):
MyLieferListe(
bestellungen.anzahlPositionen,
bestellungen.kunde,
notifyParent: refresh,
),
MyLieferListe (currently Stateless):
SingleChildScrollView(
child: ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
getBeleg();
this.notifyParent(kunden[index]);
},
child: MyBestellungText(
kunden[index].kundenbezeichnung,
kunden[index].kundenNr,
"${kunden[index].strasse}, ${kunden[index].plz} ${kunden[index].ort}",
kunden[index].tourHinweis),
);
},
itemCount: kunden.length,
),
),
How to refresh the ListView of the Child which is displayed inside of the parent?
Firstly you have to make your list stateful because on changing the list. You have to call setState.
A bad solution would be to use Navigator.pushReplacement.
A good solution would be to make your list a stateful widget.
A state change triggers a rebuild in Flutter, so in order to ensure your widget rebuilds after you sort the list, you should call setState
// a state variable
List<Kunde> kunden;
onSortButtonClick() {
List<Kunde> newKunden = kunden.sort(...);
// store to the state
setState((){
kunden = newKunden;
});
}
build(WidgetContext context) {
return ListView.builder(/* use kunden, from state, in here */)
}
As you mention, your parent is already stateful. You should move bestellungen.kunde to the state variables (set it first in initState and use setState every time you change it), then it should automatically rebuild widget if you update kunden via setState.
Alternatively, you could make the child widget stateful and put kunden in the state of that widget. Theoretically, that should be slightly better since you then only rebuild the child, not the parent, but it realistically shouldn't make much difference in this case.

Dispatch first bloc event or cubit method, on page start inside StatelessWidget

I have 10 buttons in main menu of my app and each of them contains BlocBuilder inside them.
So when I click on those buttons to open a new page, I want to dispatch the first event, but I don't know how. I can change all classes to stateful widget and then call bloc.dispatch(event) inside initialState() function, but I would like to discover another way, and not sure whether it's the best way
In order to trigger the first event/method call inside BlocBuilder I had to add bloc argument and give parameter provided my BlocProvider, only after this I managed to call my method.
BlocBuilder<MyCubit, MyState>(
bloc: BlocProvider.of<MyCubit>(context)..myFunction(),
builder: (BuildContext context, state) {
//Your code...
}
you can use .. operator to add event while declaring like
BlocProvider(
create: (context) => FirstBloc()..add(InitialiEvent()), // <-- first event,
child: BlocBuilder<FirstBloc, FirstState>(
builder: (BuildContext context, state) {
...
},
),
or you can do it inside the initState method as well

Flutter/Dart - Edit Page in PageView, then Refresh and Scroll back to Same Place?

Currently, I can submit edits to a single page in a PageView and then either Navigator.push to a newly created single edited page or Navigator.pop back to the original Pageview containing the unedited page.
But I'd prefer to pop back to the the same place in an updated/refreshed Pageview. I was thinking I could do this on the original PageView page:
Navigator.pushReplacement(context,new MaterialPageRoute(
builder: (BuildContext context) => EditPage()),);
But after editing, how can I pop back to a refreshed PageView which is scrolled to the now updated original page? Or is there a better way? Someone mentioned keys, but I've not yet learned to use them.
The question deals with the concept of Reactive App-State. The correct way to handle this is through having an app state management solution like Bloc or Redux.
Explanation: The app state takes care of the data which you are editing. the EditPage just tells the store(App-State container) to edit that data and the framework takes care of the data that should be updated in the PageView.
as a temporary solution you can use an async call to Navigation.push() and refresh the PageView State once the EditPage comes back. you can also use an overloaded version of pop() to return a success condition which aids for a conditional setState().
Do you know that Navigator.pushReplacement(...) returns a Future<T> which completes when you finally return to original context ?
So how are you going to utilize this fact ?
Lets say you want to update a String of the original page :
String itWillBeUpdated="old value";
#override
Widget build(BuildContext ctx)
{
.
.
.
onPressesed:() async {
itWillBeUpdated= await Navigator.pushReplacement(context,new MaterialPageRoute(
builder: (BuildContext context) => EditPage()),);
setState((){});
},
}
On your editing page , you can define Navigator.pop(...) like this :
Navigator.pop<String>(context, "new string");
by doing this , you can provide any data back to the original page and by calling setState((){}) , your page will reflect the changes
This isn't ideal, but works somewhat. First I created a provider class and added the following;
class AudioWidgetProvider with ChangeNotifier {
int refreshIndex;
setRefreshIndex (ri) {
refreshIndex = ri;
return refreshIndex;
}
}
Then in my PageView Builder on the first page, I did this;
Widget build(context) {
var audioWidgetProvider = Provider.of<AudioWidgetProvider>(context);
return
PreloadPageView.builder(
controller: PreloadPageController(initialPage: audioWidgetProvider.refreshIndex?? 0),
Then to get to the EditPage (2nd screen) I did this;
onPressed: () async {
audioWidgetProvider.setRefreshIndex(currentIndex);
Navigator.pushReplacement(context,new MaterialPageRoute(builder: (BuildContext context) => EditPage()),); }
And finally I did this to return to a reloaded PageView scrolled to the edited page;
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) =>HomePage()));
The only problem now is that the PageView list comes from a PHP/Mysql query and I'm not sure what to do if new items are added to the list from the Mysql database. This means the currentIndex will be wrong. But I guess that's the topic of another question.