检测用户是否将当前页面留在Flutter中?

时间:2018-10-29 03:57:30

标签: flutter

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

5 个答案:

答案 0 :(得分:3)

RouteObserver 和 RouteAware 如果您想检测用户是否离开屏幕,无论是返回(弹出)还是推送另一个上下文,都可以使用 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);
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

答案 1 :(得分:1)

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

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

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);
              }
          ),
        ),
      ),
    );
  }
}

答案 2 :(得分:1)

我最近遇到了这样一种情况,我需要检测路径的变化以弹出当前路径或从当前路径推到新路径,而我能够通过从RouteObserver获得回答here

使用WillPopScope,您可以在路由(弹出)发生之前检测出可能的更改,并确定是否应该允许或拒绝。如果您只是想在弹出后执行某项操作,可以通过覆盖onDispose来完成。

答案 3 :(得分:1)

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

答案 4 :(得分:-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.
);