Come disattivare o ignorare il pulsante "INDIETRO" di Android, in Flutter?


102

C'è un modo per disattivare il pulsante Indietro di Android quando ci si trova su una pagina specifica?

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

Su pageOne ho un pulsante per andare a pageTwo:

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

Il mio problema è che se premo la freccia Indietro nella parte inferiore dello schermo di Android, torno a pageOne. Vorrei che questo pulsante non venisse visualizzato affatto. Idealmente, non vorrei avere alcuna via d'uscita da questa schermata a meno che l'utente, ad esempio, non tenga il dito premuto sullo schermo per 5 secondi. (Sto cercando di scrivere un'app per i più piccoli e vorrei che solo i genitori fossero in grado di uscire da quella schermata specifica).

Risposte:


188

La risposta è WillPopScope. Impedirà che la pagina venga spuntata dal sistema. Sarai comunque in grado di utilizzareNavigator.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(),
        ),
      ),
    ),
  );
}

1
Per coloro che desiderano intercettare il pop di un modulo, è più conveniente utilizzare la onWillPopproprietà del modulo . Ha accesso allo stato del modulo e può prima verificare se esiste uno stato che l'utente potrebbe non voler perdere.
Joe Lapp

Ciao @ RémiRousselet, puoi aiutarmi a gestire lo stack posteriore come se avessi uno stack di schermate A, B, C, D e voglio navigare da D-> B OR D-> A, quindi come gestirò esso. Puoi guidarmi per favore
Ravindra Kushwaha

Conosco questa risposta forse vecchia. Ma è un GEM! Pensa ad espandere la spiegazione. Funziona alla grande. Grazie.
Val

1
Per qualcuno che vuole fare qualcosa prima di pop (), può usare questoonWillPop: () async { onBack(); // "DO YOUR FUNCTION IS HERE WHEN POP" return false; }
Huy Tower,

21

Come ha sottolineato Rémi Rousselet, di WillPopScopesolito è la strada da percorrere. Tuttavia, se stai sviluppando un widget con stato che dovrebbe reagire direttamente al pulsante Indietro, puoi utilizzare questo:

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

Nota: sono l'autore di questo pacchetto.


Con tutto il rispetto, non pensi che questo sia più adatto come commento?
CopsOnRoad

21
@CopsOnRoad No, poiché non è un commento a un'altra risposta, ma piuttosto un modo completamente diverso per risolvere lo stesso problema.
MarcG

È una buona alternativa e avrebbe potuto essere ignorata se fosse stato parte di un commento.
abhijat_saxena

16

Sebbene la risposta di Remi sia giusta, di solito non vuoi semplicemente bloccare il pulsante Indietro ma vuoi che un utente confermi l'uscita.

Puoi farlo in modo simile ottenendo una risposta dalla finestra di dialogo di conferma, perché onWillPopè un futuro.

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

Perché questa risposta ottiene un voto negativo? Qualcuno lo implementa e ha avuto una brutta esperienza?
SamiAzar

Voglio andare alla schermata precedente piuttosto che alla prima schermata dopo aver premuto il pulsante Indietro dal dispositivo. Non ho bisogno di una finestra di dialogo come questa. Cosa dovrei fare
LakshmiYogeshwaran

4

Sto postando questo qui nel caso qualcuno là fuori lo trovi e desideri trovare un semplice esempio 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"),
              )
          )
      ),
    );
  }
}

L'ho letteralmente usato io stesso ieri. Tieni presente che è solo per Android, Apple non ha il pulsante Indietro. Anche se potresti forse dirmi cosa va storto in modo da poterlo riparare, o forse funziona solo sul mio particolare emulatore. Non ho aggiornato il mio flutter da un paio di settimane, quindi forse è questo il problema. Fammi sapere
Bryan Annulla il

@BryanCancel La tua risposta funziona solo se non hai ancora alcun percorso inviato. Vedere il metodo WidgetsBinding.handlePopRoute (). Avvisa gli osservatori in ordine di registrazione e si ferma non appena riceve true. Quando ci sono rotte inviate, il navigatore restituisce prima true, quindi il tuo osservatore non viene mai effettivamente chiamato. In altre parole, il codice funziona solo per impedire che l'applicazione si chiuda quando l'utente fa clic sul pulsante Indietro, quando non ci sono più percorsi rimasti.
MarcG

3

È possibile utilizzare Future.value(bool)per gestire il pulsante Indietro.

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

Cordiali saluti, questo è equivalente a onWillPop: () async => _allow.
Lynn,

@ Lynn, sì, lo so. Volevo solo condividere la Future.value()chiamata.
CopsOnRoad

3

Ho usato mixin e widget WillPopScope semplicemente non riuscivo a fare il lavoro per me. Questo è l'approccio migliore che ho trovato, molto meglio di WillPopScope secondo me.
final bool canPop = ModalRoute.of(context)?.canPop ?? false;
L'ho usato in questo modo all'interno dell'appbar:

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(),

2

La risposta forse sapevi che usa WillPopScope , ma sfortunatamente su IOS non puoi scorrere per tornare alla pagina precedente, quindi personalizziamo il tuo 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,
        );
}

Ora puoi usare WillPopScope e scorrere indietro funzionerà su IOS. La risposta dei dettagli è qui: https://github.com/flutter/flutter/issues/14203#issuecomment-540663717


1

Basta aggiungere un contenitore nella parte iniziale della Appbar che nasconde il pulsante Indietro.

AppBar(
        title: new Text('Page Title'),
        leading: new Container(),
      ),
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.