Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter: Where to add listeners in StatelessWidget?

If I were using a StatefulWidget, then I would be listening to a Stream for example inside the initState method. Where would I do the equivalent in a StatelessWidget (like to use Bloc with streams for state management)? I could do it in the build method but since these are repetitively I wondered if there is a more efficient way than checking for existent listeners like below. I know that this is a redundant and useless example but it's just to show the problem.

    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()
                  ),
                ),
              )
            ),
          )
        );
      }
    }
like image 796
footurist Avatar asked Feb 21 '19 01:02

footurist


People also ask

How do I use StatelessWidget?

To create a stateful widget in a flutter, use the createState() method. The stateful widget is the widget that describes part of a user interface by building a constellation of other widgets that represent a user interface more concretely. A stateful Widget means a widget that has a mutable state.

Can we use initState in stateless widget Flutter?

Because we all know for StatefulWidget we have initState() overridden callback to initialize certain values etc. But for Stateless Widget no option is given by default.


Video Answer


2 Answers

There is no clean way to have a StatelessWidget listen to a Listenable/Stream. You will always need a StatefulWidget.

On the other hand, you can use composition to write that StatefulWidget just once, and be done with it.

Common examples for that pattern are widgets such as ValueListenableBuilder, StreamBuilder, or AnimatedBuilder. But it is possible to do the same thing, for listening too.

You'd use it this way:

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(),
    );
  }
}

Where ValueListenableListener is implemented this way:

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;
  }
}
like image 67
Rémi Rousselet Avatar answered Oct 20 '22 00:10

Rémi Rousselet


You shouldn't. Not handling variables that might have their values modified is the very purpose of a Stateless widget:

A stateless widget never changes.

UPDATE: I think this is a problem of understanding Flutter's state management concepts. This new recommended way by the Flutter team should clear some confusions.

like image 11
Willy Avatar answered Oct 20 '22 00:10

Willy