在 Flutter 上捕捉 Android 后退按钮事件

有没有什么办法可以从 Android 的后退按钮捕捉到 onBackPressed事件?

我已经尝试了 WillPopScope,但我的 onWillPop功能只有在我点击物质返回箭头按钮触发

我是这么说的:

class MyView extends StatelessWidget{


Widget build(BuildContext context) {


return new WillPopScope(
onWillPop: () async {
debugPrint("Will pop");
return true;
},
child: ScopedModel<AppModel>(
model: new AppModel(),
child: new Scaffold(......

我需要抓住它,因为不知道为什么我的屏幕表现不正确,当它来到后退按钮按下,它弹出的屏幕和屏幕下面,但不知道为什么,使用材质的后退箭头按钮工作正常。

更新:

代码可以正常工作,我的问题不在于这个屏幕的弹出,而是在前一个屏幕上,我使用了2个 Materials ialApp 小部件,不知怎么的,它给出了一个奇怪的行为。

105235 次浏览

In order to prevent navigating back WillPopScope is the correct way and should be used as follow:

class Page2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new WillPopScope(
child: new Scaffold(
appBar: new AppBar(
title: new Text('Page 2'),
),
body: new Center(
child: new Text('PAGE 2'),
),
),
onWillPop: () async {
return false;
},
);
}
}


Future<T> pushPage<T>(BuildContext context, Widget page) {
return Navigator.of(context)
.push<T>(MaterialPageRoute(builder: (context) => page));
}

Can call the page like:

pushPage(context, Page2());

This code work for me.

I think there may be two reasons.

  1. Child of WillPopScope is Scaffold
  2. No return in onWillPop

    return new WillPopScope(
    onWillPop: () {
    if (!_isOpened) Navigator.pop(context);
    },
    child: new Scaffold(
    key: SharedService.orderScaffoldKey,
    appBar: appBar,
    body: new Builder(
    builder: (BuildContext context) {
    return page;
    },
    ),
    ),
    );
    

This is should be helpful.

@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
_moveToScreen2(context, );
},
child: Scaffold(
key: _scaffoldKey,
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
_moveToScreen2(context);
}),
title: Text("Screen 1"),
),
),
);
}


/**
* This is probably too thin to be in its own method - consider using
* `Navigator.pushReplacementNamed(context, "screen2")` directly
*/
void _moveToScreen2(BuildContext context) =>
Navigator.pushReplacementNamed(context, "screen2");

Another way todo this is to implement a NavigatorObserver and link it to the MaterialApp:

https://api.flutter.dev/flutter/widgets/RouteObserver-class.html

You don't have to use RouteAware, you can also implement your own NavigatorObserver.

This is for example how Flutter analytics works to automatically track screen opens/closes:

        MaterialApp(
...
navigatorObservers: [
FirebaseAnalyticsObserver(analytics: analytics),
],
)

FirebaseAnalyticsObserver extends the RouteObserver which itself implements NavigatorObserver.

However WillPopScope is often the easier solution

Just adding an important point here. Please note that by using WillPopScope, we will lose the back swipe gesture on iOS.

Reference: https://github.com/flutter/flutter/issues/14203

Use WillPopScope method and return false

@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
// Do something here
print("After clicking the Android Back Button");
return false;
},
child: Scaffold(
appBar: AppBar(
title: Text("Handling the back button"),
),
body: Center(
child: Text("Body"),
),
),
);
}

You can use back_button_interceptor

it detects hardware back button & will be so useful specially in case of using persistent_bottom_nav_bar

@override
void initState() {
super.initState();
BackButtonInterceptor.add(myInterceptor);
}


@override
void dispose() {
BackButtonInterceptor.remove(myInterceptor);
super.dispose();
}


bool myInterceptor(bool stopDefaultButtonEvent, RouteInfo info) {
print("BACK BUTTON!"); // Do some stuff.
return false;// return true if u want to stop back
}

Following the documentation of BackButtonListener:

/// It can be useful for scenarios, in which you create a different state in your
/// screen but don't want to use a new page for that.

https://github.com/flutter/flutter/pull/79642

e.g.

  @override
Widget build(BuildContext context) {
return BackButtonListener(
onBackButtonPressed: () {
/// todo: close search widget
if(searchBarController.isClose()){
return false;
}else{
searchBarController.close();
return Future.value(true);
}
},
child: SearchBar(controller: searchBarController),
);
}

This is the updated code

basically, WillPopScope -> onWillPop works on the future argument we can say as when it happens then ???? so as soon the back button is pressed WillPopScope -> onWillPop gets activated and listens to the argument more specific the back button event to pop it (replace it) Most of the time I use it to show a DialogBox of Future type because it will only appear when it is needed same can be used to navigate to a new screen as well (hope so) preferred to do MaterialPage routing or named routing techniques for navigation, use WillPopScope for the hardware back button event listening (hardware = Android/IOs) to show the exit popup

code is already been given above but does not work for me so I change a little bit

@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async{
return _moveToScreen2(context);
},
child: Scaffold(
key: _scaffoldKey,
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
_moveToScreen2(context);
}),
title: Text("Screen 1"),
),
),
);
}


Future<bool>_moveToScreen2(BuildContext context) =>
Navigator.pushReplacementNamed(context, "screen2");

===============================================

What I do on Exit

@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: ()=> showExitPopup(context)
child: Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text("Screen 1"),
),
),
);
}

=========================================

On Back button Press created a dart file with the name showExitPopup

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:vexpositions/Servises/ConstantManager.dart';


Future<bool> showExitPopup(context) async{
return await showDialog<bool>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: SizedBox(
height: 90,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text("Want to Exit the app!"),
const SizedBox(height:20),
Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () {
print('yes selected');
exit(0);
},
style: ElevatedButton.styleFrom(
primary: Colors.white),
child: const Text("Yes", style: TextStyle(color:
Colors.black)),
),
),
const SizedBox(width: 15),
Expanded(
child: ElevatedButton(
onPressed: () {
print('no selected');
Navigator.of(context).pop();
},
style: ElevatedButton.styleFrom(
primary: Colors.red.shade800,
),
child: const Text("No", style: TextStyle(color:
Colors.white)),
))
],
)
],
),
),
);
});
}