web-dev-qa-db-ja.com

Flutter:StatelessWidgetでリスナーを追加する場所は?

StatefulWidgetを使用している場合、たとえばinitStateメソッド内でStreamをリッスンします。 StatelessWidgetで同等のものをどこで実行しますか(状態管理のためにストリームでBlocを使用するなど)?私はbuildメソッドでそれを行うことができましたが、これらは繰り返し行われるので、以下のような既存のリスナーを確認するよりも効率的な方法があるかどうか疑問に思いました。これは冗長で役に立たない例であることは知っていますが、問題を示すためだけのものです。

    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

すべきではない。値が変更されている可能性がある変数を処理しないことが、 ステートレス ウィジェットの目的です。

ステートレスウィジェットは決して変更されません。

[〜#〜] update [〜#〜]:これはFlutterの状態管理の概念を理解する上での問題だと思います。 Flutterチームによるこの 新しい推奨方法 は、いくつかの混乱を解消するはずです。

9
Willy

StatelessWidgetにListenable/Streamをリッスンさせる明確な方法はありません。 alwaysにはStatefulWidgetが必要です。

一方、コンポジションを使用してそのStatefulWidgetを1回だけ記述すれば、それで完了できます。

そのパターンの一般的な例は、ValueListenableBuilderStreamBuilderAnimatedBuilderなどのウィジェットです。しかし、聞くためにも同じことをすることが可能です。

次のように使用します。

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は次のように実装されています:

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

あなたはこのようなことをすることができます:

_class ExampleWidget extends StatelessWidget {
  bool _initialized = false;

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

    return Container();
  }
}
_

しかし、そうすべきではありません!実際には、IDEは警告を表示します。これは、_@immutable_としてマークされているため、ステートレスウィジェットを使用する方法ではないためです。ライフサイクルメソッドを使用する必要がある場合( initState())のように、ステートフルウィジェットにする必要があります。

0
user10481267

ストリームをStatefulWidgetでインスタンス化し、オプションとしてStatelessWidgetsに渡すことができるので、子がストリームを使用してビューを更新する間、親ウィジェットはストリームのライフサイクルを制御する役割のみを持ちます。

以前の回答について:StreamBuilder自体はStatefulWidgetから拡張されたウィジェットであり、それ自体の状態を処理し、それ自体で正しく破棄するため、StatelessWidgets内でStreamBuildersを使用しても問題はありません。

0
Cássio Sales