90 votes

Appeler une méthode dans un widget Stateful depuis un autre widget Stateful - Flutter

J'ai un projet flutter sur lequel je travaille, je ne peux pas mettre tout le code car il dépasse les 500 lignes, donc je vais essayer de poser ma question aussi simplement que possible en utilisant la section imp. du code.

Je suis en train d'avoir un widget étatique et quelques fonctions à l'intérieur de ce widget étatique, sous la classe qui étend State

fichier lib\main.dart

prenons simplement une fonction comme

class MyAppState extends State{
...
void printSample (){
  print("Texte d'exemple");
}
...

cette fonction est à l'intérieur du widget étatique à l'intérieur de la classe principale.

Il y a un autre fichier lib\MyApplication.dart

ce fichier a également un widget étatique, puis-je faire quelque chose pour pouvoir appeler la fonction printSample() ici..

class MyApplicationState extends State{
...
@override
  Widget build(BuildContext context) {
    return new FlatButton(
      child: new Text("Imprimer un exemple de texte"),
      onPressed :(){
       // je veux appeler la fonction ici, comment est-il possible d'appeler 
       // la fonction 
       // printSample()  d'ici??  
      }
    );
  }
...
}

0 votes

0 votes

Si vous pouvez vous assurer qu'un widget est un descendant d'un autre, vous pouvez utiliser InheritedWidget.

0 votes

@JacobPhillips Utilisez simplement context.ancestorStateOfType dans cette situation.

111voto

boformer Points 8206

Pour appeler une fonction d'un parent, vous pouvez utiliser le schéma de rappel. Dans cet exemple, une fonction (onColorSelected) est passée à l'enfant. L'enfant appelle la fonction lorsqu'un bouton est pressé :

import 'package:flutter/material.dart';

class Parent extends StatefulWidget {
  @override
  State createState() {
    return ParentState();
  }
}

class ParentState extends State {
  Color selectedColor = Colors.grey;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Container(
          color: selectedColor,
          height: 200.0,
        ),
        ColorPicker(
          onColorSelect: (Color color) {
            setState(() {
              selectedColor = color;
            });
          },
        )
      ],
    );
  }
}

class ColorPicker extends StatelessWidget {
  const ColorPicker({this.onColorSelect});

  final ColorCallback onColorSelect;

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        RaisedButton(
          child: Text('rouge'),
          color: Colors.red,
          onPressed: () {
            onColorSelect(Colors.red);
          },
        ),
        RaisedButton(
          child: Text('vert'),
          color: Colors.green,
          onPressed: () {
            onColorSelect(Colors.green);
          },
        ),
        RaisedButton(
          child: Text('bleu'),
          color: Colors.blue,
          onPressed: () {
            onColorSelect(Colors.blue);
          },
        )
      ],
    );
  }
}

typedef ColorCallback = void Function(Color color);

Les widgets internes de Flutter comme les boutons ou les champs de formulaire utilisent exactement le même schéma. Si vous voulez simplement appeler une fonction sans arguments, vous pouvez utiliser le type VoidCallback au lieu de définir votre propre type de rappel.


Si vous souhaitez notifier un parent plus haut, vous pouvez simplement répéter ce schéma à chaque niveau de hiérarchie :

class ColorPickerWrapper extends StatelessWidget {
  const ColorPickerWrapper({this.onColorSelect});

  final ColorCallback onColorSelect;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(20.0),
      child: ColorPicker(onColorSelect: onColorSelect),
    )
  }
}

Appeler une méthode d'un widget enfant à partir d'un widget parent est déconseillé dans Flutter. À la place, Flutter vous encourage à transmettre l'état d'un enfant en tant que paramètres de constructeur. Au lieu d'appeler une méthode de l'enfant, vous appelez simplement setState dans le widget parent pour mettre à jour ses enfants.


Une approche alternative sont les classes controller dans Flutter (ScrollController, AnimationController, ...). Celles-ci sont également passées aux enfants en tant que paramètres de constructeur, et elles contiennent des méthodes pour contrôler l'état de l'enfant sans appeler setState sur le parent. Exemple:

scrollController.animateTo(200.0, duration: Duration(seconds: 1), curve: Curves.easeInOut);

Les enfants doivent alors écouter ces changements pour mettre à jour leur état interne. Bien sûr, vous pouvez également implémenter votre propre classe de contrôleur. Si nécessaire, je vous recommande de consulter le code source de Flutter pour comprendre comment cela fonctionne.


Les futurs et les flux sont une autre alternative pour transmettre l'état, et pourraient également être utilisés pour appeler une fonction d'un enfant.

