0

I have widget that is unmounted on page change.

I am using flutter web.

I looked at all the methods here but I can't seem to find the one.

I tried

@override
void dispose() {
  print("dispose?"); 
  super.dispose();   
}

@override
void deactivate() {
  print("deactivate");
  super.deactivate();
}

and neither is called.

It is a very simple thing I need to detect when the widget is not being "built" (rendered). I don't think having something to dtect the page change is the solution, it seems overkill. Thank you

Adding code sample

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    title: 'Named Routes Demo',
    initialRoute: '/',
    routes: {
      '/': (context) => FirstScreen(),
      '/second': (context) => SecondScreen(),
    },
  ));
}

class FirstScreen extends StatefulWidget {
  FirstScreen({Key key}) : super(key: key);

  @override
  _FirstScreenState createState() => _FirstScreenState();
}

class _FirstScreenState extends State<FirstScreen> {
  @override
void dispose() {
  print("dispose"); 
  super.dispose();   
}

@override
void deactivate() {
  print("deactivate");
  super.deactivate();
}
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Launch screen'),
          onPressed: () {
            Navigator.pushNamed(context, '/second');
          },
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Screen"),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // Navigate back to the first screen by popping the current route
            // off the stack.
            Navigator.pop(context);
          },
          child: Text('Go back!'),
        ),
      ),
    );
  }
}

It seems neither deactive nor dispose are called when navigating to the second screen and the first screen widget stops being part of the render tree. So what to call to detect that Screen 1 leaves the render tree?

user3808307
  • 2,270
  • 9
  • 45
  • 99
  • So you are saying the widget becomes unmounted but you did not do anything to take it off the widget tree? Maybe post some code that demonstrate the issue would be helpful. – WSBT Dec 10 '20 at 21:25
  • @user1032613 added. I want to detect from first screen when first screen stops being redered. In react we have componentDidUnmount with classes. Is there a flutter equivalent? – user3808307 Dec 13 '20 at 15:02

2 Answers2

0

if the Widget has not mounted then return it. But you have to do it before any setState method

if (!mounted) return;
setState(() {});

or

if (mounted) {
   //your code
};
setState(() {});

You can read more about it here: https://api.flutter.dev/flutter/widgets/State/mounted.html

Anyways the only thing beeing called before the build is the initState. Everything youre doing inside this is before the build. initstate: https://api.flutter.dev/flutter/widgets/EditableTextState/initState.html

Marcel Dz
  • 2,321
  • 4
  • 14
  • 49
0

In the code example you added, screen 1 is never unmounted, thus those callbacks are not fired. Screen 2 is rendered on top of screen 1, and both are mounted.

You can confirm that screen 1 is always mounted, by adding a periodic callback in its state, add let it print out whether it's still mounted, for example:

  import 'dart:async';
  
  // in _FirstScreenState, add:

  @override
  void initState() {
    Timer.periodic(Duration(seconds: 1), (timer) {
      print("screen 1 is still mounted? $mounted");
    });
    super.initState();
  }

You will see that, it is always mounted, even if screen 2 is rendered on top of screen 1.

Also, you can do the same deactivate and dispose logic for screen 2, and you will see that, when navigating back from screen 2 to screen 1, screen 2 is truly unmounted, and both of the callbacks work correctly. Thus again proving the problem isn't at those callbacks, it's that screen 1 wasn't unmounted.

So yeah... before you posted the code sample, I thought you ran into some error about widget being unmounted (defunct). But in this case, since you are using buttons and thus know exactly when page transition occurs, maybe you can just do whatever business logic you need to do, in the button callback?

WSBT
  • 33,033
  • 18
  • 128
  • 133
  • Hi, thank you, no, I can't do it in the button call back. This is example where the button callback is ok, but it is not the real case. So, what function does trigger when the Page 1 is not in view anymore? (for whatever reason, whether it be it unmounted or it is left behind the other screen, it is not part of the render tree anymore) There must be a terminology for this that I don¿t know of. – user3808307 Dec 15 '20 at 18:25
  • @user3808307, you might be victim to the `XY problem`. It would be helpful if you can describe a legit use case where you want a callback when a widget becomes invisible (and not during the build process, because you can check "mounted" there), then I can try to help you further. – WSBT Dec 15 '20 at 22:35
  • I don't want to check when it becomes invisible, but right before it will become invisible, as to store data from an input field in the data base. It is common to need to know when widget will be remove from the tree, else there wouldn't be a specifical function for it in react. Other cases may be unsubscriptions from channels for example – user3808307 Dec 15 '20 at 23:41
  • @user3808307 Again, "deactivate" is exactly when it's removed from the tree, and "dispose" is usually where people clean up, such as unsub from channels. These are similar to the useEffect return lambda in React if I recall correctly. In your example, the fact that screen 1 is not unloaded is perfect, because you don't want to unsub in this case, so users can return to it quickly. To store input field, you should either do that when user finishes typing (onSubmit event), or when the focus is removed, or as often as each change is made. – WSBT Dec 16 '20 at 17:58
  • If it is perfect that screen 1 is not unloaded, then deactivate is not what I need. Here you can see a similar question comparing it to android functions https://stackoverflow.com/questions/44331725/onresume-and-onpause-for-widgets-on-flutter instead of react functions – user3808307 Dec 16 '20 at 19:12