Skip to content

Instantly share code, notes, and snippets.

@MariaMelnik
Created August 11, 2020 15:49
Show Gist options
  • Save MariaMelnik/3f2ca839b39191e0a61e0bbf064feed4 to your computer and use it in GitHub Desktop.
Save MariaMelnik/3f2ca839b39191e0a61e0bbf064feed4 to your computer and use it in GitHub Desktop.
import 'dart:async';
import 'package:flutter/material.dart';
abstract class MyStreamBuilderBase<T, S> extends StatefulWidget {
/// Creates a [MyStreamBuilderBase] connected to the specified [stream].
const MyStreamBuilderBase({ Key key, this.stream }) : super(key: key);
/// The asynchronous computation to which this builder is currently connected,
/// possibly null. When changed, the current summary is updated using
/// [afterDisconnected], if the previous stream was not null, followed by
/// [afterConnected], if the new stream is not null.
final Stream<T> stream;
/// Returns the initial summary of stream interaction, typically representing
/// the fact that no interaction has happened at all.
///
/// Sub-classes must override this method to provide the initial value for
/// the fold computation.
S initial();
/// Returns an updated version of the [current] summary reflecting that we
/// are now connected to a stream.
///
/// The default implementation returns [current] as is.
S afterConnected(S current) => current;
/// Returns an updated version of the [current] summary following a data event.
///
/// Sub-classes must override this method to specify how the current summary
/// is combined with the new data item in the fold computation.
S afterData(S current, T data);
/// Returns an updated version of the [current] summary following an error.
///
/// The default implementation returns [current] as is.
S afterError(S current, Object error) => current;
/// Returns an updated version of the [current] summary following stream
/// termination.
///
/// The default implementation returns [current] as is.
S afterDone(S current) => current;
/// Returns an updated version of the [current] summary reflecting that we
/// are no longer connected to a stream.
///
/// The default implementation returns [current] as is.
S afterDisconnected(S current) => current;
/// Returns a Widget based on the [currentSummary].
Widget build(BuildContext context, S currentSummary);
@override
State<MyStreamBuilderBase<T, S>> createState() => _MyStreamBuilderBaseState<T, S>();
}
/// State for [MyStreamBuilderBase].
class _MyStreamBuilderBaseState<T, S> extends State<MyStreamBuilderBase<T, S>> {
StreamSubscription<T> _subscription;
S _summary;
@override
void initState() {
super.initState();
_summary = widget.initial();
_subscribe();
}
@override
void didUpdateWidget(MyStreamBuilderBase<T, S> oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.stream != widget.stream || oldWidget.initial() != widget.initial()) {
if (_subscription != null) {
_unsubscribe();
_summary = widget.afterDisconnected(_summary);
}
_subscribe();
}
}
@override
Widget build(BuildContext context) => widget.build(context, _summary);
@override
void dispose() {
_unsubscribe();
super.dispose();
}
void _subscribe() {
if (widget.stream != null) {
_subscription = widget.stream.listen((T data) {
setState(() {
_summary = widget.afterData(_summary, data);
});
}, onError: (Object error) {
setState(() {
_summary = widget.afterError(_summary, error);
});
}, onDone: () {
setState(() {
_summary = widget.afterDone(_summary);
});
});
_summary = widget.afterConnected(_summary);
}
}
void _unsubscribe() {
if (_subscription != null) {
_subscription.cancel();
_subscription = null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment