add channels_bloc
This commit is contained in:
+16
-34
@@ -50,50 +50,32 @@ class MyApp extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
home: Container(
|
home: Container(
|
||||||
child: ChannelListPage(client),
|
child: StreamChat(
|
||||||
|
client: client,
|
||||||
|
child: ChannelListPage(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChannelListPage extends StatelessWidget {
|
class ChannelListPage extends StatelessWidget {
|
||||||
final Client client;
|
|
||||||
|
|
||||||
ChannelListPage(this.client);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: PageView(
|
body: ChannelsBloc(
|
||||||
children: <Widget>[
|
child: ChannelListView(
|
||||||
StreamChat(
|
filter: {
|
||||||
client: client,
|
'members': {
|
||||||
child: Builder(
|
'\$in': [StreamChat.of(context).user.id],
|
||||||
builder: (context) => ChannelListView(
|
}
|
||||||
filter: {
|
},
|
||||||
'members': {
|
sort: [SortOption('last_message_at')],
|
||||||
'\$in': [StreamChat.of(context).user.id],
|
pagination: PaginationParams(
|
||||||
}
|
limit: 20,
|
||||||
},
|
|
||||||
sort: [SortOption('last_message_at')],
|
|
||||||
pagination: PaginationParams(
|
|
||||||
limit: 20,
|
|
||||||
),
|
|
||||||
channelWidget: ChannelPage(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
StreamChat(
|
channelWidget: ChannelPage(),
|
||||||
client: client,
|
),
|
||||||
child: ChannelListView(
|
|
||||||
sort: [SortOption('last_message_at')],
|
|
||||||
pagination: PaginationParams(
|
|
||||||
limit: 20,
|
|
||||||
),
|
|
||||||
channelWidget: ChannelPage(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
|
import 'package:stream_chat_flutter/src/channels_bloc.dart';
|
||||||
|
|
||||||
import '../stream_chat_flutter.dart';
|
import '../stream_chat_flutter.dart';
|
||||||
import 'channel_preview.dart';
|
import 'channel_preview.dart';
|
||||||
@@ -104,11 +105,11 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final streamChat = StreamChat.of(context);
|
final channelsProvider = ChannelsBloc.of(context);
|
||||||
|
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
return streamChat.queryChannels(
|
return channelsProvider.queryChannels(
|
||||||
filter: widget.filter,
|
filter: widget.filter,
|
||||||
sortOptions: widget.sort,
|
sortOptions: widget.sort,
|
||||||
paginationParams: widget.pagination,
|
paginationParams: widget.pagination,
|
||||||
@@ -116,7 +117,7 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: StreamBuilder<List<Channel>>(
|
child: StreamBuilder<List<Channel>>(
|
||||||
stream: streamChat.channelsStream,
|
stream: channelsProvider.channelsStream,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasError) {
|
if (snapshot.hasError) {
|
||||||
if (snapshot.error is Error) {
|
if (snapshot.error is Error) {
|
||||||
@@ -164,7 +165,7 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
),
|
),
|
||||||
FlatButton(
|
FlatButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
streamChat.queryChannels(
|
channelsProvider.queryChannels(
|
||||||
filter: widget.filter,
|
filter: widget.filter,
|
||||||
sortOptions: widget.sort,
|
sortOptions: widget.sort,
|
||||||
paginationParams: widget.pagination,
|
paginationParams: widget.pagination,
|
||||||
@@ -212,11 +213,11 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
|
|
||||||
i = i ~/ 2;
|
i = i ~/ 2;
|
||||||
|
|
||||||
final streamChat = StreamChat.of(context);
|
final channelsProvider = ChannelsBloc.of(context);
|
||||||
if (i < channels.length) {
|
if (i < channels.length) {
|
||||||
final channel = channels[i];
|
final channel = channels[i];
|
||||||
|
|
||||||
final channelClient = streamChat.channels.firstWhere(
|
final channelClient = channelsProvider.channels.firstWhere(
|
||||||
(c) => c.cid == channel.cid,
|
(c) => c.cid == channel.cid,
|
||||||
orElse: () => null,
|
orElse: () => null,
|
||||||
);
|
);
|
||||||
@@ -280,13 +281,14 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return _buildQueryProgressIndicator(context, streamChat);
|
return _buildQueryProgressIndicator(context, channelsProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildQueryProgressIndicator(context, StreamChatState streamChat) {
|
Widget _buildQueryProgressIndicator(
|
||||||
|
context, ChannelsBlocState channelsProvider) {
|
||||||
return StreamBuilder<bool>(
|
return StreamBuilder<bool>(
|
||||||
stream: streamChat.queryChannelsLoading,
|
stream: channelsProvider.queryChannelsLoading,
|
||||||
initialData: false,
|
initialData: false,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.hasError) {
|
if (snapshot.hasError) {
|
||||||
@@ -318,14 +320,14 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _listenChannelPagination(StreamChatState streamChat) {
|
void _listenChannelPagination(ChannelsBlocState channelsProvider) {
|
||||||
if (_scrollController.position.maxScrollExtent ==
|
if (_scrollController.position.maxScrollExtent ==
|
||||||
_scrollController.offset) {
|
_scrollController.offset) {
|
||||||
streamChat.queryChannels(
|
channelsProvider.queryChannels(
|
||||||
filter: widget.filter,
|
filter: widget.filter,
|
||||||
sortOptions: widget.sort,
|
sortOptions: widget.sort,
|
||||||
paginationParams: widget.pagination.copyWith(
|
paginationParams: widget.pagination.copyWith(
|
||||||
offset: streamChat.channels.length,
|
offset: channelsProvider.channels.length,
|
||||||
),
|
),
|
||||||
options: widget.options,
|
options: widget.options,
|
||||||
);
|
);
|
||||||
@@ -336,11 +338,11 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
final streamChat = StreamChat.of(context);
|
final channelsProvider = ChannelsBloc.of(context);
|
||||||
|
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
|
|
||||||
streamChat.queryChannels(
|
channelsProvider.queryChannels(
|
||||||
filter: widget.filter,
|
filter: widget.filter,
|
||||||
sortOptions: widget.sort,
|
sortOptions: widget.sort,
|
||||||
paginationParams: widget.pagination,
|
paginationParams: widget.pagination,
|
||||||
@@ -348,14 +350,14 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
);
|
);
|
||||||
|
|
||||||
_scrollController.addListener(() {
|
_scrollController.addListener(() {
|
||||||
_listenChannelPagination(streamChat);
|
_listenChannelPagination(channelsProvider);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
if (state == AppLifecycleState.resumed) {
|
if (state == AppLifecycleState.resumed) {
|
||||||
StreamChat.of(context).queryChannels(
|
ChannelsBloc.of(context).queryChannels(
|
||||||
filter: widget.filter,
|
filter: widget.filter,
|
||||||
sortOptions: widget.sort,
|
sortOptions: widget.sort,
|
||||||
paginationParams: widget.pagination,
|
paginationParams: widget.pagination,
|
||||||
@@ -370,7 +372,4 @@ class _ChannelListViewState extends State<ChannelListView>
|
|||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
bool get wantKeepAlive => true;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
|
||||||
|
|
||||||
@@ -59,15 +58,12 @@ class StreamChat extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StreamChatState extends State<StreamChat>
|
class StreamChatState extends State<StreamChat> with WidgetsBindingObserver {
|
||||||
with WidgetsBindingObserver, AutomaticKeepAliveClientMixin {
|
|
||||||
Client get client => widget.client;
|
Client get client => widget.client;
|
||||||
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
|
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
|
||||||
|
|
||||||
final theme = _getTheme(context, widget.streamChatThemeData);
|
final theme = _getTheme(context, widget.streamChatThemeData);
|
||||||
return StreamChatTheme(
|
return StreamChatTheme(
|
||||||
data: theme,
|
data: theme,
|
||||||
@@ -179,83 +175,9 @@ class StreamChatState extends State<StreamChat>
|
|||||||
/// The current user as a stream
|
/// The current user as a stream
|
||||||
Stream<User> get userStream => widget.client.state.userStream;
|
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
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.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);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,14 +196,6 @@ class StreamChatState extends State<StreamChat>
|
|||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
|
|
||||||
_channelsController.close();
|
|
||||||
_queryChannelsLoadingController.close();
|
|
||||||
_newMessagesSubscription.cancel();
|
|
||||||
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
bool get wantKeepAlive => true;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export 'src/channel_image.dart';
|
|||||||
export 'src/channel_list_view.dart';
|
export 'src/channel_list_view.dart';
|
||||||
export 'src/channel_name.dart';
|
export 'src/channel_name.dart';
|
||||||
export 'src/channel_preview.dart';
|
export 'src/channel_preview.dart';
|
||||||
|
export 'src/channels_bloc.dart';
|
||||||
export 'src/message_input.dart';
|
export 'src/message_input.dart';
|
||||||
export 'src/message_list_view.dart';
|
export 'src/message_list_view.dart';
|
||||||
export 'src/message_widget.dart';
|
export 'src/message_widget.dart';
|
||||||
|
|||||||
Reference in New Issue
Block a user