add channels_bloc

This commit is contained in:
Salvatore Giordano
2020-04-07 15:23:23 +02:00
parent fafafb8c44
commit e0fde25b24
5 changed files with 166 additions and 140 deletions
+16 -34
View File
@@ -50,50 +50,32 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: Container(
child: ChannelListPage(client),
child: StreamChat(
client: client,
child: ChannelListPage(),
),
),
);
}
}
class ChannelListPage extends StatelessWidget {
final Client client;
ChannelListPage(this.client);
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
children: <Widget>[
StreamChat(
client: client,
child: Builder(
builder: (context) => ChannelListView(
filter: {
'members': {
'\$in': [StreamChat.of(context).user.id],
}
},
sort: [SortOption('last_message_at')],
pagination: PaginationParams(
limit: 20,
),
channelWidget: ChannelPage(),
),
),
body: ChannelsBloc(
child: ChannelListView(
filter: {
'members': {
'\$in': [StreamChat.of(context).user.id],
}
},
sort: [SortOption('last_message_at')],
pagination: PaginationParams(
limit: 20,
),
StreamChat(
client: client,
child: ChannelListView(
sort: [SortOption('last_message_at')],
pagination: PaginationParams(
limit: 20,
),
channelWidget: ChannelPage(),
),
),
],
channelWidget: ChannelPage(),
),
),
);
}
+18 -19
View File
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/channels_bloc.dart';
import '../stream_chat_flutter.dart';
import 'channel_preview.dart';
@@ -104,11 +105,11 @@ class _ChannelListViewState extends State<ChannelListView>
@override
Widget build(BuildContext context) {
final streamChat = StreamChat.of(context);
final channelsProvider = ChannelsBloc.of(context);
return RefreshIndicator(
onRefresh: () async {
return streamChat.queryChannels(
return channelsProvider.queryChannels(
filter: widget.filter,
sortOptions: widget.sort,
paginationParams: widget.pagination,
@@ -116,7 +117,7 @@ class _ChannelListViewState extends State<ChannelListView>
);
},
child: StreamBuilder<List<Channel>>(
stream: streamChat.channelsStream,
stream: channelsProvider.channelsStream,
builder: (context, snapshot) {
if (snapshot.hasError) {
if (snapshot.error is Error) {
@@ -164,7 +165,7 @@ class _ChannelListViewState extends State<ChannelListView>
),
FlatButton(
onPressed: () {
streamChat.queryChannels(
channelsProvider.queryChannels(
filter: widget.filter,
sortOptions: widget.sort,
paginationParams: widget.pagination,
@@ -212,11 +213,11 @@ class _ChannelListViewState extends State<ChannelListView>
i = i ~/ 2;
final streamChat = StreamChat.of(context);
final channelsProvider = ChannelsBloc.of(context);
if (i < channels.length) {
final channel = channels[i];
final channelClient = streamChat.channels.firstWhere(
final channelClient = channelsProvider.channels.firstWhere(
(c) => c.cid == channel.cid,
orElse: () => null,
);
@@ -280,13 +281,14 @@ class _ChannelListViewState extends State<ChannelListView>
),
);
} else {
return _buildQueryProgressIndicator(context, streamChat);
return _buildQueryProgressIndicator(context, channelsProvider);
}
}
Widget _buildQueryProgressIndicator(context, StreamChatState streamChat) {
Widget _buildQueryProgressIndicator(
context, ChannelsBlocState channelsProvider) {
return StreamBuilder<bool>(
stream: streamChat.queryChannelsLoading,
stream: channelsProvider.queryChannelsLoading,
initialData: false,
builder: (context, snapshot) {
if (snapshot.hasError) {
@@ -318,14 +320,14 @@ class _ChannelListViewState extends State<ChannelListView>
);
}
void _listenChannelPagination(StreamChatState streamChat) {
void _listenChannelPagination(ChannelsBlocState channelsProvider) {
if (_scrollController.position.maxScrollExtent ==
_scrollController.offset) {
streamChat.queryChannels(
channelsProvider.queryChannels(
filter: widget.filter,
sortOptions: widget.sort,
paginationParams: widget.pagination.copyWith(
offset: streamChat.channels.length,
offset: channelsProvider.channels.length,
),
options: widget.options,
);
@@ -336,11 +338,11 @@ class _ChannelListViewState extends State<ChannelListView>
void initState() {
super.initState();
final streamChat = StreamChat.of(context);
final channelsProvider = ChannelsBloc.of(context);
WidgetsBinding.instance.addObserver(this);
streamChat.queryChannels(
channelsProvider.queryChannels(
filter: widget.filter,
sortOptions: widget.sort,
paginationParams: widget.pagination,
@@ -348,14 +350,14 @@ class _ChannelListViewState extends State<ChannelListView>
);
_scrollController.addListener(() {
_listenChannelPagination(streamChat);
_listenChannelPagination(channelsProvider);
});
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
StreamChat.of(context).queryChannels(
ChannelsBloc.of(context).queryChannels(
filter: widget.filter,
sortOptions: widget.sort,
paginationParams: widget.pagination,
@@ -370,7 +372,4 @@ class _ChannelListViewState extends State<ChannelListView>
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
bool get wantKeepAlive => true;
}
+130
View File
@@ -0,0 +1,130 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/stream_chat.dart';
class ChannelsBloc extends StatefulWidget {
final Widget child;
const ChannelsBloc({
Key key,
this.child,
}) : super(key: key);
@override
ChannelsBlocState createState() => ChannelsBlocState();
static ChannelsBlocState of(BuildContext context) {
ChannelsBlocState streamChatState;
streamChatState = context.findAncestorStateOfType<ChannelsBlocState>();
if (streamChatState == null) {
throw Exception('You must have a ChannelsProvider widget as anchestor');
}
return streamChatState;
}
}
class ChannelsBlocState extends State<ChannelsBloc>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
return widget.child;
}
/// The current channel list
List<Channel> get channels => _channelsController.value;
/// The current channel list as a stream
Stream<List<Channel>> get channelsStream => _channelsController.stream;
final BehaviorSubject<bool> _queryChannelsLoadingController =
BehaviorSubject.seeded(false);
final BehaviorSubject<List<Channel>> _channelsController =
BehaviorSubject.seeded([]);
/// The stream notifying the state of queryChannel call
Stream<bool> get queryChannelsLoading =>
_queryChannelsLoadingController.stream;
/// Calls [client.queryChannels] updating [queryChannelsLoading] stream
Future<void> queryChannels({
Map<String, dynamic> filter,
List<SortOption> sortOptions,
PaginationParams paginationParams,
Map<String, dynamic> options,
bool onlyOffline = false,
}) async {
if (_queryChannelsLoadingController.value == true) {
return;
}
_queryChannelsLoadingController.sink.add(true);
try {
final clear = paginationParams == null ||
paginationParams.offset == null ||
paginationParams.offset == 0;
final oldChannels = List<Channel>.from(channels);
StreamChat.of(context)
.client
.queryChannels(
filter: filter,
sort: sortOptions,
options: options,
paginationParams: paginationParams,
onlyOffline: onlyOffline,
)
.listen((channels) {
if (clear) {
_channelsController.add(channels);
} else {
final l = oldChannels + channels;
_channelsController.add(l);
}
}, onDone: () {
_queryChannelsLoadingController.sink.add(false);
}, onError: (err, stackTrace) {
print(err);
print(stackTrace);
_queryChannelsLoadingController.addError(err, stackTrace);
});
} catch (err, stackTrace) {
_queryChannelsLoadingController.addError(err, stackTrace);
}
}
StreamSubscription _newMessagesSubscription;
@override
void initState() {
super.initState();
_newMessagesSubscription =
StreamChat.of(context).client.on(EventType.messageNew).listen((e) {
final newChannels = List<Channel>.from(channels ?? []);
final index = newChannels.indexWhere((c) => c.cid == e.cid);
if (index > 0) {
final channel = newChannels.removeAt(index);
newChannels.insert(0, channel);
_channelsController.add(newChannels);
}
});
}
@override
void dispose() {
_channelsController.close();
_queryChannelsLoadingController.close();
_newMessagesSubscription.cancel();
super.dispose();
}
@override
bool get wantKeepAlive => true;
}
+1 -87
View File
@@ -2,7 +2,6 @@ import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
@@ -59,15 +58,12 @@ class StreamChat extends StatefulWidget {
}
}
class StreamChatState extends State<StreamChat>
with WidgetsBindingObserver, AutomaticKeepAliveClientMixin {
class StreamChatState extends State<StreamChat> with WidgetsBindingObserver {
Client get client => widget.client;
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
@override
Widget build(BuildContext context) {
super.build(context);
final theme = _getTheme(context, widget.streamChatThemeData);
return StreamChatTheme(
data: theme,
@@ -179,83 +175,9 @@ class StreamChatState extends State<StreamChat>
/// The current user as a stream
Stream<User> get userStream => widget.client.state.userStream;
/// The current channel list
List<Channel> get channels => _channelsController.value;
/// The current channel list as a stream
Stream<List<Channel>> get channelsStream => _channelsController.stream;
final BehaviorSubject<bool> _queryChannelsLoadingController =
BehaviorSubject.seeded(false);
final BehaviorSubject<List<Channel>> _channelsController =
BehaviorSubject.seeded([]);
/// The stream notifying the state of queryChannel call
Stream<bool> get queryChannelsLoading =>
_queryChannelsLoadingController.stream;
/// Calls [client.queryChannels] updating [queryChannelsLoading] stream
Future<void> queryChannels({
Map<String, dynamic> filter,
List<SortOption> sortOptions,
PaginationParams paginationParams,
Map<String, dynamic> options,
bool onlyOffline = false,
}) async {
if (_queryChannelsLoadingController.value == true) {
return;
}
_queryChannelsLoadingController.sink.add(true);
try {
final clear = paginationParams == null ||
paginationParams.offset == null ||
paginationParams.offset == 0;
final oldChannels = List<Channel>.from(channels);
client
.queryChannels(
filter: filter,
sort: sortOptions,
options: options,
paginationParams: paginationParams,
onlyOffline: onlyOffline,
)
.listen((channels) {
if (clear) {
_channelsController.add(channels);
} else {
final l = oldChannels + channels;
_channelsController.add(l);
}
}, onDone: () {
_queryChannelsLoadingController.sink.add(false);
}, onError: (err, stackTrace) {
print(err);
print(stackTrace);
_queryChannelsLoadingController.addError(err, stackTrace);
});
} catch (err, stackTrace) {
_queryChannelsLoadingController.addError(err, stackTrace);
}
}
StreamSubscription _newMessagesSubscription;
@override
void initState() {
super.initState();
_newMessagesSubscription = client.on(EventType.messageNew).listen((e) {
final newChannels = List<Channel>.from(channels ?? []);
final index = newChannels.indexWhere((c) => c.cid == e.cid);
if (index > 0) {
final channel = newChannels.removeAt(index);
newChannels.insert(0, channel);
_channelsController.add(newChannels);
}
});
WidgetsBinding.instance.addObserver(this);
}
@@ -274,14 +196,6 @@ class StreamChatState extends State<StreamChat>
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_channelsController.close();
_queryChannelsLoadingController.close();
_newMessagesSubscription.cancel();
super.dispose();
}
@override
bool get wantKeepAlive => true;
}
+1
View File
@@ -5,6 +5,7 @@ export 'src/channel_image.dart';
export 'src/channel_list_view.dart';
export 'src/channel_name.dart';
export 'src/channel_preview.dart';
export 'src/channels_bloc.dart';
export 'src/message_input.dart';
export 'src/message_list_view.dart';
export 'src/message_widget.dart';