web-dev-qa-db-fra.com

Flutter Navigator.pop (contexte) renvoyant un écran noir

La structure de mon projet Flutter est la suivante

Main() //Run App with MaterialApp and Routes
L HomePage() //Default route (/), with BottomNavigation
  L MoviesPage() //Default BottomNavigation Index and shows a list of movies form TMDB 
    L DetailsPage()
  L SeriesPage()
  L SupportPage()

Après avoir cliqué sur n'importe quel film, il accède à DetailsPage (), mais lorsque j'appelle Navigator.pop à partir de DetailsPage (), il devrait revenir à l'écran précédent, mais ce n'est pas le cas.

Le Navigator.canPop (contexte) renvoie false Mais le bouton de retour matériel fonctionne parfaitement, alors comment puis-je le corriger?

main.Dart

class BerryMain extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
      home: Inferno(
        {
          '/': (context, argumets) => HomePage(),
          '/detailspage': (context, arguments) => DetailsPage(arguments),
        },
      ).home(context),
    );
  }
}

page d'accueil

class HomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return _HomePageState();
  }
}

class _HomePageState extends State<HomePage> {
  int _currentIndex = 0;
  final List<Widget> _childnav = [MoviesPage(), SeriesPage(), SupportPage()];

  void onTabPressed(...)

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text('...'),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.search),
            onPressed: () {},
          )
        ],
      ),
      body: _childnav[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        onTap: onTabPressed,
        currentIndex: _currentIndex, //this property defines current active tab
        items: [
          BottomNavigationBarItem(
              icon: Icon(Icons.movie), title: Text('Movies')),
          BottomNavigationBarItem(icon: Icon(Icons.tv), title: Text('Series')),
          BottomNavigationBarItem(icon: Icon(Icons.help), title: Text('Help'))
        ],
      ),
    );
  }
}

MoviesPage

//Inside ListView Builder
Virgil.pushNamed(context, '/detailspage', arguments: args);

DétailsPage

//Inside MaterialApp > Scaffold > SliverAppbar > BackButton
Navigator.pop(context)

J'utilise Virgil mais je ne pense pas que ce soit le problème.

8
Nadeem Siddique

Cela peut se produire si votre MoviesPage a un autre widget MaterialApp. La plupart du temps, vous souhaitez utiliser Scaffold comme widget supérieur pour une nouvelle page/écran, mais si vous utilisez accidentellement MaterialApp à la place, rien ne vous avertit.

Ce qui se passe, c'est que MaterialApp crée un nouveau Navigator, donc si vous passez d'une page avec MaterialApp à une autre, vous avez maintenant deux Navigateurs dans l'arborescence des widgets.

L'appel Navigator.of(context) recherche le navigateur le plus proche, il utilisera donc celui nouvellement créé dans votre MoviesPage. Comme l'historique de vos transitions d'itinéraire est stocké dans un premier navigateur, celui-ci ne peut pas revenir en arrière - il a un historique d'itinéraire vide.

Par conséquent, l'écran noir.

Bref, pour résoudre ce problème, utilisez simplement Scaffold comme widget supérieur au lieu de MaterialApp dans tous les écrans imbriqués.

27
divan

Je le résous en utilisant ce code:

Navigator.pushReplacementNamed(context, '/route')
4
Sunny

J'ai eu un problème très similaire et ma solution a été de passer BuildContext context du StatelessWidget créé à une variable globale et de faire apparaître ce contexte à partir de la variable globale. Alors...

var globalContext; // Declare global variable to store context from StatelessWidget

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    globalContext = context; // globalContext receives context from StetelessWidget.

    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        canvasColor: Colors.transparent,
        primarySwatch: Colors.blue,
      ),
      home: SecondScreenPage(title: 'SECOND SCREEN'),
    );
  }
}

class SecondScreenPage extends StatefulWidget {
  SecondScreenPage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _SecondScreenPageState createState() => _SecondScreenPageState();
}

class _SecondScreenPageState extends State<SecondScreenPage>() {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Screen"),
        leading: IconButton(
          icon: Icon(Icons.arrow_back),
          onPressed: () => Navigator.pop(globalContext), // POPPING globalContext
        )
      ),
  ...
}

Maintenant, je n'ai pas d'écran noir et je suis sur ma première page comme si j'avais cliqué sur le bouton de retour d'Android. =)

Je ne sais pas si c'est une bonne pratique. Si quelqu'un connaît un moyen de le faire "plus correctement", n'hésitez pas à répondre.

1

Selon cela Réponse J'ai trouvé un moyen.

Vous devez passer le contexte MoviesPage au DetailsPage. Et ce contexte doit être appelé dans Navigator.pop(context);

par exemple: PREMIER ÉCRAN

class FirstScreen extends StatelessWidget {
      final BuildContext context;

      FirstScreen(this.context);///here u have to call the build Context of FirstScreen

      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
            home: FirstScreenScreen(this.context));
      }
    }

    class FirstScreenScreen extends StatefulWidget {
      final BuildContext context;

      FirstScreenScreen(this.context);/// and here also.

      @override
      _FirstScreenState createState() => _FirstScreenState();
    }

    class _FirstScreenState extends State<FirstScreenScreen> {
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
     body: Container(
            child: new RaisedButton(onPressed: (){

               navigateAndDisplaySelection(context);

            },
            child: new Text('Go to SecondPage'),
            ),
          ),
       );
     }
    }

DEUXIÈME ÉCRAN

 class SecondScreen extends StatelessWidget {
      final BuildContext context;

      SecondScreen(this.context);///here u have to call the build Context of FirstScreen

      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
            home: SecondScreenScreen(this.context));
      }
    }

    class SecondScreenScreen extends StatefulWidget {
      final BuildContext context;

      SecondScreenScreen(this.context);/// and here also.

      @override
      _SecondScreenState createState() => _SecondScreenState();
    }

    class _SecondScreenState extends State<SecondScreenScreen> {
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
     body: Container(
            child: new RaisedButton(onPressed: (){
              Navigator.pop(widget.context);///this context is passed from firstScreen
            },
            child: new Text('Go Back'),
            ),
          ),
       );
     }
    }

Données de passe et retour
si vous voulez transmettre des données et retourner, ajoutez un constructeur avec un paramètre dans SecondScreen et appelez setState dans la méthode Fisrtlike ceci:

 navigateAndDisplaySelection(
      BuildContext context) async {
    // Navigator.Push returns a Future that will complete after we call
    // Navigator.pop on the Second Screen!
    Navigator.Push(
      context,
      MaterialPageRoute(
          builder: (context) => new SecondScreen(context)),
    );
    setState(() {
     /// the passed value data will change here, when you change in second screen
    });
  }
1

J'ai eu des problèmes similaires lors de l'affichage des vues, la façon dont j'ai corrigé était au lieu de Navigator.of(context).pop(); J'utilise Navigator.of(context).maybePop();.

Plus d'écrans noirs après ça.

0
Renan Nery