diff --git a/example/lib/main.dart b/example/lib/main.dart index 8182be2e..a4b343fe 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -63,17 +63,28 @@ class ChannelListPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - body: ChannelListView( - filter: { - 'members': { - '\$in': [StreamChat.of(context).user.id], - } - }, - sort: [SortOption('last_message_at')], - pagination: PaginationParams( - limit: 20, - ), - channelWidget: ChannelPage(), + body: PageView( + children: [ + ChannelListView( + filter: { + 'members': { + '\$in': [StreamChat.of(context).user.id], + } + }, + sort: [SortOption('last_message_at')], + pagination: PaginationParams( + limit: 20, + ), + channelWidget: ChannelPage(), + ), + ChannelListView( + sort: [SortOption('last_message_at')], + pagination: PaginationParams( + limit: 20, + ), + channelWidget: ChannelPage(), + ), + ], ), ); } diff --git a/lib/src/channel_list_view.dart b/lib/src/channel_list_view.dart index 8bcaaa96..13a43fd7 100644 --- a/lib/src/channel_list_view.dart +++ b/lib/src/channel_list_view.dart @@ -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'; @@ -99,15 +100,16 @@ class ChannelListView extends StatefulWidget { } class _ChannelListViewState extends State - with WidgetsBindingObserver { + with WidgetsBindingObserver, AutomaticKeepAliveClientMixin { final ScrollController _scrollController = ScrollController(); + ChannelsBloc channelsBloc; @override Widget build(BuildContext context) { - final streamChat = StreamChat.of(context); + super.build(context); return RefreshIndicator( onRefresh: () async { - return streamChat.queryChannels( + return channelsBloc.queryChannels( filter: widget.filter, sortOptions: widget.sort, paginationParams: widget.pagination, @@ -115,7 +117,7 @@ class _ChannelListViewState extends State ); }, child: StreamBuilder>( - stream: streamChat.channelsStream, + stream: channelsBloc.channelsStream, builder: (context, snapshot) { if (snapshot.hasError) { if (snapshot.error is Error) { @@ -163,7 +165,7 @@ class _ChannelListViewState extends State ), FlatButton( onPressed: () { - streamChat.queryChannels( + channelsBloc.queryChannels( filter: widget.filter, sortOptions: widget.sort, paginationParams: widget.pagination, @@ -215,7 +217,7 @@ class _ChannelListViewState extends State if (i < channels.length) { final channel = channels[i]; - final channelClient = streamChat.channels.firstWhere( + final channelClient = channelsBloc.channels.firstWhere( (c) => c.cid == channel.cid, orElse: () => null, ); @@ -285,7 +287,7 @@ class _ChannelListViewState extends State Widget _buildQueryProgressIndicator(context, StreamChatState streamChat) { return StreamBuilder( - stream: streamChat.queryChannelsLoading, + stream: channelsBloc.queryChannelsLoading, initialData: false, builder: (context, snapshot) { if (snapshot.hasError) { @@ -320,11 +322,11 @@ class _ChannelListViewState extends State void _listenChannelPagination(StreamChatState streamChat) { if (_scrollController.position.maxScrollExtent == _scrollController.offset) { - streamChat.queryChannels( + channelsBloc.queryChannels( filter: widget.filter, sortOptions: widget.sort, paginationParams: widget.pagination.copyWith( - offset: streamChat.channels.length, + offset: channelsBloc.channels.length, ), options: widget.options, ); @@ -334,11 +336,12 @@ class _ChannelListViewState extends State @override void initState() { super.initState(); + final streamChat = StreamChat.of(context); + channelsBloc = ChannelsBloc(streamChat.client); WidgetsBinding.instance.addObserver(this); - final streamChat = StreamChat.of(context); - streamChat.queryChannels( + channelsBloc.queryChannels( filter: widget.filter, sortOptions: widget.sort, paginationParams: widget.pagination, @@ -353,7 +356,7 @@ class _ChannelListViewState extends State @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed) { - StreamChat.of(context).queryChannels( + channelsBloc.queryChannels( filter: widget.filter, sortOptions: widget.sort, paginationParams: widget.pagination, @@ -366,6 +369,10 @@ class _ChannelListViewState extends State @override void dispose() { WidgetsBinding.instance.removeObserver(this); + channelsBloc.dispose(); super.dispose(); } + + @override + bool get wantKeepAlive => true; } diff --git a/lib/src/channels_bloc.dart b/lib/src/channels_bloc.dart new file mode 100644 index 00000000..b9620834 --- /dev/null +++ b/lib/src/channels_bloc.dart @@ -0,0 +1,72 @@ +import 'package:rxdart/rxdart.dart'; +import 'package:stream_chat/stream_chat.dart'; + +class ChannelsBloc { + final Client client; + + ChannelsBloc(this.client); + + /// The current channel list + List get channels => _channelsController.value; + + /// The current channel list as a stream + Stream> get channelsStream => _channelsController.stream; + + final BehaviorSubject _queryChannelsLoadingController = + BehaviorSubject.seeded(false); + + final BehaviorSubject> _channelsController = + BehaviorSubject.seeded([]); + + /// The stream notifying the state of queryChannel call + Stream get queryChannelsLoading => + _queryChannelsLoadingController.stream; + + /// Calls [client.queryChannels] updating [queryChannelsLoading] stream + Future queryChannels({ + Map filter, + List sortOptions, + PaginationParams paginationParams, + Map 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.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) { + _queryChannelsLoadingController.addError(err, stackTrace); + }); + } catch (err, stackTrace) { + _queryChannelsLoadingController.addError(err, stackTrace); + } + } + + void dispose() { + _channelsController.close(); + _queryChannelsLoadingController.close(); + } +} diff --git a/lib/src/stream_chat.dart b/lib/src/stream_chat.dart index 76576273..915562f8 100644 --- a/lib/src/stream_chat.dart +++ b/lib/src/stream_chat.dart @@ -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 - with WidgetsBindingObserver, AutomaticKeepAliveClientMixin { - final List _subscriptions = []; +class StreamChatState extends State with WidgetsBindingObserver { Client get client => widget.client; final GlobalKey _navigatorKey = GlobalKey(); @override Widget build(BuildContext context) { - super.build(context); final theme = _getTheme(context, widget.streamChatThemeData); return StreamChatTheme( data: theme, @@ -179,46 +175,6 @@ class StreamChatState extends State /// The current user as a stream Stream get userStream => widget.client.state.userStream; - /// The current channel list - List get channels => client.state.channels; - - /// The current channel list as a stream - Stream> get channelsStream => client.state.channelsStream; - - final BehaviorSubject _queryChannelsLoadingController = - BehaviorSubject.seeded(false); - - /// The stream notifying the state of queryChannel call - Stream get queryChannelsLoading => - _queryChannelsLoadingController.stream; - - /// Calls [client.queryChannels] updating [queryChannelsLoading] stream - Future queryChannels({ - Map filter, - List sortOptions, - PaginationParams paginationParams, - Map options, - bool onlyOffline = false, - }) async { - if (_queryChannelsLoadingController.value == true) { - return; - } - _queryChannelsLoadingController.sink.add(true); - - try { - await widget.client.queryChannels( - filter: filter, - sort: sortOptions, - options: options, - paginationParams: paginationParams, - onlyOffline: onlyOffline, - ); - _queryChannelsLoadingController.sink.add(false); - } catch (err, stackTrace) { - _queryChannelsLoadingController.addError(err, stackTrace); - } - } - @override void initState() { super.initState(); @@ -239,12 +195,7 @@ class StreamChatState extends State @override void dispose() { - _subscriptions.forEach((s) => s.cancel()); - _queryChannelsLoadingController.close(); WidgetsBinding.instance.removeObserver(this); super.dispose(); } - - @override - bool get wantKeepAlive => true; } diff --git a/pubspec.yaml b/pubspec.yaml index 0207b7a6..765ce1ff 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: url_launcher: ^5.4.2 video_player: ^0.10.8+1 chewie: ^0.9.10 - file_picker: ^1.5.1 + file_picker: ^1.6.0 image_picker: ^0.6.4 flutter_keyboard_visibility: ^0.7.0 stream_chat: