74 votes

Le contrôle de l'État de l'extérieur d'un StatefulWidget

J'essaie de comprendre les meilleures pratiques pour le contrôle d'un StatefulWidget de l'état en dehors de Widgets de l'État.

J'ai l'interface suivante définie.

abstract class StartupView {
  Stream<String> get onAppSelected;

  set showActivity(bool activity);
  set message(String message);
}

Je voudrais créer un StatefulWidget StartupPage qui implémente cette interface. J'attends le Widget pour effectuer les opérations suivantes:

  1. Quand un bouton est pressé d'envoyer un événement au cours de la onAppSelected flux. Un contrôleur à écouter ce même et d'effectuer une action ( db appel, la demande de service, etc ).

  2. Le contrôleur peut appeler showActivity ou set message d'avoir le point de vue de montrer les progrès avec un message.

Parce que avec un état de Widget ne pas exposer l'État de la propriété, je ne sais pas la meilleure approche pour l'accès et la modification de l'État des attributs.

La façon dont je m'attends à utiliser ce serait quelque chose comme:

Widget createStartupPage() {
    var page = new StartupPage();
    page.onAppSelected.listen((app) {
      page.showActivity = true;
      //Do some work
      page.showActivity = false;
    });
  }

J'ai pensé à l'instanciation de la Widget en passant dans l'état que je veux revenir en createState() mais qui se sent mal.

Quelques informations sur la raison pour laquelle cette approche: Nous avons actuellement une Fléchette de l'application web. Pour la vue-contrôleur de la séparation, de la testabilité et de la réflexion en direction de Flottement, nous avons décidé de créer une interface pour chaque vue de notre application. Cela permettrait une WebComponent ou un Flutter Widget pour implémenter cette interface et de laisser l'ensemble de la logique du contrôleur de même.

125voto

Rémi Rousselet Points 45139

Il existe de multiples façons d'interagir avec d'autres stateful widgets.

1. ancestorStateOfType

La première et la plus simple est par context.ancestorStateOfType méthode.

Généralement emballés dans une méthode statique de la Stateful sous-classe comme ceci :

class MyState extends StatefulWidget {
  static of(BuildContext context, {bool root = false}) => root
      ? context.rootAncestorStateOfType(const TypeMatcher<_MyStateState>())
      : context.ancestorStateOfType(const TypeMatcher<_MyStateState>());

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

class _MyStateState extends State<MyState> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

C'est combien de Navigator fonctionne par exemple.

Pro:

  • Solution la plus simple

Con:

  • Tenté d'accéder State propriétés ou manuellement appel setState
  • Nécessite d'exposer State sous-classe

N'utilisez pas cette méthode si vous voulez accéder à une variable. En tant que votre gadget peut pas recharger lorsque ce changement de variable.

2. Écoutable, diffuser et/ou InheritedWidget

Parfois, au lieu d'une méthode, vous pouvez accéder à certaines propriétés. La chose est, vous avez probablement voulez que vos widgets à mettre à jour chaque fois que la valeur change au cours du temps.

Dans cette situation, dart offrent Stream et Sink. Et flutter ajoute sur le haut de il InheritedWidget et Listenable comme ValueNotifier. Elles le sont toutes relativement la même chose: s'abonner à un événement de changement de valeur lorsqu'il est couplé avec un StreamBuilder/context.inheritFromWidgetOfExactType/AnimatedBuilder.

C'est la solution lorsque vous voulez que votre State d'exposer certaines propriétés. Je ne vais pas couvrir toutes les possibilités, mais voici un petit exemple d'utilisation InheritedWidget :

Tout d'abord, nous avons une InheritedWidget qui exposent un count :

class Count extends InheritedWidget {
  static of(BuildContext context) =>
      context.inheritFromWidgetOfExactType(Count);

  final int count;

  Count({Key key, @required Widget child, @required this.count})
      : assert(count != null),
        super(key: key, child: child);

  @override
  bool updateShouldNotify(Count oldWidget) {
    return this.count != oldWidget.count;
  }
}

Ensuite, nous avons nos State que instancier cette InheritedWidget

class _MyStateState extends State<MyState> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Count(
      count: count,
      child: Scaffold(
        body: CountBody(),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            setState(() {
              count++;
            });
          },
        ),
      ),
    );
  }
}

Enfin, nous avons notre CountBody qui atteignent ce exposées count

class CountBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(Count.of(context).count.toString()),
    );
  }
}

Pour:

  • Plus performant qu' ancestorStateOfType
  • Flux alternatif est dart seulement (fonctionne avec web) et est fortement intégré dans la langue (mots-clés comme await for ou async*)
  • Automic rechargement des enfants lors de la variation de la valeur

Inconvénients:

  • Plus passe-partout
  • Cours d'eau peut être compliqué

3. Les Notifications

Au lieu de directement de l'appel de méthodes sur State, vous pouvez envoyer un Notification de votre widget. Et faire State abonnez-vous à ces notifications.

Un exemple d' Notification serait :

class MyNotification extends Notification {
  final String title;

  const MyNotification({this.title});
}

Pour envoyer la notification appelez simplement dispatch(context) sur votre notification de l'instance et il vont remonter.

MyNotification(title: "Foo")..dispatch(context)

Note: vous devez vous mettre au-dessus de la ligne de code à l'intérieur d'une classe, sinon, pas de contexte, ne peut PAS de notification d'appel.

Tout widget peut écouter les notifications envoyés par leurs enfants à l'aide de NotificationListener<T> :

class _MyStateState extends State<MyState> {
  @override
  Widget build(BuildContext context) {
    return NotificationListener<MyNotification>(
      onNotification: onTitlePush,
      child: Container(),
    );
  }

  bool onTitlePush(MyNotification notification) {
    print("New item ${notification.title}");
    // true meaning processed, no following notification bubbling.
    return true;
  }
}

Un exemple serait l' Scrollable, qui peut envoyer ScrollNotification y compris de début/fin/overscroll. Ensuite utilisée par Scrollbar savoir faire défiler des informations sans avoir accès à l' ScrollController

Pour:

  • Cool réactives de l'API. Nous n'avons pas directement de faire des trucs sur State. Il est State qui s'abonne aux événements déclenchés par ses enfants
  • Plus d'un widget pouvez vous abonner à la même notification
  • Empêche les enfants d'accéder à des indésirables State propriétés

Inconvénients:

  • Peut ne pas convenir à votre cas d'utilisation
  • Nécessite plus passe-partout

49voto

xqwzts Points 1623

Vous pouvez exposer l'état du widget avec une méthode statique, un peu de flottement exemples le faire de cette façon et j'ai commencé à l'utiliser ainsi:

class StartupPage extends StatefulWidget {
  static _StartupPageState of(BuildContext context) => context.ancestorStateOfType(const TypeMatcher<_StartupPageState>());

  @override
  _StartupPageState createState() => new _StartupPageState();
}

class _StartupPageState extends State<StartupPage> {
  ...
}

Vous pouvez ensuite accéder à l'état en appelant StartupPage.of(context).doSomething();.

L'inconvénient ici est que vous devez avoir un BuildContext avec cette page quelque part dans son arbre.

0voto

Nicolás Carrasco Points 1495

Avez-vous pensé à la levée de l'état pour le parent du widget? C'est une commune, bien que moins idéal que Redux, façon de gérer l'état à Réagir comme je le sais, et ce référentiel indique comment appliquer le concept à un Flottement de l'app.

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