Jak dezaktywować lub zastąpić przycisk „WSTECZ” Androida w aplikacji Flutter?

112

Czy istnieje sposób na dezaktywację przycisku Wstecz Androida na określonej stronie?

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

Na pageOne Mam przycisk, aby przejść do strony Dwa:

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

Mój problem polega na tym, że jeśli naciśnę strzałkę Wstecz u dołu ekranu Androida, wracam do strony pierwszej. Chciałbym, żeby ten przycisk w ogóle się nie pojawiał. Najlepiej, gdybym nie miał możliwości wyjścia z tego ekranu, chyba że użytkownik przykładowo przytrzyma palec na ekranie przez 5 sekund. (Próbuję napisać aplikację dla małych dzieci i chciałbym, aby tylko rodzice mogli opuścić dany ekran).

Jackpap
źródło

Odpowiedzi:

202

Odpowiedź jest WillPopScope. Zapobiegnie to wyświetlaniu strony przez system. Nadal będziesz mógł korzystaćNavigator.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
źródło
1
Dla tych, którzy chcą przechwycić pop formularza, wygodniej jest użyć właściwości Form onWillPop. Ma dostęp do stanu formularza i może najpierw sprawdzić, czy istnieje stan, którego użytkownik nie chciałby utracić.
Joe Lapp
Cześć @ RémiRousselet, Czy możesz mi pomóc w zarządzaniu stosem z tyłu, tak jakbym miał stos ekranów A, B, C, D i chcę nawigować z D-> B LUB D-> A, to jak sobie poradzę to. Czy możesz mnie w tym
poprowadzić
Wiem, że ta odpowiedź może być stara. Ale to klejnot! Pomyśl o rozszerzeniu wyjaśnienia. Działa znakomicie. Dziękuję Ci.
Val
1
Dla kogoś, kto chce coś zrobić przed pop (), może użyć tegoonWillPop: () async { onBack(); // "DO YOUR FUNCTION IS HERE WHEN POP" return false; }
Huy Tower
24

Jak zauważył Rémi Rousselet, WillPopScope zazwyczaj jest to najlepszy sposób. Jeśli jednak tworzysz widżet stanowy, który powinien reagować bezpośrednio na przycisk Wstecz, możesz użyć tego:

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

Uwaga: jestem autorem tego pakietu.

MarcG
źródło
Z całym szacunkiem, czy nie sądzisz, że lepiej pasuje to jako komentarz?
CopsOnRoad
25
@CopsOnRoad Nie, ponieważ nie jest to komentarz do innej odpowiedzi, a raczej zupełnie inny sposób rozwiązania tego samego problemu.
MarcG
To dobra alternatywa i mogłaby zostać zignorowana, gdyby była częścią komentarza.
abhijat_saxena
17

Chociaż odpowiedź Remiego jest prawidłowa, zwykle nie chcesz po prostu blokować przycisku Wstecz, ale chcesz, aby użytkownik potwierdził wyjście.

Możesz to zrobić w podobny sposób, uzyskując odpowiedź z okna dialogowego potwierdzenia, ponieważ onWillPopto przyszłość.

@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
źródło
Dlaczego ta odpowiedź jest przeciwna? Czy ktoś to wdraża i miał złe doświadczenia?
SamiAzar
Chcę przejść do poprzedniego ekranu, a raczej przejść do pierwszego ekranu po naciśnięciu przycisku Wstecz na urządzeniu. Nie potrzebuję takiego okna dialogowego. Co powinienem zrobić
LakshmiYogeshwaran
4

Publikuję to tutaj, na wypadek, gdyby ktoś to znalazł i chciałby znaleźć prosty przykład 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 Anuluj
źródło
Sam go wczoraj dosłownie użyłem. Pamiętaj, że jest to tylko dla Androida, Apple nie ma przycisku Wstecz. Chociaż możesz mi powiedzieć, co poszło nie tak, abym mógł to naprawić, a może działa tylko na moim konkretnym emulatorze. Nie aktualizowałem trzepotania od kilku tygodni, więc może to jest problem. Daj mi znać
Bryan Anuluj
@BryanCancel Twoja odpowiedź działa tylko wtedy, gdy nie masz jeszcze żadnych wymuszonych tras. Zobacz metodę WidgetsBinding.handlePopRoute (). Powiadamia obserwatorów w kolejności rejestracji i zatrzymuje się, gdy tylko otrzyma prawdę. W przypadku tras wypychanych nawigator najpierw zwraca wartość true, a następnie obserwator nigdy nie zostaje wywołany. Innymi słowy, twój kod działa tylko po to, aby zapobiec wyłączaniu aplikacji, gdy użytkownik kliknie przycisk Wstecz, gdy nie ma już żadnych tras.
MarcG,
3

Możesz użyć Future.value(bool)do obsługi przycisku Wstecz.

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
źródło
FYI, to jest równoważne z onWillPop: () async => _allow.
Lynn
@Lynn tak, wiem to. Chciałem tylko podzielić się Future.value()rozmową.
CopsOnRoad,
3

Użyłem mixin i widżetu WillPopScope po prostu nie mogłem wykonać tej pracy za mnie. To najlepsze podejście, które znalazłem, znacznie lepsze niż WillPopScope.
final bool canPop = ModalRoute.of(context)?.canPop ?? false;
Użyłem tego w następujący sposób na pasku aplikacji:

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
źródło
2

Odpowiedź może wiedziałeś, że to użycie WillPopScope , ale niestety na IOS nie możesz przesunąć palcem do poprzedniej strony, więc dostosujmy twoją 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,
        );
}

Teraz możesz użyć WillPopScope i przesunąć palcem do tyłu będzie działać na IOS. Odpowiedź na szczegóły jest tutaj: https://github.com/flutter/flutter/issues/14203#issuecomment-540663717

Nhật Trần
źródło
1

Po prostu dodaj kontener na początku paska aplikacji, który ukrywa przycisk Wstecz.

AppBar(
        title: new Text('Page Title'),
        leading: new Container(),
      ),
Niroop Nife
źródło