【发布时间】:2019-04-01 23:24:24
【问题描述】:
有什么方法可以检测用户是否离开当前页面?
我认为WidgetsBinding 不会起作用,因为它自己处理这些事件。
那么,有人有什么解决办法吗?
任何帮助表示赞赏。
【问题讨论】:
-
您可能正在寻找
willPopScope小部件。 stackoverflow.com/questions/44635450/…
标签: flutter
有什么方法可以检测用户是否离开当前页面?
我认为WidgetsBinding 不会起作用,因为它自己处理这些事件。
那么,有人有什么解决办法吗?
任何帮助表示赞赏。
【问题讨论】:
willPopScope 小部件。 stackoverflow.com/questions/44635450/…
标签: flutter
如果“离开页面”是指用户从该页面“返回”,我有一个简单的解决方案。如果您还想在用户在当前页面之前打开另一个页面时收到通知,则以下解决方案将不起作用。
对于第一种情况,您可以使用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.
);
【讨论】:
return true; 之前加上print(),我确认它甚至没有到达。
根据您的描述,我认为您希望在用户按下后退按钮或返回上一屏幕时跟踪您的用户。
您可以通过覆盖 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);
}
),
),
),
);
}
}
【讨论】:
如果您想检测用户是否离开屏幕,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);
},
),
],
),
),
),
);
}
}
【讨论】:
我最近遇到了一种情况,我需要检测路线的变化,以弹出当前路线或从当前路线推送到新路线,我能够通过RouteObserver从答案@中获得这一点987654322@.
使用WillPopScope,您可以在路由(pop)发生之前检测到可能发生的变化,并确定是否应该允许或拒绝它。如果你只是想在pop之后执行一个动作,可以通过覆盖onDispose来完成。
【讨论】:
您可以通过覆盖dispose() 方法来实现此目的。
【讨论】: