导航到新路线时处置小部件

S.D*_*.D. 23 dart flutter

我的应用程序中有两个屏幕。

屏幕 A 在打开时运行计算成本很高的操作,并通过在dispose()调用时取消动画/订阅数据库来正确处理以防止内存泄漏。

从屏幕 A,您可以打开另一个屏幕(屏幕 B)。

当我使用 时Navigator.pushNamed,屏幕 A 保留在内存中,dispose()不会被调用,即使现在显示了屏幕 B。

有没有办法在屏幕 A 不在视野中时强制处理它?

永远不会处理第一条路线的示例代码:

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    title: 'Navigation Basics',
    home: FirstRoute(),
  ));
}

class FirstRoute extends StatefulWidget {
  @override
  _FirstRouteState createState() => _FirstRouteState();
}

class _FirstRouteState extends State<FirstRoute> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Route'),
      ),
      body: RaisedButton(
        child: Text('Open route'),
        onPressed: () {
          Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => SecondRoute()),
          );
        },
      ),
    );
  }

  @override
  void dispose() {
    // Never called
    print("Disposing first route");
    super.dispose();
  }
}

class SecondRoute extends StatefulWidget {
  @override
  _SecondRouteState createState() => _SecondRouteState();
}

class _SecondRouteState extends State<SecondRoute> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Route"),
      ),
      body: RaisedButton(
        onPressed: () {
          Navigator.pop(context);
        },
        child: Text('Go back!'),
      ),
    );
  }

  @override
  void dispose() {
    print("Disposing second route");
    super.dispose();
  }
}
Run Code Online (Sandbox Code Playgroud)

Jos*_*ior 14

我知道这有点晚了,但我认为您应该覆盖该deactivate方法。由于我们正在更改页面,我们实际上并没有销毁它,这就是为什么dispose没有被调用的原因。

如果您想了解更多信息,此页面列出了有状态小部件的生命周期。

从链接:

'deactivate()' 在从树中移除 State 时被调用,但它可能会在当前帧更改完成之前重新插入。这种方法之所以存在,基本上是因为 State 对象可以从树中的一个点移动到另一个点。

  • 这不起作用,我测试过,当我调用 navigator.push 时,未调用非活动函数 (5认同)

axe*_*aze 8

Navigator.pushReplacement在第一和第二屏幕之间路由时调用。

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    title: 'Navigation Basics',
    home: FirstRoute(),
  ));
}

class FirstRoute extends StatefulWidget {
  @override
  _FirstRouteState createState() => _FirstRouteState();
}

class _FirstRouteState extends State<FirstRoute> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Route'),
      ),
      body: RaisedButton(
        child: Text('Open route'),
        onPressed: () {
          Navigator.pushReplacement(
            context,
            MaterialPageRoute(builder: (context) => SecondRoute()),
          );
        },
      ),
    );
  }

  @override
  void dispose() {
    // Never called
    print("Disposing first route");
    super.dispose();
  }
}

class SecondRoute extends StatefulWidget {
  @override
  _SecondRouteState createState() => _SecondRouteState();
}

class _SecondRouteState extends State<SecondRoute> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Route"),
      ),
      body: RaisedButton(
        onPressed: () {
          Navigator.pushReplacement(
            context,
            MaterialPageRoute(builder: (context) => FirstRoute()),
          );
        },
        child: Text('Go back!'),
      ),
    );
  }

  @override
  void dispose() {
    print("Disposing second route");
    super.dispose();
  }
}
Run Code Online (Sandbox Code Playgroud)

尝试这个

  • 我希望它在处理第一个屏幕时表现正常(正常的后退按钮)。 (8认同)
  • 非常适合登录重定向! (2认同)

Ben*_*aie 5

在新版本的 flutter 中,deactivate当您将新的小部件推到另一个小部件之上时,不会调用 flutter。flutter 的 github 上还有一个与此主题相关的未解决问题:https://github.com/flutter/flutter/issues/50147

处理此问题的最佳方法是添加RouteObserver<PageRoute>到您的材料应用程序并覆盖didPushNextdidPopNext功能。

有一篇与此主题相关的非常有用的媒体文章,您可以在这里找到:https://medium.com/koahealth/how-to-track-screen-transitions-in-flutter-with-routeobserver-733984a90dea

正如文章所说创建你自己的RouteAwareWidget,你可以将这两个回调添加到小部件的字段中: didPopNext didPushNext

class RouteAwareWidget extends StatefulWidget {
  final Widget child;
  final VoidCallback? didPopNext;
  final VoidCallback? didPushNext;

  const RouteAwareWidget({
    Key? key,
    required this.child,
    this.didPopNext,
    this.didPushNext,
  }) : super(key: key);

  @override
  State<RouteAwareWidget> createState() => RouteAwareWidgetState();
}

class RouteAwareWidgetState extends State<RouteAwareWidget> with RouteAware {
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute);
  }

  @override
  void dispose() {
    routeObserver.unsubscribe(this);
    super.dispose();
  }

  @override
  void didPush() {}

  @override
  void didPopNext() {
    dPrint('didPopNext');
    widget.didPopNext == null ? null : widget.didPopNext!();
    super.didPopNext();
  }

  @override
  void didPushNext() {
    dPrint('didPushNext');
    widget.didPushNext == null ? null : widget.didPushNext!();
    super.didPushNext();
  }

  @override
  Widget build(BuildContext context) => widget.child;
}

Run Code Online (Sandbox Code Playgroud)

创建一个全局RouteObserver<PageRoute>并将其添加到您的材质应用程序中:

final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();

MaterialApp(
     navigatorObservers: [routeObserver],
     debugShowCheckedModeBanner: false,
     routes: _routes,
)
Run Code Online (Sandbox Code Playgroud)

然后在您的路线中,您应该使用您的路线包装RouteAwareWidget并添加您想要的自定义函数:

  final _routes = {
    HomePage.routeName: (context) => RouteAwareWidget(
          child: const HomePage(),
          didPushNext: () => sl<CameraBloc>().add(Dispose()),
          didPopNext: () => sl<CameraBloc>().add(Init()),
        ),
    MyQuestions.routeName: (context) => const RouteAwareWidget(
          child: MyQuestions(),
        ),
};
Run Code Online (Sandbox Code Playgroud)

didPushNext当您将一个小部件推到主页顶部时将被调用,并且didPopNext当您在主页上方弹出最后一个小部件时将被调用。