web-dev-qa-db-fra.com

Forcer le navigateur Flutter à recharger son état lors du popping

J'ai un StatefulWidget dans Flutter avec le bouton, qui me dirige vers un autre StatefulWidget en utilisant Navigator.Push(). Sur le deuxième widget, je change d'état global (certaines préférences de l'utilisateur). Quand je reviens du second widget au premier, en utilisant Navigator.pop(), le premier widget est à l’état ancien, mais je veux forcer le rechargement. Une idée comment faire ça? J'ai une idée mais ça a l'air moche:

  1. pop pour supprimer le deuxième widget (le dernier)
  2. pop à nouveau pour supprimer le premier widget (précédent)
  3. Poussez premier widget (il devrait forcer redessiner)
8
bartektartanus

Il y a plusieurs choses que vous pouvez faire ici. La réponse de @ Mahi, bien que correcte, pourrait être un peu plus succincte et utiliser réellement Push plutôt que showDialog comme le demandait le PO. Ceci est un exemple qui utilise Navigator.Push:

import 'package:flutter/material.Dart';

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Container(
      color: Colors.green,
      child: new Column(
        children: <Widget>[
          new RaisedButton(
            onPressed: () => Navigator.pop(context),
            child: new Text("back"),
          ),
        ],
      ),
    );
  }
}

class FirstPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new FirstPageState();
}

class FirstPageState extends State<FirstPage> {

  Color color = Colors.white;

  @override
  Widget build(BuildContext context) {
    return new Container(
      color: color,
      child: new Column(
        children: <Widget>[
          new RaisedButton(
              child: new Text("next"),
              onPressed: () {
                Navigator
                    .Push(
                  context,
                  new MaterialPageRoute(builder: (context) => new SecondPage()),
                )
                    .then((value) {
                  setState(() {
                    color = color == Colors.white ? Colors.grey : Colors.white;
                  });
                });
              }),
        ],
      ),
    );
  }
}

void main() => runApp(
      new MaterialApp(
        builder: (context, child) => new SafeArea(child: child),
        home: new FirstPage(),
      ),
    );

Cependant, il existe une autre façon de procéder qui pourrait bien convenir à votre cas d'utilisation. Si vous utilisez la variable global qui affecte la construction de votre première page, vous pouvez utiliser un InheritedWidget pour définir vos préférences utilisateur globales et chaque fois qu'elles seront modifiées, votre FirstPage sera reconstruite. Cela fonctionne même dans un widget sans état, comme indiqué ci-dessous (mais devrait également fonctionner dans un widget avec état).

Le thème de l'application est un exemple d'héritage-tour en mouvement, bien qu'ils le définissent dans un widget au lieu de le construire directement comme je l'ai fait ici.

import 'package:flutter/material.Dart';
import 'package:meta/meta.Dart';

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Container(
      color: Colors.green,
      child: new Column(
        children: <Widget>[
          new RaisedButton(
            onPressed: () {
              ColorDefinition.of(context).toggleColor();
              Navigator.pop(context);
            },
            child: new Text("back"),
          ),
        ],
      ),
    );
  }
}

class ColorDefinition extends InheritedWidget {
  ColorDefinition({
    Key key,
    @required Widget child,
  }): super(key: key, child: child);

  Color color = Colors.white;

  static ColorDefinition of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(ColorDefinition);
  }

  void toggleColor() {
    color = color == Colors.white ? Colors.grey : Colors.white;
    print("color set to $color");
  }

  @override
  bool updateShouldNotify(ColorDefinition oldWidget) =>
      color != oldWidget.color;
}

class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var color = ColorDefinition.of(context).color;

    return new Container(
      color: color,
      child: new Column(
        children: <Widget>[
          new RaisedButton(
              child: new Text("next"),
              onPressed: () {
                Navigator.Push(
                  context,
                  new MaterialPageRoute(builder: (context) => new SecondPage()),
                );
              }),
        ],
      ),
    );
  }
}

void main() => runApp(
      new MaterialApp(
        builder: (context, child) => new SafeArea(
              child: new ColorDefinition(child: child),
            ),
        home: new FirstPage(),
      ),
    );

Si vous utilisez un widget hérité, vous n'avez pas à vous soucier de l'affichage de la page que vous avez insérée, ce qui fonctionne pour les cas d'utilisation de base, mais risque de poser des problèmes dans un scénario plus complexe.

7
rmtmckenzie

Vous pouvez renvoyer un dynamic result lorsque vous ouvrez le contexte, puis appeler la setState((){}) lorsque la valeur est true, sinon laissez simplement l'état tel qu'il est.

J'ai collé des extraits de code pour votre référence.

handleClear() async {
    try {
      var delete = await deleteLoanWarning(
        context,
        'Clear Notifications?',
        'Are you sure you want to clear notifications. This action cannot be undone',
      );
      if (delete.toString() == 'true') {
        //call setState here to rebuild your state.

      }
    } catch (error) {
      print('error clearing notifications' + error.toString());
             }
  }



Future<bool> deleteLoanWarning(BuildContext context, String title, String msg) async {

  return await showDialog<bool>(
        context: context,
        child: new AlertDialog(
          title: new Text(
            title,
            style: new TextStyle(fontWeight: fontWeight, color: CustomColors.continueButton),
            textAlign: TextAlign.center,
          ),
          content: new Text(
            msg,
            textAlign: TextAlign.justify,
          ),
          actions: <Widget>[
            new Container(
              decoration: boxDecoration(),
              child: new MaterialButton(
                child: new Text('NO',),
                onPressed: () {
                  Navigator.of(context).pop(false);
                },
              ),
            ),
            new Container(
              decoration: boxDecoration(),
              child: new MaterialButton(
                child: new Text('YES', ),
                onPressed: () {
                  Navigator.of(context).pop(true);
                },
              ),
            ),
          ],
        ),
      ) ??
      false;
}

Cordialement, Mahi

1
Mahi

Aujourd'hui, j'ai fait face à la même situation, mais j'ai réussi à la résoudre de manière beaucoup plus simple. Je viens de définir une variable globale utilisée dans la première classe avec état, et lorsque je navigue vers un second widget avec état, je le laisse mettre à jour la valeur de l'objet global variable qui force automatiquement le premier widget à se mettre à jour. Voici un exemple (je l'ai écrit à la hâte, je n'ai donc pas installé d'échafaudage ni d'application matérielle, je voulais simplement illustrer mon propos): 

import 'package:flutter/material.Dart';
int count = 0 ;

class FirstPage extends StatefulWidget {
FirstPage({Key key}) : super(key: key);

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

class _FirstPageState extends State<FirstPage> {
@override
Widget build(BuildContext context) {
return InkWell(
onTap(){
Navigator.of(context).Push(MaterialPageRoute(builder: (context) =>
                  new SecondPage());
},
child: Text('First', style : TextStyle(fontSize: count == 0 ? 20.0 : 12.0)),
),

}


class SecondPage extends StatefulWidget {
SecondPage({Key key}) : super(key: key);

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

class _SecondPageState extends State<SecondPage> {
@override
Widget build(BuildContext context) {
return IconButton(
         icon: new Icon(Icons.add),
         color: Colors.amber,
         iconSize: 15.0,
         onPressed: (){
         count++ ;
         },
       ),
     }
0
Mazin Ibrahim