Flutter, hot not to call StreamBuilder when setState - flutter

My screen has an AnimatedBackground which is continuously keep looping like this
Stack(
children: [
AnimatedContainer(
duration: Duration(seconds: 2),
onEnd: () {
setState(() {
//doing some animation repeatedly
});
},
),
StreamBuilder(
//keeping rebuild because of the above setState
),
],
),
I am so stuck right now rebuild is so often, because of the setState() (the problem)
I want to use StreamBuilder inside this screen because the information is continuously changing
how not to loop StreamBuilder? how to achieve this?

Since setState({}) method updates the whole widget where you call it, you need in some way separate your widgets. The idea is that the widget where you call setState method should be lower in the widget's tree and the widget, which contains StreamBuilder is upper. As a result calling setState method won't trigger widget with StreamBuilder to rerun.

Related

flutter swiper package avoid to swipe to next position, it stay on first

I have a working code before null safety flutter upgrade. But after the migration, the same code doesn't work.
I had a simple horizontal swipe card, but now something force the swipe to stay on the first position or rebuild. When I remove didChangeDependencies (function I use to load when data change) the swipe is OK. I think when data is load by didChangeDependenciesit refresh new Swiper.children( and force to return always to first index position.
But I can't do without didChangeDependencies, how can I do ?
Here is the package https://pub.dev/packages/flutter_swiper_null_safety/example
Here is my code:
#override
void didChangeDependencies() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final MyInheritedWidgetState state = MyInheritedWidget.of(context);
}
new Swiper.children(
viewportFraction: 0.8,
scale: 0.6,
autoplay: false,
loop: false,
control: new SwiperControl(
size: 25.0,
color: Color(0xffff9a7b),
disableColor: Colors.transparent ,
padding: const EdgeInsets.all (5.0),
),
children: <Widget>[
Card1()
Card2()
Card3()
]
Usually you would need to have a controller or index saved in the state that could hold the state of the downstream widget so on rebuilds the state stayed the same.
After looking at this package it doesn't appear you can pass in an Index or Controller to the widget so it will be rebuilt any time something above it on the stack is rebuilt.
Is it possible to reorganize your page so that the swiper is not under it in the stack?

Is it OK to add a listener in the build method in a Stateful widget?

I have a scrollcontroller which I need to add a listener to do some pagination functions on when the user scrolls down on a listview.
Currently, I create the scrollcontroller and a listener in the initState. I'm doing it there, because the scroll controller is actually a PrimaryScrollController and it needs context
var _scrollController = PrimaryScrollController.of(context);
Now I've run into a problem where when my page gets rebuilt for one reason or another the listview will jump to the top.
From what I understand its because on a rebuild, everything is getting rebuild however the initState isn't being run.
SO my solution is to move the scrollcontroller creation into the build method, which seems to be working just fine. However, the listener does not work, unless I also move it into the build method.
Is this ok? Or am I creating potentially many parallel listeners which can increase each time the page gets rebuilt?
If you are looking to listen to the scroll position and do some operations based on the scroll offset you can try a builder named valueListenableBuilder
ValueListenableBuilder<int>(
builder: (BuildContext context, int value, Widget? child) {
return Row(
children: <Widget>[
Text('$value'),
child!,
],
);
},
valueListenable: scrollController.offset,
child: Container(),
)

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.

Flutter StreamBuilder how i can run setState() when ConnectionState.done?

I am using StreamBuilder to display a loading progress bar. It is possible on reaching ConnectionState.done - run setState() for updating my widgets that are outside of StreamBuilder?.
If I try to call setState() from a widget, I get an error
setState() or markNeedsBuild() called during build
case ConnectionState.done:
children = <Widget>[
Icon(
Icons.info,
color: Colors.blue,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('\$${snapshot.data} (closed)'),
)
];
//I'm trying to trigger an update
setState((){
isOffsetLoading = true;
});
break;
From thhe docs:
Streambuilder: https://api.flutter.dev/flutter/widgets/StreamBuilder-class.html
Widget that builds itself based on the latest snapshot of interaction with a Stream.
setState: https://api.flutter.dev/flutter/widgets/State/setState.html
Calling setState notifies the framework that the internal state of this object has changed in a way that might impact the user interface in this subtree, which causes the framework to schedule a build for this State object.
The error means that you are calling setState while the widget is building which will cause the widget to rebuild repeatedly.
You can update isOffsetLoading and call setState in a separate function and call the function instead in your code in the question.

Flutter: how to access context from Dismissible onDismissed

I'm trying to implement undo for a Dismissible list item in Flutter, and having problems accessing a BuildContext.
I have a flutter list, where each item is a card. The card is wrapped in a Dismissible, which allows the user to swipe to dismiss the card. Dismissible automatically removes the item from the list. Dismissible also has an onDismissed event - I'm using this event to update the item in Redux state store (setting an isDismissed flag to true), then show a snackBar which contains an UNDO button.
This is where I'm running into problems. I want the UNDO button to restore the item, by dispatching another action to the Redux store to set isDismissed to false. To do this I need a context, from which to get the store dispatcher. However when I try with the below code, I get an error when clicking on UNDO:
Looking up a deactivated widget's ancestor is unsafe
class ProductCard extends StatelessWidget {
final Product product;
const ProductCard(this.product);
#override
Widget build(BuildContext context) {
return Dismissible(
key: Key(product.id.toString()),
onDismissed: (direction) {
StoreProvider.of<AppState>(context).dispatch(DismissAction(product));
// Then show a snackbar to allow undo
Scaffold.of(context).showSnackBar(
SnackBar(
content: Row(
children: <Widget>[
Expanded(child: Text("Dismissed ${product.title}"),),
FlatButton(
onPressed: () {
// THIS IS WHERE I GET THE ERROR
StoreProvider.of<AppState>(context).dispatch(UndoDismissAction(product));
},
child: Text("UNDO"),
)
],
)
)
);
},
child: Card(
child: ...
)
);
}
}
From what I've read, I think what is going on is that the line StoreProvider.of<AppState>(context) inside the undo button's onPressed action is trying to use a context which belongs to the Card, but because the card has been removed from the list, it no longer exists.
I'm not sure how to do work around this. I've read about flutter keys, and think the answer may be to start passing around some kind of global key, but I can't quite get my head around how that works. I gave it a go and ran into another problem with 'inheritFromWidgetOfExactType' was called on null. Are keys the solution to this problem? If so where do I create the key, do I pass it in to the widget, what type of key should I use etc, or is there a better solution?
Many thanks!
Extract a single copy of the store into a local variable, which will then get captured by all the lambdas below.
#override
Widget build(BuildContext context) {
var store = StoreProvider.of<AppState>(context);
return Dismissible(
...
store.dispatch(DismissAction(product));