Mais je ne le recommande vraiment pas. Si vous devez appeler une méthode d'un widget enfant, il est très probable que l'architecture de votre application est défectueuse. Essayez de remonter l'état jusqu'à l'ancêtre commun !

1 votes

"Appeler une méthode d'un widget enfant à partir d'un widget parent est déconseillée" est-ce déconseillée même lorsqu'elle est appelée via une GlobalKey?

3 votes

Un peu compliqué !!

0 votes

Oui, merci! ceci a été un sauveur de vie. J'avais une grande boîte de dialogue appelée à partir d'un bouton d'action flottant, avec beaucoup de menus déroulants locaux définis et je ne voulais PAS copier/coller cela mais je voulais le même bouton d'action flottant dans la page enfant étatique aussi. C'était exactement ce que je cherchais.

43voto

Vous pouvez le faire en utilisant la clé du widget

myWidget.dart

    class MyWidget extends StatefulWidget {

      const MyWidget ({Key key}) : super(key: key);

      @override
      State createState()=> MyState();

    }

   class MyState extends State{

      Widget build(BuildContext context){ return ....}

      void printSample (){
         print("Texte d'exemple");
      }

 }

maintenant lorsque vous utilisez MyWidget, déclarez GlobalKey en tant que clé globale

  GlobalKey _myKey = GlobalKey();

et passez-le lors de la création du widget

MyWidget(
key : _myKey,
)

avec cette clé, vous pouvez appeler n'importe quelle méthode publique à l'intérieur de l'état

_myKey.currentState.printSample();

0 votes

N'avez-vous pas voulu dire _myKey.currentState.printSample();?

0 votes

Le seul effet secondaire des GlobalKeys est que Flutter s'efforce de garder le widget dans l'arbre, même si vous préféreriez qu'il soit supprimé. En d'autres termes : les GlobalKeys rendent les widgets plus difficiles à éliminer, ce que résout ValueKey/ObjectKey, mais alors vous revenez là où vous avez commencé.

16voto

Si vous voulez appeler la fonction printSample(), vous pouvez utiliser :

class Myapp extends StatefulWidget{
...
    MyappState myAppState=new MyappState();
    @override
    MyappState createState() => myAppState;
    void printSample(){
        myAppState.printSample();
    }
}
class MyAppState extends State{
    void printSample (){
        print("Texte d'exemple");
    }
}

...............
Myapp _myapp = new Myapp();
myapp.printSample();
...

6 votes

Je ne pense pas que cela soit valable pour un cas plus complexe : le StatefulWidget Myapp peut être construit de manière répétée et donc l'état que détient une instance donnée peut ne pas être le même objet d'état que Flutter détient et associe à l'arbre, n'est-ce pas ?

0 votes

Bonjour @PatNiemeyer, Est-ce que c'est correct? _StatefulButtonState _lastState; //remplacer État createState() { _lastState = new _StatefulButtonState(onPressed: onPressed); return _lastState; }

7voto

mn128b Points 109

J'ai trouvé une autre solution par essais et erreurs, mais ça a fonctionné.

import 'main.dart' as main;

Ensuite, ajoutez cette ligne sous onPressed.

main.MyAppState().printSample();

3 votes

C'est un anti-pattern, vous devriez remonter l'état.

2 votes

Ici, vous instanciez un nouvel objet state, ce qui est totalement incorrect.

0 votes

@DarkNeuron vous êtes ouvert à le corriger.

7voto

CopsOnRoad Points 4705

Vous pouvez essayer ceci, cela appellera une méthode définie dans Page2 (StatefulWidget) depuis le widget Page1 (StatefulWidget).

class Page1 extends StatefulWidget {
  @override
  _Page1State createState() => _Page1State();
}

class _Page1State extends State {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RaisedButton(
          child: Text("Appeler la méthode de la page 2"),
          onPressed: () => Page2().method(),
        ),
      ),
    );
  }
}

class Page2 extends StatefulWidget {
  method() => createState().methodInPage2();

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

class _Page2State extends State {
  methodInPage2() => print("méthode dans la page 2");

  @override
  Widget build(BuildContext context) => Container();
}

2 votes

Cette réponse est exactement la même que la réponse de Mehmet Akif BAYSAL (le concept est à 100% le même). La réponse de Mehmet Akif BAYSAL est simplement plus élégamment illustrée dans l'exemple de code et donc beaucoup plus facile à comprendre.

0 votes

Si je veux appeler une méthode dans un bloc, quelle est la bonne manière? stackoverflow.com/questions/62097660/…

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X