diff --git a/example/lib/main.dart b/example/lib/main.dart index f81c61be..ca204fe8 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -108,6 +108,14 @@ class ChannelListPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( + floatingActionButton: FloatingActionButton( + child: Icon(Icons.add), + onPressed: () { + Navigator.of(context).push(MaterialPageRoute(builder: (context) { + return CreateChannelPage(); + })); + }, + ), body: ChannelsBloc( child: ChannelListView( filter: { @@ -198,3 +206,189 @@ class ThreadPage extends StatelessWidget { ); } } + +class CreateChannelPage extends StatefulWidget { + @override + _CreateChannelPageState createState() => _CreateChannelPageState(); +} + +class _CreateChannelPageState extends State { + final ScrollController _scrollController = ScrollController(); + Client client; + List users = []; + List selectedUsers = []; + int offset = 0; + bool loading = false; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + elevation: 0, + backgroundColor: Colors.transparent, + title: Text( + 'Create a channel', + style: Theme.of(context).textTheme.headline6, + ), + ), + floatingActionButton: + selectedUsers.isNotEmpty ? _buildFAB(context) : SizedBox(), + body: _buildListView(), + ); + } + + ListView _buildListView() { + return ListView.builder( + controller: _scrollController, + itemBuilder: _itemBuilder, + itemCount: users.length, + ); + } + + Widget _itemBuilder(context, i) { + final user = users[i]; + return ListTile( + onLongPress: () { + _selectUser(user); + }, + selected: selectedUsers.contains(user), + onTap: () { + if (selectedUsers.isNotEmpty) { + return _selectUser(user); + } + _createChannel(context, [user]); + }, + leading: UserAvatar( + user: user, + ), + title: Text(user.name), + ); + } + + Widget _buildFAB(BuildContext context) { + return FloatingActionButton( + child: Icon(Icons.done), + onPressed: () async { + String name; + if (selectedUsers.length > 1) { + name = await _showEnterNameDialog(context); + if (name?.isNotEmpty != true) { + return; + } + } + + _createChannel(context, selectedUsers, name); + }, + ); + } + + Future _showEnterNameDialog(BuildContext context) { + final controller = TextEditingController(); + return showDialog( + context: context, + builder: (context) => SimpleDialog( + contentPadding: const EdgeInsets.all(16), + title: Text('Enter a name for the channel'), + children: [ + TextField( + controller: controller, + decoration: InputDecoration( + border: OutlineInputBorder(), + ), + ), + ButtonBar( + children: [ + FlatButton( + onPressed: () => Navigator.pop(context), + child: Text('Cancel'), + ), + FlatButton( + onPressed: () => Navigator.pop(context, controller.text), + child: Text('Ok'), + ), + ], + ), + ], + ), + ); + } + + Future _createChannel( + BuildContext context, + List users, [ + String name, + ]) async { + final channel = client.channel('messaging', extraData: { + 'members': [ + client.state.user.id, + ...users.map((e) => e.id), + ], + if (name != null) 'name': name, + }); + await channel.watch(); + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) { + return StreamChannel( + child: ChannelPage(), + channel: channel, + ); + }, + ), + ); + } + + void _selectUser(User user) { + if (!selectedUsers.contains(user)) { + setState(() { + selectedUsers.add(user); + }); + } else { + setState(() { + selectedUsers.remove(user); + }); + } + } + + @override + void initState() { + super.initState(); + + client = StreamChat.of(context).client; + + _scrollController.addListener(() async { + if (!loading && + _scrollController.offset >= + _scrollController.position.maxScrollExtent - 100) { + offset += 25; + await _queryUsers(); + } + }); + + _queryUsers(); + } + + Future _queryUsers() { + loading = true; + return client.queryUsers( + pagination: PaginationParams( + limit: 25, + offset: offset, + ), + sort: [ + SortOption( + 'name', + direction: SortOption.ASC, + ), + ], + ).then((value) { + setState(() { + users = [ + ...users, + ...value.users, + ]; + }); + }).whenComplete(() => loading = false); + } +} diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 23beab12..9e62c5e5 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,7 +1,7 @@ name: example description: A new Flutter project. -version: 1.0.3+4 +version: 1.0.6+7 environment: sdk: ">=2.2.2 <3.0.0" diff --git a/lib/src/channels_bloc.dart b/lib/src/channels_bloc.dart index cc2d4f93..df6f641b 100644 --- a/lib/src/channels_bloc.dart +++ b/lib/src/channels_bloc.dart @@ -124,18 +124,23 @@ class ChannelsBlocState extends State _subscriptions.add(client.on(EventType.messageNew).listen((e) { final newChannels = List.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); + if (index > -1) { + if (index > 0) { + final channel = newChannels.removeAt(index); + newChannels.insert(0, channel); + } } else { final hiddenIndex = _hiddenChannels.indexWhere((c) => c.cid == e.cid); if (hiddenIndex > -1) { newChannels.insert(0, _hiddenChannels[hiddenIndex]); _hiddenChannels.removeAt(hiddenIndex); - _channelsController.add(newChannels); + } else { + if (client.state.channels[e.cid] != null) { + newChannels.insert(0, client.state.channels[e.cid]); + } } } + _channelsController.add(newChannels); })); } diff --git a/pubspec.yaml b/pubspec.yaml index 34a48b8f..66a60736 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -23,7 +23,7 @@ dependencies: file_picker: ^1.12.0 image_picker: ^0.6.7+2 flutter_keyboard_visibility: ^3.2.1 - stream_chat: ^0.2.5 + stream_chat: ^0.2.5+1 mime: ^0.9.6+3 visibility_detector: ^0.1.5 http_parser: ^3.1.4