web-dev-qa-db-fra.com

Flutter: où ajouter des écouteurs dans StatelessWidget?

Si j'utilisais un StatefulWidget, j'écouterais un Stream par exemple à l'intérieur de la méthode initState. Où pourrais-je faire l'équivalent dans un StatelessWidget (comme utiliser Bloc avec des flux pour la gestion de l'État)? Je pourrais le faire dans la méthode de construction, mais comme ce sont des répétitions, je me suis demandé s'il y avait un moyen plus efficace que de vérifier les écouteurs existants comme ci-dessous. Je sais que c'est un exemple redondant et inutile mais c'est juste pour montrer le problème.

    import "package:rxdart/rxdart.Dart";

    import 'package:flutter/material.Dart';

    final counter = BehaviorSubject<int>();
    final notifier = ValueNotifier<int>(0);

    void main() => runApp(MyApp());

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        if (!counter.hasListener)
          counter.listen((value) => notifier.value += value);  

        return MaterialApp(
          home: Scaffold(
            body: Center(
              child:FlatButton(
                onPressed: () => counter.add(1),
                child: ValueListenableBuilder(
                  valueListenable: notifier,
                  builder: (context, value, child) => Text(
                    value.toString()
                  ),
                ),
              )
            ),
          )
        );
      }
    }
14
footurist

Tu ne devrais pas. Ne pas gérer les variables dont la valeur pourrait être modifiée est le but même d'un widget Stateless :

Un widget sans état ne change jamais.

[~ # ~] mise à jour [~ # ~] : Je pense que c'est un problème de compréhension des concepts de gestion d'état de Flutter. Cette nouvelle méthode recommandée par l'équipe Flutter devrait effacer certaines confusions.

9
Willy

Il n'y a aucun moyen propre d'avoir un StatelessWidget écouter un Listenable/Stream. Vous aurez toujours besoin d'un StatefulWidget.

D'un autre côté, vous pouvez utiliser la composition pour écrire ce StatefulWidget une seule fois et en finir avec.

Des exemples courants pour ce modèle sont des widgets tels que ValueListenableBuilder, StreamBuilder ou AnimatedBuilder. Mais il est possible de faire la même chose, pour écouter aussi.

Vous l'utiliseriez de cette façon:

class Foo extends StatelessWidget {
  Foo({Key key, this.counter}): super(key: key);

  final ValueListenable<int> counter;

  @override
  Widget build(BuildContext context) {
    return ValueListenableListener(
      valueListenable: counter,
      onChange: (value) {
        // TODO: do something
      },
      child: Something(),
    );
  }
}

ValueListenableListener est implémenté de cette façon:

class ValueListenableListener<T> extends StatefulWidget {
  const ValueListenableListener(
      {Key key, this.valueListenable, this.onChange, this.child})
      : super(key: key);

  final ValueListenable<T> valueListenable;
  final ValueChanged<T> onChange;
  final Widget child;

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

class _ValueListenableListenerState extends State<ValueListenableListener> {
  @override
  void initState() {
    super.initState();
    widget.valueListenable?.addListener(_listener);
    _listener();
  }

  @override
  void didUpdateWidget(ValueListenableListener oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.valueListenable != widget.valueListenable) {
      oldWidget.valueListenable?.removeListener(_listener);
      widget.valueListenable?.addListener(_listener);
      _listener();
    }
  }

  @override
  void dispose() {
    widget.valueListenable?.removeListener(_listener);
    super.dispose();
  }

  void _listener() {
    widget.onChange?.call(widget.valueListenable.value);
  }

  @override
  Widget build(BuildContext context) {
    return widget.child;
  }
}
3
Rémi Rousselet

Vous pouvez faire quelque chose comme ça:

class ExampleWidget extends StatelessWidget {
  bool _initialized = false;

  @override
  Widget build(BuildContext context) {
    if (!_initialized) {
      _initialized = true;
      // Add listeners here only once
    }

    return Container();
  }
}

Mais tu ne devrais pas! En fait, votre IDE vous donnera un avertissement, car ce n'est pas la voie à suivre avec le widget Stateless car il est marqué comme @immutable. Si vous devez utiliser des méthodes de cycle de vie (comme initState()) vous devriez en faire un widget avec état. Il n'y a pas grand-chose.

0
user10481267

Vous pouvez faire en sorte que vos flux soient instanciés dans un StatefulWidget, puis transmis à vos StatelessWidgets en option, de sorte que le widget parent n'aurait qu'un rôle de contrôle du cycle de vie du flux tandis que l'enfant utiliserait le flux pour mettre à jour la vue.

Concernant la réponse précédente: Il n'y a aucun problème à utiliser StreamBuilders dans vos StatelessWidgets puisque le StreamBuilder lui-même est un Widget qui s'étend de StatefulWidget et prendra soin de son propre état et se débarrassera correctement de lui-même.

0
Cássio Sales