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()
),
),
)
),
)
);
}
}
StatelessWidgetにListenable/Streamをリッスンさせる明確な方法はありません。 alwaysにはStatefulWidgetが必要です。
一方、コンポジションを使用してそのStatefulWidgetを1回だけ記述すれば、それで完了できます。
そのパターンの一般的な例は、ValueListenableBuilder
、StreamBuilder
、AnimatedBuilder
などのウィジェットです。しかし、聞くためにも同じことをすることが可能です。
次のように使用します。
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;
}
}
あなたはこのようなことをすることができます:
_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()
)のように、ステートフルウィジェットにする必要があります。
ストリームをStatefulWidgetでインスタンス化し、オプションとしてStatelessWidgetsに渡すことができるので、子がストリームを使用してビューを更新する間、親ウィジェットはストリームのライフサイクルを制御する役割のみを持ちます。
以前の回答について:StreamBuilder自体はStatefulWidgetから拡張されたウィジェットであり、それ自体の状態を処理し、それ自体で正しく破棄するため、StatelessWidgets内でStreamBuildersを使用しても問題はありません。