I have a custom appbar class where I pass the title as its constructor.
class MyAppBar extends StatelessWidget implements PreferredSizeWidget {
final String? title;
@override
Size get preferredSize => Size.fromHeight(AppBar().preferredSize.height);
const MyAppBar({super.key, this.title});
@override
Widget build(BuildContext context) {
return AppBar(title: Text(title ?? "CFlytics"));
}
}
Now, I’m using MyAppBar in a ConsumerStatefulWidget with ‘appbartitle’ as a state variable passed to it.
When I update the appbartitle variable using setState, it doesn’t update the appbar title at all
class _MyHomePageState extends State<MyHomePage> {
String? appbartitle;
@override
Widget build(BuildContext context) {
final myData =ref.watch(someProvider);
return Scaffold(
appBar: MyAppBar(title: appbartitle),
body: myData.when(
data: (data) {
setState(() {appbartitle = "123";});
return ...;
},
loading: {return ...}
error: {return ...}
But when I wrap the setState line in Future.delayed even with 0 ms delay, it works as required
Future.delayed(const Duration(milliseconds: 0), () {
setState(() {appbartitle = "new title";});
});
Why is it so and what would be the correct way to implement this?
2
Answers
First of all, this syntax looks like old riverpod, and someprovider would be a FutureProvider in this sense. Nevertheless, I would expect to see the error ‘setstate or markNeedsBuild() called during build’ if you do this. I think it’s not working because, you are calling setstate in myData.when() which effectively calls setstate also, all within the same synchronous process.
Placing it in a Future.delayed makes it an Asynchronous call which is scheduled after synchronous tasks, in the event loop. You can look up event queue, microtask queue and async processes in Flutter.
By wrapping it in a Future, you are simply calling setstate at a later time after myData.when has processed it’s own setstate.
Riverpod has ref.listen, which is a more appropriate method to use within the build to update data.
Alternatively, I would write something like this to not change too much code.
Nevertheless you will still need to handle loading and error states and myData.haveValue can be true whiile state is in loading/error.
Rather than using class variable use : ValueNotifier<String?> appBarTitle = ValueNotifier<String?>(null);
Wrap your AppBar with ValueListenableBuilder Like This :
assign new value like this :
never use setState in builders;