Comment désactiver ou remplacer le bouton "RETOUR" d'Android, dans Flutter?

102

Existe-t-il un moyen de désactiver le bouton de retour Android sur une page spécifique?

class WakeUpApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: "Time To Wake Up ?",
      home: new WakeUpHome(),
      routes: <String, WidgetBuilder>{
        '/pageOne': (BuildContext context) => new pageOne(),
        '/pageTwo': (BuildContext context) => new pageTwo(),
      },
    );
  }
}

Sur la pageOne j'ai un bouton pour aller à la pageDeux:

new FloatingActionButton(
  onPressed: () {    
    Navigator.of(context).pushNamed('/pageTwo');
  },
)

Mon problème est que si j'appuie sur la flèche Retour en bas de l'écran Android, je retourne à pageOne. J'aimerais que ce bouton n'apparaisse pas du tout. Idéalement, je voudrais n'avoir aucun moyen de sortir de cet écran à moins que l'utilisateur par exemple garde son doigt appuyé sur l'écran pendant 5 secondes. (J'essaie d'écrire une application pour les tout-petits, et je voudrais que seuls les parents puissent sortir de l'écran particulier).

Jackpap
la source

Réponses:

188

La réponse est WillPopScope. Cela empêchera la page d'être affichée par le système. Vous pourrez toujours utiliserNavigator.of(context).pop()

@override
Widget build(BuildContext context) {
  return new WillPopScope(
    onWillPop: () async => false,
    child: new Scaffold(
      appBar: new AppBar(
        title: new Text("data"),
        leading: new IconButton(
          icon: new Icon(Icons.ac_unit),
          onPressed: () => Navigator.of(context).pop(),
        ),
      ),
    ),
  );
}
Rémi Rousselet
la source
1
Pour ceux qui souhaitent intercepter le pop d'un formulaire, il est plus pratique d'utiliser la onWillPoppropriété du formulaire . Il a accès à l'état du formulaire et peut d'abord vérifier s'il existe un état que l'utilisateur ne souhaite pas perdre.
Joe Lapp
Salut @ RémiRousselet, Pouvez-vous s'il vous plaît m'aider sur la pile arrière à gérer comme si j'avais une pile d'écrans A, B, C, D et je veux naviguer à partir de D-> B OU D-> A, alors comment vais-je gérer il. Pouvez-vous s'il vous plaît me guider dessus
Ravindra Kushwaha
Je connais cette réponse peut-être ancienne. Mais c'est un bijou! Pensez à élargir l'explication. Cela fonctionne superbe. Je vous remercie.
Val le
1
Pour quelqu'un qui veut faire quelque chose avant pop (), peut utiliser cecionWillPop: () async { onBack(); // "DO YOUR FUNCTION IS HERE WHEN POP" return false; }
Huy Tower
21

Comme l'a souligné Rémi Rousselet, WillPopScopec'est généralement la voie à suivre. Cependant, si vous développez un widget avec état qui devrait réagir directement au bouton de retour, vous pouvez utiliser ceci:

https://pub.dartlang.org/packages/back_button_interceptor

Remarque: je suis l'auteur de ce package.

MarcG
la source
Sauf votre respect, ne pensez-vous pas que cela convient mieux comme commentaire?
CopsOnRoad
21
@CopsOnRoad Non, car ce n'est pas un commentaire sur une autre réponse, mais plutôt une manière totalement différente de résoudre le même problème.
MarcG
C'est une bonne alternative et elle aurait pu être ignorée si elle faisait partie d'un commentaire.
abhijat_saxena il y a
16

Bien que la réponse de Remi soit juste, généralement vous ne voulez pas simplement bloquer le bouton de retour, mais voulez qu'un utilisateur confirme la sortie.

Vous pouvez le faire de la même manière en obtenant une réponse dans la boîte de dialogue de confirmation, car onWillPopc'est un avenir.

@override
Widget build(BuildContext context) {
  return WillPopScope(
    child: Scaffold(...),
    onWillPop: () => showDialog<bool>(
      context: context,
      builder: (c) => AlertDialog(
        title: Text('Warning'),
        content: Text('Do you really want to exit'),
        actions: [
          FlatButton(
            child: Text('Yes'),
            onPressed: () => Navigator.pop(c, true),
          ),
          FlatButton(
            child: Text('No'),
            onPressed: () => Navigator.pop(c, false),
          ),
        ],
      ),
    ),
  );
}
Airon Tark
la source
Pourquoi cette réponse reçoit-elle un vote négatif? Est-ce que quelqu'un a mis en œuvre cela et a eu une mauvaise expérience?
SamiAzar
Je veux aller à l'écran précédent plutôt qu'au premier écran après avoir appuyé sur le bouton retour de l'appareil. Je n'ai pas besoin d'une boîte de dialogue comme celle-ci. Que dois-je faire
LakshmiYogeshwaran
4

Je poste ceci ici au cas où quelqu'un trouverait ceci et souhaiterait trouver un exemple simple https://gist.github.com/b-cancel/0ca372017a25f0c120b14dfca3591aa5

