I am building an app which has local database and I keep some infos about movies in that database. There is a screen called "Favorites" where I get the movies from local database and put them in a listview:
And if I click any movie from that list, I will be directed to a new Screen where I can see movie details. In this screen, there is a button which removes the movie from local database.
Now my problem starts here: After I remove any movie from database and go back to "Favorites" Screen, I still see that movie in ListView. But I have already removed that movie from database. If I was doin this on Android platform, I would override onStart method and make a query to database, then update the listview. But in Flutter as long as I know, there is no method as onStart. So, what should I do?
class FavoritesScreen extends StatefulWidget {
#override
_FavoritesScreenState createState() => _FavoritesScreenState();
}
class _FavoritesScreenState extends State<FavoritesScreen> {
var favoriteMovieDatabase = FavoriteMovieDatabase();
List<MovieOverview> movieOverviewList = List<MovieOverview>();
#override
void initState() {
super.initState();
favoriteMovieDatabase
.getAllFavoriteMovies()
.then((List<MovieOverview> list) {
setState(() {
movieOverviewList = list;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Favorites")),
body: MovieOverviewListView(movieOverviewList, "FAVORITE")
);
}
}
EDIT:
I have tried to use WidgetsBindingObserver but it did not help.
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if(state == AppLifecycleState.resumed){
favoriteMovieDatabase
.getAllFavoriteMovies()
.then((List<MovieOverview> list) {
setState(() {
movieOverviewList = list;
});
});
}
}
It did not even call this method.
As a simple solution, you can await the result of a navigator push:
goToDetailPage(BuildContext context) async {
await Navigator.of(context).pushNamed('movieDetail');
// this is reached when the route is popped
reloadData();
}
To make the code even more efficient, you can return a result from your detail route that signifies if the movie was deleted:
Navigator.pop(context, 'MOVIE_DELETED');
and
goToDetailPage(BuildContext context) async {
final result = await Navigator.of(context).pushNamed<String>('movieDetail');
if(result == 'MOVIE_DELETED') {
reloadData();
}
}
In the long run, when there are more ways to change the database content (think of a semitransparent dialog where the movie list is still visible in background, or live updates), it would be better to move to some kind of reactive architecture similar to Room on Android.
Assuming your problem happens when you use Navigator to push a new route, then you can use RouteAware mixin combined with RouteObserver and then overrides didPopNext method.
didPopNext is called whenever a route becomes visible again.
final RouteObserver<PageRoute> routeObserver = new RouteObserver<PageRoute>();
void main() {
runApp(new MaterialApp(
home: new Container(),
navigatorObservers: [routeObserver],
));
}
class RouteAwareWidget extends StatefulWidget {
State<RouteAwareWidget> createState() => new RouteAwareWidgetState();
}
// Implement RouteAware in a widget's state and subscribe it to the RouteObserver.
class RouteAwareWidgetState extends State<RouteAwareWidget> with RouteAware {
#override
void didChangeDependencies() {
super.didChangeDependencies();
routeObserver.subscribe(this, ModalRoute.of(context));
}
#override
void dispose() {
routeObserver.unsubscribe(this);
super.dispose();
}
#override
void didPopNext() {
// TODO: query DB again
}
#override
Widget build(BuildContext context) => new Container();
}
You can achieve this with the initState:
#override
void initState() {
_doWhatYouWantToDo().then((value){
print('Async done');
});
super.initState();
}
add these code when you want to run method on pop
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ConversationScreen() ,
)).then((value) {
//add your method which you want to run on pop!! cheers
setState(() {});
});
},
Related
I was Learning Async and Future Functions in Dart but I got confused because I still not get it like how print("object") is compiled before getData() function because traditionally next Line is read by compiler once the before line or Function is fully compiled/Executed . If I am making a Mistake Please correct me out , I am noob tbh
import 'package:flutter/material.dart';
class Loading extends StatefulWidget {
const Loading({super.key});
#override
State\<Loading\> createState() =\> \_LoadingState();
}
class \_LoadingState extends State\<Loading\> {
void getdata() async {
String parth = await Future.delayed(Duration(seconds: 3), () {
return 'parth';
});
print('Hey');
print(parth);
}
#override
int count = 0;
void initState() {
// TODO: implement initState
super.initState();
getdata();
print('object');
}
#override
Widget build(BuildContext context) {
// print(' Setstae vala + $count');
return Scaffold(
appBar: AppBar(
title: Text('Loading'),
),
body: ElevatedButton(
onPressed: () {
setState(() {
count++;
});
},
child: Text('$count')),
);
}
}
Your output should be like this:
object
Hey
parth
because traditionally next Line is read by compiler once the before
line or Function is fully compiled/Executed
Normaly, yes. But since you are using the async keyword in this case, it works a little differently.
What happens here:
You call getData first in your initState. In getData you have a future delayed in it, which you wait for with keywoard await. So it waits 3 seconds until you return 'parth' as a string. At the same time, however, it continues to run in your initState to return 'object'.
getdata is a async method, you need to return Future<void> to await.
Now the initState() cant be async, you can create another method to await and place it here.
void newMethod() async {
await getdata();
print('object');
}
And place newMethod() inside initState.
Or you can use .then
getdata().then((_) {
print('object');
});
In android if an activity is visible onResume is called. What is the equivalent method of onResume in Flutter?
I need the know when my widget screen is visible so I can auto-play a video based on that. I might go to another widget screen an when I come back it should auto-play.
My approach was to play the video in didUpdateWidget but didUpdateWidget is called every-time even the widget screen is not visible.
Note: I'm not asking about didChangeAppLifecycleState from WidgetsBindingObserver as it gives onResume etc callbacks for the app lifecycle not a particular widget screen.
All of the problems are solved.
Put an observer on the navigator from the root of the widget tree (materialappwidget).
If you need more explanation please follow this link:
https://api.flutter.dev/flutter/widgets/RouteObserver-class.html
I have implemented in my project and its working great #Sp4Rx
// Register the RouteObserver as a navigation observer.
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
void main() {
runApp(MaterialApp(
home: Container(),
navigatorObservers: [routeObserver],
));
}
class RouteAwareWidget extends StatefulWidget {
State<RouteAwareWidget> createState() => RouteAwareWidgetState();
}
// Implement RouteAware in a widget's state and subscribe it to
// the
// RouteObserver.
class RouteAwareWidgetState extends State<RouteAwareWidget> with RouteAware {
#override
void didChangeDependencies() {
super.didChangeDependencies();
routeObserver.subscribe(this, ModalRoute.of(context));
}
#override
void dispose() {
routeObserver.unsubscribe(this);
super.dispose();
}
#override
void didPush() {
// Route was pushed onto navigator and is now topmost route.
}
#override
void didPopNext() {
// Covering route was popped off the navigator.
}
#override
Widget build(BuildContext context) => Container();
}
I struggled to get a video to pause when not viewing the main screen of my app. I applied this VisibilityDetector and grabbed the visiblePercentage to force a pause or resume:
VisibilityDetector(
key: Key('visible-video--key-${this.randomkeygenerator}-1'),
onVisibilityChanged: (visibilityInfo) {
var visiblePercentage = visibilityInfo.visibleFraction * 100;
if (visiblePercentage < 1){ //the magic is done here
if(_video_controller != null) {
if(disposed_vid == false) {
_video_controller.pause();
}
}
}else{
if(_video_controller != null) {
if(disposed_vid == false) {
_video_controller.play();
}
}
}
debugPrint(
'Widget ${visibilityInfo.key} is ${visiblePercentage}% visible');
},
child: VideoPlayer(_video_controller)),
#override
void dispose() {
// If the video is playing, pause it.
_video_controller .pause();
_video_controller .dispose();
disposed_vid = true;
super.dispose();
}
Because the animation of the background route will be disabled. So we can judge whether it is in the foreground in this way:
final isForeground = TickerMode.of(context);
Wrap it into a widget:
/// Created by ipcjs on 2021/3/23.
class ForegroundDetector extends StatefulWidget {
const ForegroundDetector({
Key? key,
required this.child,
required this.onForegroundChanged,
}) : super(key: key);
final ValueChanged<bool> onForegroundChanged;
final Widget child;
#override
ForegroundDetectorState createState() => ForegroundDetectorState();
}
class ForegroundDetectorState extends State<ForegroundDetector> {
bool get isForeground => _isForeground ?? false;
bool? _isForeground;
#override
Widget build(BuildContext context) {
final isForeground = TickerMode.of(context);
if (_isForeground != isForeground) {
_isForeground = isForeground;
widget.onForegroundChanged(isForeground);
}
return widget.child;
}
}
None of these existing questions exactly answered the question for me, so I wrote up a more thorough answer here which talks about how to get all the same lifecycle methods as iOS and Android.
But the gist: I recommend using the FocusDetector package. It works exactly like onResume and onPause. It would be implemented as follows.
class PageState extends State<Page> {
void onResume() {
log("onResume / viewWillAppear / onFocusGained");
}
void onPause() {
log("onPause / viewWillDisappear / onFocusLost");
}
#override
Widget build(BuildContext context) {
return FocusDetector(
onFocusGained: onResume,
onFocusLost: onPause,
child: Text('Rest of my widget'),
);
}
}
It's probably not the simplest and definitely not perfect, but a while back I implemented events like those with routes. Basically, EventRoute<T> is a drop-in replacement for MaterialPageRoute<T> that provides optional callbacks for when the Widget is created, pushed to the foreground, pushed to the background and when it gets popped off.
event_route.dart:
import 'package:flutter/material.dart';
enum RouteState {
none,
created,
foreground,
background,
destroyed
}
class EventRoute<T> extends MaterialPageRoute<T> {
BuildContext _context;
RouteState _state;
Function(BuildContext) _onCreateCallback;
Function(BuildContext) _onForegroundCallback;
Function(BuildContext) _onBackgroundCallback;
Function(BuildContext) _onDestroyCallback;
EventRoute(BuildContext context, {
builder,
RouteSettings settings,
bool maintainState = true,
bool fullscreenDialog = false,
Function(BuildContext) onCreate,
Function(BuildContext) onForeground,
Function(BuildContext) onBackground,
Function(BuildContext) onDestroy
}):
_context = context,
_onCreateCallback = onCreate,
_onForegroundCallback = onForeground,
_onBackgroundCallback = onBackground,
_onDestroyCallback = onDestroy,
_state = RouteState.none,
super(builder: builder, settings: settings, maintainState: maintainState, fullscreenDialog: fullscreenDialog);
void get state => _state;
#override
void didChangeNext(Route nextRoute) {
if (nextRoute == null) {
_onForeground();
} else {
_onBackground();
}
super.didChangeNext(nextRoute);
}
#override
bool didPop(T result) {
_onDestroy();
return super.didPop(result);
}
#override
void didPopNext(Route nextRoute) {
_onForeground();
super.didPopNext(nextRoute);
}
#override
TickerFuture didPush() {
_onCreate();
return super.didPush();
}
#override
void didReplace(Route oldRoute) {
_onForeground();
super.didReplace(oldRoute);
}
void _onCreate() {
if (_state != RouteState.none || _onCreateCallback == null) {
return;
}
_onCreateCallback(_context);
}
void _onForeground() {
if (_state == RouteState.foreground) {
return;
}
_state = RouteState.foreground;
if (_onForegroundCallback != null) {
_onForegroundCallback(_context);
}
}
void _onBackground() {
if (_state == RouteState.background) {
return;
}
_state = RouteState.background;
if (_onBackgroundCallback != null) {
_onBackgroundCallback(_context);
}
}
void _onDestroy() {
if (_state == RouteState.destroyed || _onDestroyCallback == null) {
return;
}
_onDestroyCallback(_context);
}
}
And then to push your route you do:
Navigator.push(context, EventRoute(context, builder: (context) => YourWidget(context),
onCreate: (context) => print('create'),
onForeground: (context) => print('foreground'),
onBackground: (context) => print('background'),
onDestroy: (context) => print('destroy')
));
The context is a little icky though...
I'm using flutter video_player(https://pub.dartlang.org/packages/video_player) plugin to play videos. But When I navigate from one page to another in flutter the video is still playing. I'm having the same problem when playing music from any other app(eg: playing music from notification while flutter video is playing). How can I pause it?
Edit:
Second option:
So I came up with another solution. VisibilityDetector
is a `Widget' that can wrap any other Widget and notify when the visible area of the widget changed.
VisibilityDetector(
key: Key("unique key"),
onVisibilityChanged: (VisibilityInfo info) {
debugPrint("${info.visibleFraction} of my widget is visible");
if(info.visibleFraction == 0){
videoPlayerController.pause();
}
else{
videoPlayerController.play();
}
},
child: VideoPlayer());
)
First Option:
By using the RouteObserver and RouteAware your widget will be informed whenever another screen is pushed on top of it, or it's popped back on top.
First, you need to add a RouteObserver to MaterialApp or your Navigator.
RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
navigatorObservers: [routeObserver], //HERE
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
Then you must add the RouteAware mixin to your widget, and subscribe it to the RouteObserver.
class VideoPlayerItem extends StatefulWidget {
final File file;
VideoPlayerItem(this.file);
#override
_VideoPlayerItemState createState() => _VideoPlayerItemState();
}
class _VideoPlayerItemState extends State<VideoPlayerItem> with RouteAware {
VideoPlayerController _videoPlayerController;
#override
void initState() {
super.initState();
_videoPlayerController = VideoPlayerController.file(widget.file);
_videoPlayerController.initialize().then((_) {
if (mounted) {
setState(() {});
_videoPlayerController.play();
}
});
}
#override
void didChangeDependencies() {
routeObserver.subscribe(this, ModalRoute.of(context));//Subscribe it here
super.didChangeDependencies();
}
/// Called when the current route has been popped off.
#override
void didPop() {
print("didPop");
super.didPop();
}
/// Called when the top route has been popped off, and the current route
/// shows up.
#override
void didPopNext() {
print("didPopNext");
_videoPlayerController.play();
super.didPopNext();
}
/// Called when the current route has been pushed.
#override
void didPush() {
print("didPush");
super.didPush();
}
/// Called when a new route has been pushed, and the current route is no
/// longer visible.
#override
void didPushNext() {
print("didPushNext");
_videoPlayerController.pause();
super.didPushNext();
}
#override
Widget build(BuildContext context) {
return _videoPlayerController?.value?.initialized ?? false
? AspectRatio(
aspectRatio: _videoPlayerController.value.aspectRatio,
child: VideoPlayer(_videoPlayerController),
)
: Container();
}
#override
void dispose() {
routeObserver.unsubscribe(this); //Don't forget to unsubscribe it!!!!!!
super.dispose();
_videoPlayerController.dispose();
}
}
You have - VideoPlayerController _controller;
Before Navigating to other screen -
_controller.pause(); // add this
Navigator.push(
context, MaterialPageRoute(builder: (BuildContext context) => NextPage()));
OR
if it's a Stateful widget then you can override - dispose() method.
#override
void dispose() {
super.dispose();
_controller.pause();
}
Simple and its work for me, I get this from chewie example code: https://pub.dev/packages/chewie/example
Just on the onTap/onPressed use setState and in this setState make videoController.pause or dispose, whatever you want.
onTap:() {
setState(() {
widget.videoController.pause();
Navigator.of(context)
.pushNamed(YOUR-NAVIGATION-PAGE-NAME);
});
},
I wrote a checks for something and then navigates to the next page if the condition is true. However i keep getting an error whenever that condition returns true and i'm meant to navigate
class BuyTickets extends StatefulWidget {
#override
_BuyTicketsState createState() => new _BuyTicketsState();
}
class _BuyTicketsState extends State<BuyTickets> {
#override
void initState(){
...
if(condition){
//Skip to next page
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => SelectSeat(data: widget.data)
)
);
}
super.initState();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
...
)
}
Error:
please how do i fix?
It complains that it can't find the parent because the render object associated to the widget is not fully created and mounted.
In all these cases you need to delay the calls to the moment the render object is mounted. One way is executing it after the first frame.
#override
void initState() {
super.initState();
if(condition){
WidgetsBinding.instance.addPostFrameCallback((_) async {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => SelectSeat(data: widget.data)
)
);
});
}
}
You cannot navigate during lifecycle functions like initState() or build(). You could put your login in an async function or use Future.delayed or something similar.
#override
void initState(){
super.initState();
...
if(condition){
skipToNextPage();
}
}
void skipToNextPage() async {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => SelectSeat(data: widget.data)
)
);
}
I had written a comment long ago in which I recommended a delay of one second to start operations, it's actually wrong. What we need to do is initialize the variables and for any operation dependent on another, we must wait for the graphics to be created by simply entering:
#override
void initState() {
super.initState();
//here you can init your variables
WidgetsBinding.instance.addPostFrameCallback((_) async {
//All dynamic operations that will impact on graphics
});
}
}
Basically I am trying to make an app whose content will be updated with an async function that takes information from a website, but when I do try to set the new state, it doesn't reload the new content. If I debug the app, it shows that the current content is the new one, but after "rebuilding" the whole widget, it doesn't show the new info.
Edit: loadData ( ) method, basically read a URL with http package, the URL contains a JSON file whose content changes every 5 minutes with new news. For example a .json file with sports real-time scoreboards whose scores are always changing, so the content should always change with new results.
class mainWidget extends StatefulWidget
{
State<StatefulWidget> createState() => new mainWidgetState();
}
class mainWidgetState extends State<mainWidget>
{
List<Widget> _data;
Timer timer;
Widget build(BuildContext context) {
return new ListView(
children: _data);
}
#override
void initState() {
super.initState();
timer = new Timer.periodic(new Duration(seconds: 2), (Timer timer) async {
String s = await loadData();
this.setState(() {
_data = <Widget> [new childWidget(s)];
});
});
}
}
class childWidget extends StatefulWidget {
childWidget(String s){
_title = s;
}
Widget _title;
createState() => new childState();
}
class childState extends State<gameCardS> {
Widget _title;
#override
Widget build(BuildContext context) {
return new GestureDetector(onTap: foo(),
child: new Card(child: new Text(_title));
}
initState()
{
super.initState();
_title = widget._title;
}
}
This should sort your problem out. Basically you always want your Widgets created in your build method hierarchy.
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(new MaterialApp(home: new Scaffold(body: new MainWidget())));
class MainWidget extends StatefulWidget {
#override
State createState() => new MainWidgetState();
}
class MainWidgetState extends State<MainWidget> {
List<ItemData> _data = new List();
Timer timer;
Widget build(BuildContext context) {
return new ListView(children: _data.map((item) => new ChildWidget(item)).toList());
}
#override
void initState() {
super.initState();
timer = new Timer.periodic(new Duration(seconds: 2), (Timer timer) async {
ItemData data = await loadData();
this.setState(() {
_data = <ItemData>[data];
});
});
}
#override
void dispose() {
super.dispose();
timer.cancel();
}
static int testCount = 0;
Future<ItemData> loadData() async {
testCount++;
return new ItemData("Testing #$testCount");
}
}
class ChildWidget extends StatefulWidget {
ItemData _data;
ChildWidget(ItemData data) {
_data = data;
}
#override
State<ChildWidget> createState() => new ChildState();
}
class ChildState extends State<ChildWidget> {
#override
Widget build(BuildContext context) {
return new GestureDetector(onTap: () => foo(),
child: new Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24.0),
child: new Card(
child: new Container(
padding: const EdgeInsets.all(8.0),
child: new Text(widget._data.title),
),
),
)
);
}
foo() {
print("Card Tapped: " + widget._data.toString());
}
}
class ItemData {
final String title;
ItemData(this.title);
#override
String toString() {
return 'ItemData{title: $title}';
}
}
This was really giving me headache and no Google results were working. What finally worked was so simple. In your child build() assign the value to the local variable before you return. Once I did this everything worked with subsequent data loads. I even took out the initState() code.
Many thanks to #Simon. Your answer somehow inspired me to try this.
In your childState:
#override
Widget build(BuildContext context) {
_title = widget._title; // <<< ADDING THIS HERE IS THE FIX
return new GestureDetector(onTap: foo(),
child: new Card(child: new Text(_title));
}
Hopefully this works in your code. For me, I use a Map for the entire JSON record passed in, rather than a single String, but that should still work.
The Root issue explained
initState(), for the child widget, is called only once when the Widget is inserted into the tree. Because of this, your child Widget variables will never be updated when they change on the parent widget. Technically the variables for the widgets are changing, you are just not capturing that change in your state class.
build() is the method that gets called every time something in the Widget changes. This is the reason #gregthegeek solution works. Updating the variables inside the build method of your child widget will ensure they get the latest from parent.
Works
class ChildState extends State<ChildWidget> {
late String _title;
#override
Widget build(BuildContext context) {
_title = widget._title; // <==== IMPORTANT LINE
return new GestureDetector(onTap: () => foo(),
child: new Text(_title),
);
}
}
Does not work
(It will not update when _title changes in parent)
class ChildState extends State<ChildWidget> {
late String _title;
#override
void initState() {
super.initState();
_title = widget._title; // <==== IMPORTANT LINE
}
#override
Widget build(BuildContext context) {
return new GestureDetector(onTap: () => foo(),
child: new Text(_title),
);
}
}
I'm unsure why this happens when calling setState(...) in an async function, but one simple solution is to use:
WidgetsBinding.instance.addPostFrameCallback((_) => setState(...));
instead of just setState(...)
This fixed my issue... If you have an initial value to be assigned on a variable use it in initState()
Note : Faced this issue when I tried to set initial value inside build function.
#override
void initState() {
count = widget.initialValue.length; // Initial value
super.initState();
}
don't use a future within a future; use different function that will return each future individually like this
List<Requests> requestsData;
List<DocumentSnapshot> requestsDocumentData;
var docId;
#override
void initState() {
super.initState();
getRequestDocs();
}
Future<FirebaseUser> getData() {
var _auth = FirebaseAuth.instance;
return _auth.currentUser();
}
getRequestDocs() {
getData().then((FirebaseUser user) {
this.setState(() {
docId = user.uid;
});
});
FireDb()
.getDocuments("vendorsrequests")
.then((List<DocumentSnapshot> documentSnapshots) {
this.setState(() {
requestsDocumentData = documentSnapshots;
});
});
for (DocumentSnapshot request in requestsDocumentData) {
this.setState(() {
requestsData.add(Requests(
request.documentID,
request.data['requests'],
Icons.data_usage,
request.data['requests'][0],
"location",
"payMessage",
"budget",
"tokensRequired",
"date"));
});
}
}
you can create individual functions for
FireDb().getDocuments("vendorsrequests")
.then((List<DocumentSnapshot> documentSnapshots) {
this.setState(() {
requestsDocumentData = documentSnapshots;
});
});
and
for (DocumentSnapshot request in requestsDocumentData) {
this.setState(() {
requestsData.add(Requests(
request.documentID,
request.data['requests'],
Icons.data_usage,
request.data['requests'][0],
"location",
"payMessage",
"budget",
"tokensRequired",
"date"));
});
}
I found that the use of
this
with setState is must
The real issue on child StatefulWidget not rebuilding is in the KEY
Hey, I'm a bit late to the discussion, but I think this is important.
I was facing a similar problem a while back and I even came to this thread to get some ideas.
In my case, I was simply getting widget.value directly inside the build method of the childWidget, and it was not updating when i called setState in the mainWidget.
Then i found this video: https://youtu.be/kn0EOS-ZiIc
(When to Use Keys - Flutter Widgets 101 Ep. 4) -
Here the Google dev talks about how keys in Flutter.
The short answer is
In a StatefulWidget the actual value you pass is stored in the state, not in the widget itself, like a StatelessWidget does.
When you call setState in the mainWidget, Flutter walks down the widget tree and checks each childWidget's type and key, to see if anything has changed. As stateful widgets store their values in the state, Flutter thinks the child widgets did not change (because the types and keys are the same) and does not rebuild them, even if the value changed.
The real solution is to give the widget a key containing the value that is changing, so when Flutter is walking down the tree, it notices that the key changed, and rebuilds the stateful widget.
Other solutions here may work as well, but if you want to really understand it, this video is worth watching.
first check whether it is a stateless or stateful widget,and if the class is stateless then make it to a stateful widget and try adding a code after closing the
setState(() { _myState = newValue; });
In my case, it was just defining the state as a class property and not a local variable in the build method
Doing this -
List<Task> tasks = [
Task('Buy milk'),
Task('Buy eggs'),
Task('Buy bread'),
];
#override
Widget build(BuildContext context) {
return ListView.builder(
itemBuilder: (context, index) => TaskTile(
...
instead of this -
#override
Widget build(BuildContext context) {
List<Task> tasks = [
Task('Buy milk'),
Task('Buy eggs'),
Task('Buy bread'),
];
return ListView.builder(
itemBuilder: (context, index) => TaskTile(
...
Found the best solution.
If you are using a stateless widget you can't use set state, so just convert the stateless widget to statefull widget