FlutterError (This widget has been unmounted, so the State no longer has a context (and should be considered defunct). Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.)
I wrote a simple component like this:
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: const HomeView(),
);
}
}
final data = signal(0);
void increment() {
data.value++;
}
class HomeView extends StatelessWidget {
const HomeView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Demo')),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Watch((_) => Text(data.value.toString())),
const SizedBox(height: 20),
ElevatedButton(onPressed: increment, child: const Text('Increment')),
],
),
);
}
}
Then I edited this part:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Demo')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Watch((_) => Text(data.value.toString())),
const SizedBox(height: 20),
ElevatedButton(onPressed: increment, child: const Text('Increment')),
],
),
),
);
}
I added a Center widget, and then I got an error:
FlutterError (This widget has been unmounted, so the State no longer has a context (and should be considered defunct).
Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.)
Why is this happening? Did I write something wrong?
I tried using a StatefulWidget, but it still doesn't work:
class HomeView extends StatefulWidget {
const HomeView({super.key});
@override
State<HomeView> createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
final data = signal(0);
void increment() {
data.value++;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Demo')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Watch((_) => Text(data.value.toString())),
const SizedBox(height: 20),
ElevatedButton(
onPressed: increment,
child: const Text('Increment'),
),
],
),
),
);
}
}
It only works correctly in a StatelessWidget for hot reload:
class HomeView extends StatelessWidget {
HomeView({super.key});
final data = signal(0);
void increment() {
data.value++;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Demo')),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Watch((_) => Text(data.value.toString())),
const SizedBox(height: 20),
ElevatedButton(
onPressed: increment,
child: const Text('Increment'),
),
],
),
);
}
}
In the official MVI demo, adding a Center or other Widget at this position also triggers this error
Does a hot restart fix it?
Does a hot restart fix it?
There is no problem with Hot restart, but the problem is with Hot reload, and Hot reload is something that is often used in development.
I think I've experienced this issue a couple times today.
Hot reload is a known issue and working on seeing if there is a way to address it.
The issue is about resubscribing all the listeners and when hot reload happens you either get widgets that do not update if the signal changes post reload or what we have now.
That is also why I recommend the SignalsMixin
Hello everyone. I think I tracked down and found a possible fix.
This bug is closely related to #398.
In lib/src/watch/builder.dart.
If the Watcher widgets gets replaced in the widget tree with a new instance on hot reload, the _WatchState.result gets disposed. The post-frame-callback still runs and tries to calculate the disposed result which throws.
Adding a guard clause seems to solve the problem:
void reassemble() {
super.reassemble();
final target = core.SignalsObserver.instance;
if (target is core.DevToolsSignalsObserver) {
target.reassemble();
}
WidgetsBinding.instance.addPostFrameCallback((_) {
+ if (result.disposed) return;
result.recompute();
if (mounted) setState(() {});
result.value;
});
}
Can you confirm?