import 'package:flutter/material.dart';

import 'dart:async';

void main() => runApp(new BackButtonOverrideDemoWidget());

class BackButtonOverrideDemoWidget extends StatefulWidget{
  @override
  _BackButtonOverrideDemoWidgetState createState() => new _BackButtonOverrideDemoWidgetState();
}

class _BackButtonOverrideDemoWidgetState extends State<BackButtonOverrideDemoWidget> with WidgetsBindingObserver{

  //-------------------------Test Variable

  bool isBackButtonActivated = false;

  //-------------------------Required For WidgetsBindingObserver

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  //-------------------------Function That Triggers when you hit the back key

  @override
  didPopRoute(){
    bool override;
    if(isBackButtonActivated)
      override = false;
    else
      override = true;
    return new Future<bool>.value(override);
  }

  //-------------------------Build Method

  @override
  Widget build(BuildContext context) {
    return new Directionality(
      textDirection: TextDirection.ltr,
      child: new Container(
          color: (isBackButtonActivated) ? Colors.green : Colors.red,
          child: new Center(
              child: new FlatButton(
                color: Colors.white,
                onPressed: () {
                  isBackButtonActivated = !isBackButtonActivated;
                  setState(() {});
                },
                child: (isBackButtonActivated) ?
                new Text("DeActive the Back Button") : new Text("Activate the Back Button"),
              )
          )
      ),
    );
  }
}
Bryan Annuler
la source
Je l'ai littéralement utilisé moi-même hier. Gardez à l'esprit que ce n'est que pour Android, Apple n'a pas de bouton de retour. Pourriez-vous peut-être me dire ce qui ne va pas pour que je puisse le réparer, ou peut-être que cela ne fonctionne que sur mon émulateur particulier. Je n'ai pas mis à jour mon flutter depuis quelques semaines, alors c'est peut-être le problème. Faites-moi savoir
Bryan Annuler le
@BryanCancel Votre réponse ne fonctionne que si vous n'avez pas encore de routes poussées. Voir la méthode WidgetsBinding.handlePopRoute (). Il notifie les observateurs dans l'ordre d'enregistrement, et s'arrête dès qu'il reçoit vrai. Lorsqu'il y a des routes poussées, le navigateur retourne d'abord true, puis votre observateur n'est jamais réellement appelé. En d'autres termes, votre code fonctionne uniquement pour empêcher l'application de s'arrêter lorsque l'utilisateur clique sur le bouton Précédent, lorsqu'il ne reste plus de routes.
MarcG
3

Vous pouvez utiliser Future.value(bool)pour gérer le bouton de retour.

bool _allow = true;

@override
Widget build(BuildContext context) {
  return WillPopScope(
    child: Scaffold(appBar: AppBar(title: Text("Back"))),
    onWillPop: () {
      return Future.value(_allow); // if true allow back else block it
    },
  );
}
CopsOnRoad
la source
FYI, cela équivaut à onWillPop: () async => _allow.
Lynn le
@Lynn oui je le sais. Je voulais juste partager l' Future.value()appel.
CopsOnRoad
3

J'ai utilisé mixin et le widget WillPopScope ne pouvait tout simplement pas faire le travail pour moi. C'est la meilleure approche que j'ai trouvée, bien meilleure que WillPopScope à mon avis.
final bool canPop = ModalRoute.of(context)?.canPop ?? false;
Utilisé comme ceci dans la barre d'application:

leading: ModalRoute.of(context)?.canPop ?? false
    ? IconButton(
        onPressed: () {
          Navigator.pop(context);
        },
        icon: (Platform.isAndroid)
            ? const Icon(Icons.arrow_back)
            : const Icon(Icons.arrow_back_ios),
      )
    : Container(),
Djordje Komazec
la source
2

La réponse peut-être que vous saviez que cela utilise WillPopScope , mais malheureusement sur IOS, vous ne pouvez pas glisser pour revenir à la page précédente, alors personnalisons votre MaterialPageRoute:

class CustomMaterialPageRoute<T> extends MaterialPageRoute<T> {
  @protected
  bool get hasScopedWillPopCallback {
    return false;
  }
  CustomMaterialPageRoute({
    @required WidgetBuilder builder,
    RouteSettings settings,
    bool maintainState = true,
    bool fullscreenDialog = false,
  }) : super( 
          builder: builder,
          settings: settings,
          maintainState: maintainState,
          fullscreenDialog: fullscreenDialog,
        );
}

Vous pouvez maintenant utiliser WillPopScope et faire glisser vers l'arrière fonctionnera sur IOS. La réponse détaillée est ici: https://github.com/flutter/flutter/issues/14203#issuecomment-540663717

Nhật Trần
la source
1

Ajoutez simplement un conteneur au début de l'Appbar qui masque le bouton de retour.

AppBar(
        title: new Text('Page Title'),
        leading: new Container(),
      ),
Niroop Nife
la source