【问题标题】:Detect if the user leaves the current page in Flutter?检测用户是否离开 Flutter 中的当前页面?
【发布时间】:2019-04-01 23:24:24
【问题描述】:

有什么方法可以检测用户是否离开当前页面? 我认为WidgetsBinding 不会起作用,因为它自己处理这些事件。 那么,有人有什么解决办法吗? 任何帮助表示赞赏。

【问题讨论】:

标签: flutter


【解决方案1】:

如果“离开页面”是指用户从该页面“返回”,我有一个简单的解决方案。如果您还想在用户在当前页面之前打开另一个页面时收到通知,则以下解决方案将不起作用。

对于第一种情况,您可以使用WillPopScope。 当封闭的ModalRoute(由Navigator 内部使用)即将弹出时,它会通知您。它甚至让您可以选择是否要发生流行音乐。

只需将第二个屏幕的Scaffold 包裹在WillPopScope 中。

return WillPopScope(
  onWillPop: () async {
    // You can do some work here.
    // Returning true allows the pop to happen, returning false prevents it.
    return true;
  },
  child: ... // Your Scaffold goes here.
);

【讨论】:

  • WillPopScope() 根本不适合我。没有错误,什么都没有。在return true; 之前加上print(),我确认它甚至没有到达。
  • 我正在使用导航器的推送方法来推送我的“第二个”页面,WillPopScope 效果很好!如果对你不起作用,是不是你没有使用 Flutter 内置的 Navigator?
【解决方案2】:

根据您的描述,我认为您希望在用户按下后退按钮或返回上一屏幕时跟踪您的用户。

您可以通过覆盖 State 类上的 dispose 来实现此目的。

以下示例可以帮助您找出解决方案。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: EventRow(),
    );
  }
}


class EventRow extends StatelessWidget {


  @override
  Widget build (BuildContext context) {
    return Scaffold(
      appBar: new AppBar(
        title: new Text("Demo"),
      ),
      body: Center(
        child: Container(
          child: new RaisedButton(
            onPressed: (){
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => SecondScreen()),
              );
              },
            child: Text("Goto Second Scrren"),
          ),
        ),
      ),
    );
  }

}


class SecondScreen extends StatefulWidget {
  @override
  _SecondScreenState createState() => _SecondScreenState();
}

class _SecondScreenState extends State<SecondScreen> {

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
  }

  @override
  void dispose() {
    // TODO: implement dispose
    print("Back To old Screen");
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: new Center(
        child: new Container(
          child: new RaisedButton(
              child: Text("Goto First Scrren"),
              onPressed: (){
                Navigator.pop(context);
              }
          ),
        ),
      ),
    );
  }
}

【讨论】:

  • 与 WillPopScope() 相比,这非常有效
  • 它会起作用的。但在某些情况下,如果您的导航速度过快,则需要一些时间才能反映为 dispose 方法调用。
【解决方案3】:

如果您想检测用户是否离开屏幕,RouteObserver 和 RouteAware 是一种选择,无论是返回(弹出)还是推送另一个上下文。

这是一个例子。抱歉有点长,如果你尝试运行它你会发现它非常简单。

import 'package:flutter/material.dart';

void main() async {
  runApp(App());
}

class App extends StatelessWidget {
  static final RouteObserver<PageRoute> routeObserver =
      RouteObserver<PageRoute>();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/',
      navigatorObservers: [routeObserver],
      routes: {
        '/': (context) => Screen1(),
        'screen2': (context) => Screen2(),
      },
    );
  }
}

class ScreenWrapper extends StatefulWidget {
  final Widget child;
  final Function() onLeaveScreen;
  final String routeName;
  ScreenWrapper({this.child, this.onLeaveScreen, @required this.routeName});

  @override
  State<StatefulWidget> createState() {
    return ScreenWrapperState();
  }
}

class ScreenWrapperState extends State<ScreenWrapper> with RouteAware {
  @override
  Widget build(BuildContext context) {
    return widget.child;
  }

  void onLeaveScreen() {
    if (widget.onLeaveScreen != null) {
      widget.onLeaveScreen();
    }
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    App.routeObserver.subscribe(this, ModalRoute.of(context));
  }

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

  @override
  void didPush() {
    print('*** Entering screen: ${widget.routeName}');
  }

  void didPushNext() {
    print('*** Leaving screen: ${widget.routeName}');
    onLeaveScreen();
  }

  @override
  void didPop() {
    print('*** Going back, leaving screen: ${widget.routeName}');
    onLeaveScreen();
  }

  @override
  void didPopNext() {
    print('*** Going back to screen: ${widget.routeName}');
  }
}

class Screen1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ScreenWrapper(
      onLeaveScreen: () {
        print("***** Here's my special handling for leaving screen1!!");
      },
      routeName: '/',
      child: Scaffold(
        backgroundColor: Colors.yellow,
        body: SafeArea(
          child: Column(
            children: [
              Text('This is Screen1'),
              FlatButton(
                child: Text('Press here to go to screen 2'),
                onPressed: () {
                  Navigator.pushNamed(context, 'screen2');
                },
              ),
              FlatButton(
                child: Text(
                    "Press here to go back (only works if you've pushed before)"),
                onPressed: () {
                  Navigator.maybePop(context);
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class Screen2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ScreenWrapper(
      routeName: 'screen2',
      child: Scaffold(
        backgroundColor: Colors.blue,
        body: SafeArea(
          child: Column(
            children: [
              Text('This is Screen2'),
              FlatButton(
                child: Text('Press here to go to screen 1'),
                onPressed: () {
                  Navigator.pushNamed(context, '/');
                },
              ),
              FlatButton(
                child: Text(
                    "Press here to go back (only works if you've pushed before)"),
                onPressed: () {
                  Navigator.maybePop(context);
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

【讨论】:

    【解决方案4】:

    我最近遇到了一种情况,我需要检测路线的变化,以弹出当前路线或从当前路线推送到新路线,我能够通过RouteObserver从答案@中获得这一点987654322@.

    使用WillPopScope,您可以在路由(pop)发生之前检测到可能发生的变化,并确定是否应该允许或拒绝它。如果你只是想在pop之后执行一个动作,可以通过覆盖onDispose来完成。

    【讨论】:

    • 感谢您的回答,但我需要在 onDispose 完成之前保留一些数据并且它不起作用。你有什么建议? .... 没关系 RouteObserver 是我需要的。谢谢
    【解决方案5】:

    您可以通过覆盖dispose() 方法来实现此目的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-17
      • 2020-06-28
      • 2018-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-13
      相关资源
      最近更新 更多