refactor!(core): Refactor BetterStreamBuilder.

1. BREAKING: Renamed `BetterStreamBuilder.loadingBuilder` to `.noDataBuilder`.
2. BREAKING: Added non-null constraint on `BetterStreamBuilder<T extends Object>`.
3. `BetterStreamBuilder.initialData` is now nullable/not-required.

Signed-off-by: xsahil03x <[email protected]>
This commit is contained in:
Sahil Kumar
2021-08-20 16:13:27 +05:30
committed by xsahil03x
parent 84fccf2c63
commit f90dc4127d
6 changed files with 46 additions and 48 deletions
@@ -1,5 +1,11 @@
## Upcoming
🛑️ Breaking Changes from `2.1.1`
- Renamed `BetterStreamBuilder.loadingBuilder` to `.noDataBuilder`
🔄 Changed
- `BetterStreamBuilder.initialData` is now nullable/not-required.
🐞 Fixed
- [#612](https://github.com/GetStream/stream-chat-flutter/issues/612) `ChannelListView` pagination doesn't work after refresh
@@ -6,23 +6,23 @@ import 'package:flutter/widgets.dart';
/// It requires [initialData] and will rebuild
/// only when the new data is different than the current data
/// The [comparator] is used to check if the new data is different
class BetterStreamBuilder<T> extends StatefulWidget {
class BetterStreamBuilder<T extends Object> extends StatefulWidget {
/// Creates a new BetterStreamBuilder
const BetterStreamBuilder({
required this.stream,
required this.initialData,
required this.builder,
this.loadingBuilder,
this.initialData,
this.noDataBuilder,
this.errorBuilder,
this.comparator,
Key? key,
}) : super(key: key);
/// The stream to listen to
final Stream<T>? stream;
final Stream<T?>? stream;
/// The initial data available
final T initialData;
final T? initialData;
/// Comparator used to check if the new data is different than the last one
final bool Function(T?, T?)? comparator;
@@ -31,7 +31,7 @@ class BetterStreamBuilder<T> extends StatefulWidget {
final Widget Function(BuildContext context, T data) builder;
/// Builder that builds when the data is null
final Widget Function(BuildContext context)? loadingBuilder;
final Widget Function(BuildContext context)? noDataBuilder;
/// Builder used when there is an error
final Widget Function(BuildContext context, Object error)? errorBuilder;
@@ -40,21 +40,26 @@ class BetterStreamBuilder<T> extends StatefulWidget {
_BetterStreamBuilderState createState() => _BetterStreamBuilderState<T>();
}
class _BetterStreamBuilderState<T> extends State<BetterStreamBuilder<T>> {
class _BetterStreamBuilderState<T extends Object>
extends State<BetterStreamBuilder<T>> {
T? _lastEvent;
StreamSubscription? _subscription;
StreamSubscription<T?>? _subscription;
Object? _lastError;
@override
Widget build(BuildContext context) {
if (_lastError != null) {
return widget.errorBuilder!(context, _lastError!);
final error = _lastError;
if (error != null) {
final errorBuilder = widget.errorBuilder;
if (errorBuilder != null) {
return errorBuilder(context, error);
}
}
if (_lastEvent == null) {
return widget.loadingBuilder?.call(context) ?? const Offstage();
final event = _lastEvent;
if (event == null) {
return widget.noDataBuilder?.call(context) ?? const Offstage();
}
return widget.builder(context, _lastEvent ?? widget.initialData);
return widget.builder(context, event);
}
@override
@@ -87,22 +92,22 @@ class _BetterStreamBuilderState<T> extends State<BetterStreamBuilder<T>> {
void _onError(error) {
if (widget.errorBuilder != null && error != _lastError) {
_lastError = error;
if (mounted) {
setState(() {});
}
_lastError = error;
}
}
void _onEvent(T event) {
void _onEvent(T? event) {
_lastError = null;
final isEqual =
widget.comparator?.call(_lastEvent, event) ?? event == _lastEvent;
if (!isEqual) {
_lastEvent = event;
if (mounted) {
setState(() {});
}
_lastEvent = event;
}
}
}
@@ -4,6 +4,7 @@ import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter_core/src/better_stream_builder.dart';
import 'package:stream_chat_flutter_core/src/channels_bloc.dart';
import 'package:stream_chat_flutter_core/src/stream_chat_core.dart';
import 'package:stream_chat_flutter_core/src/typedef.dart';
@@ -137,19 +138,14 @@ class ChannelListCoreState extends State<ChannelListCore> {
@override
Widget build(BuildContext context) => _buildListView(_channelsBloc);
StreamBuilder<List<Channel>> _buildListView(
BetterStreamBuilder<List<Channel>> _buildListView(
ChannelsBlocState channelsBlocState,
) =>
StreamBuilder<List<Channel>>(
BetterStreamBuilder<List<Channel>>(
stream: channelsBlocState.channelsStream,
builder: (context, snapshot) {
if (snapshot.hasError) {
return widget.errorBuilder(context, snapshot.error!);
}
if (!snapshot.hasData) {
return widget.loadingBuilder(context);
}
final channels = snapshot.data!;
errorBuilder: widget.errorBuilder,
noDataBuilder: widget.loadingBuilder,
builder: (context, channels) {
if (channels.isEmpty) {
return widget.emptyBuilder(context);
}
@@ -138,7 +138,7 @@ class MessageListCoreState extends State<MessageListCore> {
return true;
}
return BetterStreamBuilder<List<Message>?>(
return BetterStreamBuilder<List<Message>>(
initialData: initialData,
comparator: const ListEquality().equals,
stream: messagesStream!.map(
@@ -148,9 +148,9 @@ class MessageListCoreState extends State<MessageListCore> {
),
),
errorBuilder: widget.errorBuilder,
loadingBuilder: widget.loadingBuilder,
noDataBuilder: widget.loadingBuilder,
builder: (context, data) {
final messageList = data?.reversed.toList(growable: false) ?? [];
final messageList = data.reversed.toList(growable: false);
if (messageList.isEmpty && !_isThreadConversation) {
if (_upToDate) {
return widget.emptyBuilder(context);
@@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter_core/src/better_stream_builder.dart';
import 'package:stream_chat_flutter_core/src/message_search_bloc.dart';
import 'package:stream_chat_flutter_core/src/typedef.dart';
@@ -132,16 +133,11 @@ class MessageSearchListCoreState extends State<MessageSearchListCore> {
Widget build(BuildContext context) => _buildListView(_messageSearchBloc!);
Widget _buildListView(MessageSearchBlocState messageSearchBloc) =>
StreamBuilder<List<GetMessageResponse>>(
BetterStreamBuilder<List<GetMessageResponse>>(
stream: messageSearchBloc.messagesStream,
builder: (context, snapshot) {
if (snapshot.hasError) {
return widget.errorBuilder(context, snapshot.error!);
}
if (!snapshot.hasData) {
return widget.loadingBuilder(context);
}
final items = snapshot.data!;
errorBuilder: widget.errorBuilder,
noDataBuilder: widget.loadingBuilder,
builder: (context, items) {
if (items.isEmpty) {
return widget.emptyBuilder(context);
}
@@ -176,16 +176,11 @@ class UserListCoreState extends State<UserListCore>
},
);
StreamBuilder<List<ListItem>> _buildListView() => StreamBuilder(
BetterStreamBuilder<List<ListItem>> _buildListView() => BetterStreamBuilder(
stream: _buildUserStream(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return widget.errorBuilder(context, snapshot.error!);
}
if (!snapshot.hasData) {
return widget.loadingBuilder(context);
}
final items = snapshot.data!;
errorBuilder: widget.errorBuilder,
noDataBuilder: widget.loadingBuilder,
builder: (context, items) {
if (items.isEmpty) {
return widget.emptyBuilder(context);
}