From 515b9734cbcb97d0266f0d84285d4ca2968f04a6 Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Mon, 23 Nov 2020 20:40:09 +0530 Subject: [PATCH 01/15] [NewGroupChatScreen] Maintain selectedUsersList when going back and forward in the flow --- example/lib/group_chat_details_screen.dart | 310 +++++++++++---------- example/lib/new_group_chat_screen.dart | 11 +- lib/src/back_button.dart | 2 +- 3 files changed, 168 insertions(+), 155 deletions(-) diff --git a/example/lib/group_chat_details_screen.dart b/example/lib/group_chat_details_screen.dart index 6aff5711..0aba4578 100644 --- a/example/lib/group_chat_details_screen.dart +++ b/example/lib/group_chat_details_screen.dart @@ -53,167 +53,173 @@ class _GroupChatDetailsScreenState extends State { @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Color.fromRGBO(252, 252, 252, 1), - appBar: AppBar( - elevation: 1, - backgroundColor: Colors.white, - leading: const StreamBackButton(), - title: Text( - 'Name of Group Chat', - style: TextStyle( - color: Colors.black, - fontSize: 16, - ), - ), - centerTitle: true, - bottom: PreferredSize( - preferredSize: Size.fromHeight(kToolbarHeight), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 16), - child: Row( - children: [ - Text( - 'NAME', - style: TextStyle( - fontSize: 12, - color: Colors.black.withOpacity(0.5), - ), - ), - SizedBox(width: 16), - Expanded( - child: TextField( - controller: _groupNameController, - decoration: InputDecoration( - isDense: true, - border: InputBorder.none, - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, - errorBorder: InputBorder.none, - disabledBorder: InputBorder.none, - contentPadding: const EdgeInsets.all(0), - hintText: 'Choose a group chat name', - hintStyle: TextStyle( - fontSize: 14, color: Colors.black.withOpacity(.5)), - ), - ), - ), - ], + return WillPopScope( + onWillPop: () async { + Navigator.pop(context, _selectedUsers); + return false; + }, + child: Scaffold( + backgroundColor: Color.fromRGBO(252, 252, 252, 1), + appBar: AppBar( + elevation: 1, + backgroundColor: Colors.white, + leading: const StreamBackButton(), + title: Text( + 'Name of Group Chat', + style: TextStyle( + color: Colors.black, + fontSize: 16, ), ), - ), - actions: [ - NeumorphicButton( - child: IconButton( - padding: const EdgeInsets.all(0), - icon: Icon(StreamIcons.check), - color: Color(0xFF006CFF), - onPressed: _isGroupNameEmpty - ? null - : () async { - final groupName = _groupNameController.text; - final client = StreamChat.of(context).client; - final channel = client - .channel('messaging', id: Uuid().v4(), extraData: { - 'members': [ - client.state.user.id, - ..._selectedUsers.map((e) => e.id), - ], - 'name': groupName, - }); - await channel.watch(); - Navigator.of(context) - ..pop() - ..pushReplacement( - MaterialPageRoute( - builder: (context) { - return StreamChannel( - child: ChannelPage(), - channel: channel, - ); - }, - ), - ); - }, - ), - ), - ], - ), - body: Column( - children: [ - Container( - width: double.maxFinite, - color: Colors.grey.shade50, + centerTitle: true, + bottom: PreferredSize( + preferredSize: Size.fromHeight(kToolbarHeight), child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 8, + padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 16), + child: Row( + children: [ + Text( + 'NAME', + style: TextStyle( + fontSize: 12, + color: Colors.black.withOpacity(0.5), + ), + ), + SizedBox(width: 16), + Expanded( + child: TextField( + controller: _groupNameController, + decoration: InputDecoration( + isDense: true, + border: InputBorder.none, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + errorBorder: InputBorder.none, + disabledBorder: InputBorder.none, + contentPadding: const EdgeInsets.all(0), + hintText: 'Choose a group chat name', + hintStyle: TextStyle( + fontSize: 14, color: Colors.black.withOpacity(.5)), + ), + ), + ), + ], ), - child: Text( - '$_totalUsers ${_totalUsers > 1 ? 'Members' : 'Member'}', - style: TextStyle( - fontWeight: FontWeight.w500, + ), + ), + actions: [ + NeumorphicButton( + child: IconButton( + padding: const EdgeInsets.all(0), + icon: Icon(StreamIcons.check), + color: Color(0xFF006CFF), + onPressed: _isGroupNameEmpty + ? null + : () async { + final groupName = _groupNameController.text; + final client = StreamChat.of(context).client; + final channel = client + .channel('messaging', id: Uuid().v4(), extraData: { + 'members': [ + client.state.user.id, + ..._selectedUsers.map((e) => e.id), + ], + 'name': groupName, + }); + await channel.watch(); + Navigator.of(context) + ..pop() + ..pushReplacement( + MaterialPageRoute( + builder: (context) { + return StreamChannel( + child: ChannelPage(), + channel: channel, + ); + }, + ), + ); + }, + ), + ), + ], + ), + body: Column( + children: [ + Container( + width: double.maxFinite, + color: Colors.grey.shade50, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 8, + ), + child: Text( + '$_totalUsers ${_totalUsers > 1 ? 'Members' : 'Member'}', + style: TextStyle( + fontWeight: FontWeight.w500, + ), ), ), ), - ), - Expanded( - child: ListView.separated( - itemCount: _selectedUsers.length + 1, - separatorBuilder: (_, __) => Container( - height: 1, - color: Theme.of(context).brightness == Brightness.dark - ? Colors.white.withOpacity(0.1) - : Colors.black.withOpacity(0.1), - ), - itemBuilder: (_, index) { - if (index == _selectedUsers.length) { - return Container( - height: 1, - color: Theme.of(context).brightness == Brightness.dark - ? Colors.white.withOpacity(0.1) - : Colors.black.withOpacity(0.1), + Expanded( + child: ListView.separated( + itemCount: _selectedUsers.length + 1, + separatorBuilder: (_, __) => Container( + height: 1, + color: Theme.of(context).brightness == Brightness.dark + ? Colors.white.withOpacity(0.1) + : Colors.black.withOpacity(0.1), + ), + itemBuilder: (_, index) { + if (index == _selectedUsers.length) { + return Container( + height: 1, + color: Theme.of(context).brightness == Brightness.dark + ? Colors.white.withOpacity(0.1) + : Colors.black.withOpacity(0.1), + ); + } + final user = _selectedUsers[index]; + return ListTile( + key: ObjectKey(user), + leading: UserAvatar( + user: user, + constraints: BoxConstraints.tightFor( + width: 40, + height: 40, + ), + ), + title: Text( + user.name, + style: TextStyle(fontWeight: FontWeight.bold), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 8, + ), + trailing: IconButton( + icon: Icon( + Icons.clear_rounded, + color: Colors.black, + ), + padding: const EdgeInsets.all(0), + splashRadius: 24, + onPressed: () { + setState(() { + _selectedUsers.remove(user); + }); + if (_selectedUsers.isEmpty) { + Navigator.pop(context, _selectedUsers); + } + }, + ), ); - } - final user = _selectedUsers[index]; - return ListTile( - key: ObjectKey(user), - leading: UserAvatar( - user: user, - constraints: BoxConstraints.tightFor( - width: 40, - height: 40, - ), - ), - title: Text( - user.name, - style: TextStyle(fontWeight: FontWeight.bold), - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 8, - ), - trailing: IconButton( - icon: Icon( - Icons.clear_rounded, - color: Colors.black, - ), - padding: const EdgeInsets.all(0), - splashRadius: 24, - onPressed: () { - setState(() { - _selectedUsers.remove(user); - }); - if (_selectedUsers.isEmpty) { - Navigator.pop(context); - } - }, - ), - ); - }, + }, + ), ), - ), - ], + ], + ), ), ); } diff --git a/example/lib/new_group_chat_screen.dart b/example/lib/new_group_chat_screen.dart index b75e7510..3c6ad2ba 100644 --- a/example/lib/new_group_chat_screen.dart +++ b/example/lib/new_group_chat_screen.dart @@ -69,8 +69,8 @@ class _NewGroupChatScreenState extends State { StreamIcons.arrow_right, color: Color(0xFF006CFF), ), - onPressed: () { - Navigator.push( + onPressed: () async { + final updatedList = await Navigator.push( context, MaterialPageRoute( builder: (_) => GroupChatDetailsScreen( @@ -78,6 +78,13 @@ class _NewGroupChatScreenState extends State { ), ), ); + if (updatedList != null) { + setState(() { + _selectedUsers + ..clear() + ..addAll(updatedList); + }); + } }, ) ], diff --git a/lib/src/back_button.dart b/lib/src/back_button.dart index 78b86ae5..303ad8a9 100644 --- a/lib/src/back_button.dart +++ b/lib/src/back_button.dart @@ -33,7 +33,7 @@ class StreamBackButton extends StatelessWidget { if (onPressed != null) { onPressed(); } else { - Navigator.of(context).pop(); + Navigator.maybePop(context); } }, child: Icon( From 6508c040dd0d9316b29d087c0fa55c351504ab63 Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Mon, 23 Nov 2020 20:48:46 +0530 Subject: [PATCH 02/15] [ChipsInputTextField] Request focus and vice versa when item addition is resumed/paused. --- example/lib/chips_input_text_field.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/example/lib/chips_input_text_field.dart b/example/lib/chips_input_text_field.dart index 317a68f7..76501d05 100644 --- a/example/lib/chips_input_text_field.dart +++ b/example/lib/chips_input_text_field.dart @@ -51,12 +51,14 @@ class ChipInputTextFieldState extends State> { if (!_pauseItemAddition) { setState(() => _pauseItemAddition = true); } + widget.focusNode.unfocus(); } void resumeItemAddition() { if (_pauseItemAddition) { setState(() => _pauseItemAddition = false); } + widget.focusNode.requestFocus(); } @override From bcb6515ea89ff9ef9e1951ebfe4c136bad6d05a9 Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Mon, 23 Nov 2020 20:51:03 +0530 Subject: [PATCH 03/15] minor fixes --- example/lib/chips_input_text_field.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/example/lib/chips_input_text_field.dart b/example/lib/chips_input_text_field.dart index 76501d05..7ee0568c 100644 --- a/example/lib/chips_input_text_field.dart +++ b/example/lib/chips_input_text_field.dart @@ -43,7 +43,6 @@ class ChipInputTextFieldState extends State> { _chips.remove(item); if (_chips.isEmpty) resumeItemAddition(); }); - if (widget.focusNode != null) widget.focusNode.requestFocus(); if (widget.onChipRemoved != null) widget.onChipRemoved(item); } @@ -51,14 +50,14 @@ class ChipInputTextFieldState extends State> { if (!_pauseItemAddition) { setState(() => _pauseItemAddition = true); } - widget.focusNode.unfocus(); + if (widget.focusNode != null) widget.focusNode.unfocus(); } void resumeItemAddition() { if (_pauseItemAddition) { setState(() => _pauseItemAddition = false); } - widget.focusNode.requestFocus(); + if (widget.focusNode != null) widget.focusNode.requestFocus(); } @override From 7aac92e890d78a6b93378a73bce7d24103c6589f Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Mon, 23 Nov 2020 20:57:32 +0530 Subject: [PATCH 04/15] minor fixes --- example/lib/chips_input_text_field.dart | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/example/lib/chips_input_text_field.dart b/example/lib/chips_input_text_field.dart index 7ee0568c..857782fe 100644 --- a/example/lib/chips_input_text_field.dart +++ b/example/lib/chips_input_text_field.dart @@ -50,27 +50,20 @@ class ChipInputTextFieldState extends State> { if (!_pauseItemAddition) { setState(() => _pauseItemAddition = true); } - if (widget.focusNode != null) widget.focusNode.unfocus(); + widget.focusNode?.unfocus(); } void resumeItemAddition() { if (_pauseItemAddition) { setState(() => _pauseItemAddition = false); } - if (widget.focusNode != null) widget.focusNode.requestFocus(); + widget.focusNode?.requestFocus(); } @override Widget build(BuildContext context) { return GestureDetector( - onTap: _pauseItemAddition - ? () { - setState(() { - _pauseItemAddition = false; - widget.focusNode?.requestFocus(); - }); - } - : null, + onTap: _pauseItemAddition ? resumeItemAddition : null, child: Material( elevation: 1, color: Colors.white, From a0afe4d0911b6f843dd7783db979e2d8dbebfe0b Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Tue, 24 Nov 2020 15:06:18 +0530 Subject: [PATCH 05/15] [UserListView] Replace ListView.custom, GridView.custom with separated,builder respectively, --- lib/src/user_list_view.dart | 51 ++++++++++++------------------------- 1 file changed, 16 insertions(+), 35 deletions(-) diff --git a/lib/src/user_list_view.dart b/lib/src/user_list_view.dart index 63285d8b..046ad9c7 100644 --- a/lib/src/user_list_view.dart +++ b/lib/src/user_list_view.dart @@ -328,56 +328,37 @@ class _UserListViewState extends State } if (_isListView) { - return ListView.custom( + return ListView.separated( physics: AlwaysScrollableScrollPhysics(), controller: _scrollController, - childrenDelegate: SliverChildBuilderDelegate( - (context, i) { - return _listItemBuilder(context, i, items); - }, - childCount: (items.length * 2) + 1, - findChildIndexCallback: (key) { - final ValueKey valueKey = key; - final index = - items.indexWhere((item) => item.key == valueKey.value); - return index != -1 ? (index * 2) : null; - }, - ), + itemCount: items.isNotEmpty ? items.length + 1 : items.length, + separatorBuilder: (_, index) { + if (widget.separatorBuilder != null) { + return widget.separatorBuilder(context, index); + } + return _separatorBuilder(context, index); + }, + itemBuilder: (context, index) { + return _listItemBuilder(context, index, items); + }, ); } - return GridView.custom( + return GridView.builder( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: widget.crossAxisCount, ), + itemCount: items.isNotEmpty ? items.length + 1 : items.length, physics: AlwaysScrollableScrollPhysics(), controller: _scrollController, - childrenDelegate: SliverChildBuilderDelegate( - (context, i) { - return _gridItemBuilder(context, i, items); - }, - childCount: items.length, - findChildIndexCallback: (key) { - final ValueKey valueKey = key; - final index = - items.indexWhere((item) => item.key == valueKey.value); - return index != -1 ? index : null; - }, - ), + itemBuilder: (context, index) { + return _gridItemBuilder(context, index, items); + }, ); }, ); } Widget _listItemBuilder(BuildContext context, int i, List items) { - if (i % 2 != 0) { - if (widget.separatorBuilder != null) { - return widget.separatorBuilder(context, i); - } - return _separatorBuilder(context, i); - } - - i = i ~/ 2; - final usersProvider = UsersBloc.of(context); if (i < items.length) { final item = items[i]; From 2ee8b72a319d8357ac4709a1fc97c2e9ad745962 Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Tue, 24 Nov 2020 21:45:42 +0530 Subject: [PATCH 06/15] [UserListView] Use LazyLoadScrollView for pagination rather than listening scroll controller --- lib/src/lazy_load_scroll_view.dart | 81 ++++++++++++++++++++++++++++ lib/src/user_list_view.dart | 86 +++++++++++++----------------- 2 files changed, 119 insertions(+), 48 deletions(-) create mode 100644 lib/src/lazy_load_scroll_view.dart diff --git a/lib/src/lazy_load_scroll_view.dart b/lib/src/lazy_load_scroll_view.dart new file mode 100644 index 00000000..e83fc777 --- /dev/null +++ b/lib/src/lazy_load_scroll_view.dart @@ -0,0 +1,81 @@ +import 'package:flutter/widgets.dart'; + +enum LoadingStatus { LOADING, STABLE } + +/// Signature for EndOfPageListeners +typedef EndOfPageListenerCallback = void Function(); + +/// A widget that wraps a [Widget] and will trigger [onEndOfPage] when it +/// reaches the bottom of the list +class LazyLoadScrollView extends StatefulWidget { + /// The [Widget] that this widget watches for changes on + final Widget child; + + /// Called when the [child] reaches the end of the list + final EndOfPageListenerCallback onEndOfPage; + + /// The offset to take into account when triggering [onEndOfPage] in pixels + final int scrollOffset; + + /// Used to determine if loading of new data has finished. You should use set this if you aren't using a FutureBuilder or StreamBuilder + final bool isLoading; + + LazyLoadScrollView({ + Key key, + @required this.child, + @required this.onEndOfPage, + this.isLoading = false, + this.scrollOffset = 100, + }) : assert(onEndOfPage != null), + assert(child != null), + super(key: key); + + @override + State createState() => _LazyLoadScrollViewState(); +} + +class _LazyLoadScrollViewState extends State { + LoadingStatus _loadMoreStatus = LoadingStatus.STABLE; + + @override + void didUpdateWidget(LazyLoadScrollView oldWidget) { + super.didUpdateWidget(oldWidget); + if (!widget.isLoading) { + _loadMoreStatus = LoadingStatus.STABLE; + } + } + + @override + Widget build(BuildContext context) { + return NotificationListener( + child: widget.child, + onNotification: _onNotification, + ); + } + + bool _onNotification(Notification notification) { + if (notification is ScrollUpdateNotification) { + if (notification.metrics.maxScrollExtent > notification.metrics.pixels && + notification.metrics.maxScrollExtent - notification.metrics.pixels <= + widget.scrollOffset) { + if (_loadMoreStatus != null && + _loadMoreStatus == LoadingStatus.STABLE) { + _loadMoreStatus = LoadingStatus.LOADING; + widget.onEndOfPage(); + } + } + return true; + } + if (notification is OverscrollNotification) { + if (notification.overscroll > 0) { + if (_loadMoreStatus != null && + _loadMoreStatus == LoadingStatus.STABLE) { + _loadMoreStatus = LoadingStatus.LOADING; + widget.onEndOfPage(); + } + } + return true; + } + return false; + } +} diff --git a/lib/src/user_list_view.dart b/lib/src/user_list_view.dart index 046ad9c7..569eb38a 100644 --- a/lib/src/user_list_view.dart +++ b/lib/src/user_list_view.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:stream_chat/stream_chat.dart'; +import 'package:stream_chat_flutter/src/lazy_load_scroll_view.dart'; import 'package:stream_chat_flutter/src/users_bloc.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; @@ -144,8 +145,6 @@ class UserListView extends StatefulWidget { class _UserListViewState extends State with WidgetsBindingObserver { - final ScrollController _scrollController = ScrollController(); - bool get _isListView => widget.crossAxisCount == 1; @override @@ -158,14 +157,6 @@ class _UserListViewState extends State pagination: widget.pagination, options: widget.options, ); - - _scrollController.addListener(() { - usersBloc.queryUsersLoading.first.then((loading) { - if (!loading) { - _listenUserPagination(usersBloc); - } - }); - }); } @override @@ -327,32 +318,35 @@ class _UserListViewState extends State ); } - if (_isListView) { - return ListView.separated( - physics: AlwaysScrollableScrollPhysics(), - controller: _scrollController, - itemCount: items.isNotEmpty ? items.length + 1 : items.length, - separatorBuilder: (_, index) { - if (widget.separatorBuilder != null) { - return widget.separatorBuilder(context, index); - } - return _separatorBuilder(context, index); - }, - itemBuilder: (context, index) { - return _listItemBuilder(context, index, items); - }, - ); - } - return GridView.builder( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: widget.crossAxisCount, - ), - itemCount: items.isNotEmpty ? items.length + 1 : items.length, - physics: AlwaysScrollableScrollPhysics(), - controller: _scrollController, - itemBuilder: (context, index) { - return _gridItemBuilder(context, index, items); - }, + final child = _isListView + ? ListView.separated( + physics: AlwaysScrollableScrollPhysics(), + // controller: _scrollController, + itemCount: items.isNotEmpty ? items.length + 1 : items.length, + separatorBuilder: (_, index) { + if (widget.separatorBuilder != null) { + return widget.separatorBuilder(context, index); + } + return _separatorBuilder(context, index); + }, + itemBuilder: (context, index) { + return _listItemBuilder(context, index, items); + }, + ) + : GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: widget.crossAxisCount, + ), + itemCount: items.isNotEmpty ? items.length + 1 : items.length, + physics: AlwaysScrollableScrollPhysics(), + itemBuilder: (context, index) { + return _gridItemBuilder(context, index, items); + }, + ); + + return LazyLoadScrollView( + onEndOfPage: () => _listenUserPagination(usersBlocState), + child: child, ); }, ); @@ -488,18 +482,14 @@ class _UserListViewState extends State } void _listenUserPagination(UsersBlocState usersProvider) { - if (_scrollController.position.maxScrollExtent == - _scrollController.offset && - _scrollController.offset != 0) { - usersProvider.queryUsers( - filter: widget.filter, - sort: widget.sort, - pagination: widget.pagination.copyWith( - offset: usersProvider.users?.length ?? 0, - ), - options: widget.options, - ); - } + usersProvider.queryUsers( + filter: widget.filter, + sort: widget.sort, + pagination: widget.pagination.copyWith( + offset: usersProvider.users?.length ?? 0, + ), + options: widget.options, + ); } @override From fbc279f9dd900dd52cc00ed2f3f7baf0daf74d96 Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Tue, 24 Nov 2020 21:46:31 +0530 Subject: [PATCH 07/15] [NewGroupChatScreen] Float selected users and searchField when list is scrolling. --- example/lib/new_group_chat_screen.dart | 498 ++++++++++++++----------- 1 file changed, 273 insertions(+), 225 deletions(-) diff --git a/example/lib/new_group_chat_screen.dart b/example/lib/new_group_chat_screen.dart index 3c6ad2ba..f9cf1cba 100644 --- a/example/lib/new_group_chat_screen.dart +++ b/example/lib/new_group_chat_screen.dart @@ -24,11 +24,12 @@ class _NewGroupChatScreenState extends State { void _userNameListener() { if (_debounce?.isActive ?? false) _debounce.cancel(); _debounce = Timer(const Duration(milliseconds: 350), () { - if (mounted) + if (mounted) { setState(() { _userNameQuery = _controller.text; _isSearchActive = _userNameQuery.isNotEmpty; }); + } }); } @@ -46,247 +47,294 @@ class _NewGroupChatScreenState extends State { super.dispose(); } - @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Color.fromRGBO(252, 252, 252, 1), - appBar: AppBar( - elevation: 1, - backgroundColor: Colors.white, - leading: const StreamBackButton(), - title: Text( - 'Add Group Members', - style: TextStyle( - color: Colors.black, - fontSize: 16, - ), - ), - centerTitle: true, - actions: [ - if (_selectedUsers.isNotEmpty) - IconButton( - icon: Icon( - StreamIcons.arrow_right, - color: Color(0xFF006CFF), - ), - onPressed: () async { - final updatedList = await Navigator.push( - context, - MaterialPageRoute( - builder: (_) => GroupChatDetailsScreen( - selectedUsers: _selectedUsers.toList(growable: false), + return SafeArea( + child: UsersBloc( + child: Scaffold( + body: NestedScrollView( + floatHeaderSlivers: true, + headerSliverBuilder: + (BuildContext context, bool innerBoxIsScrolled) { + return [ + SliverAppBar( + // floating: true, + pinned: true, + forceElevated: true, + elevation: 1, + backgroundColor: Colors.white, + leading: const StreamBackButton(), + title: Text( + 'Add Group Members', + style: TextStyle( + color: Colors.black, + fontSize: 16, ), ), - ); - if (updatedList != null) { + centerTitle: true, + actions: [ + if (_selectedUsers.isNotEmpty) + IconButton( + icon: Icon( + StreamIcons.arrow_right, + color: Color(0xFF006CFF), + ), + onPressed: () async { + final updatedList = await Navigator.push( + context, + MaterialPageRoute( + builder: (_) => GroupChatDetailsScreen( + selectedUsers: + _selectedUsers.toList(growable: false), + ), + ), + ); + if (updatedList != null) { + setState(() { + _selectedUsers + ..clear() + ..addAll(updatedList); + }); + } + }, + ) + ], + ), + SliverToBoxAdapter( + child: Container( + height: 36, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all( + color: Colors.grey.shade300, + ), + borderRadius: BorderRadius.circular(24), + ), + margin: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 8, + ), + child: TextField( + controller: _controller, + decoration: InputDecoration( + prefixIcon: Icon( + StreamIcons.search, + color: Colors.black, + size: 24, + ), + hintText: 'Search', + hintStyle: TextStyle( + color: Colors.black.withOpacity(0.5), + fontSize: 14, + ), + contentPadding: const EdgeInsets.all(0), + border: OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(24), + ), + ), + ), + ), + ), + if (_selectedUsers.isNotEmpty) + SliverToBoxAdapter( + child: Container( + height: 104, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: _selectedUsers.length, + padding: const EdgeInsets.all(8), + separatorBuilder: (_, __) => SizedBox(width: 16), + itemBuilder: (_, index) { + final user = _selectedUsers.elementAt(index); + return Column( + children: [ + Stack( + children: [ + UserAvatar( + onlineIndicatorAlignment: + Alignment(0.9, 0.9), + user: user, + showOnlineStatus: true, + borderRadius: BorderRadius.circular(32), + constraints: BoxConstraints.tightFor( + height: 64, + width: 64, + ), + ), + Positioned( + top: -4, + right: -4, + child: GestureDetector( + onTap: () { + if (_selectedUsers.contains(user)) { + setState(() => + _selectedUsers.remove(user)); + } + }, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + border: Border.all( + color: Colors.grey.shade100, + ), + ), + child: Padding( + padding: const EdgeInsets.all(0.0), + child: Icon( + StreamIcons.close, + size: 24, + ), + ), + ), + ), + ) + ], + ), + SizedBox(height: 4), + Text( + user.name.split(' ')[0], + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 12, + ), + ), + ], + ); + }, + ), + ), + ), + SliverPersistentHeader( + pinned: true, + delegate: _HeaderDelegate( + height: 30, + child: Container( + width: double.maxFinite, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + Colors.black.withOpacity(0.02), + Colors.white.withOpacity(0.05), + ], + stops: [0, 1], + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 8, + ), + child: Text( + _isSearchActive + ? 'Matches for \"$_userNameQuery\"' + : 'On the platform', + style: TextStyle( + color: Colors.black.withOpacity(0.5), + ), + ), + ), + ), + ), + ), + ]; + }, + body: UserListView( + selectedUsers: _selectedUsers, + pullToRefresh: false, + groupAlphabetically: _isSearchActive ? false : true, + onUserTap: (user, _) { + if (!_selectedUsers.contains(user)) { setState(() { - _selectedUsers - ..clear() - ..addAll(updatedList); + _selectedUsers.add(user); + }); + } else { + setState(() { + _selectedUsers.remove(user); }); } }, - ) - ], - ), - body: UsersBloc( - child: Column( - children: [ - Container( - height: 36, - decoration: BoxDecoration( - color: Colors.white, - border: Border.all( - color: Colors.grey.shade300, + pagination: PaginationParams( + limit: 25, + ), + filter: { + if (_userNameQuery.isNotEmpty) + 'name': { + r'$autocomplete': _userNameQuery, + }, + 'id': { + r'$ne': StreamChat.of(context).user.id, + } + }, + sort: [ + SortOption( + 'name', + direction: 1, ), - borderRadius: BorderRadius.circular(24), - ), - margin: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 8, - ), - child: TextField( - controller: _controller, - decoration: InputDecoration( - prefixIcon: Icon( - StreamIcons.search, - color: Colors.black, - size: 24, - ), - hintText: 'Search', - hintStyle: TextStyle( - color: Colors.black.withOpacity(0.5), - fontSize: 14, - ), - contentPadding: const EdgeInsets.all(0), - border: OutlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(24), - ), - ), - ), - ), - if (_selectedUsers.isNotEmpty) - Container( - height: 104, - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: _selectedUsers.length, - padding: const EdgeInsets.all(8), - separatorBuilder: (_, __) => SizedBox(width: 16), - itemBuilder: (_, index) { - final user = _selectedUsers.elementAt(index); - return Column( - children: [ - Stack( - children: [ - UserAvatar( - onlineIndicatorAlignment: Alignment(0.9, 0.9), - user: user, - showOnlineStatus: true, - borderRadius: BorderRadius.circular(32), - constraints: BoxConstraints.tightFor( - height: 64, - width: 64, - ), - ), - Positioned( - top: -4, - right: -4, - child: GestureDetector( - onTap: () { - if (_selectedUsers.contains(user)) { - setState(() => _selectedUsers.remove(user)); - } - }, - child: Container( - decoration: BoxDecoration( - color: Colors.white, - shape: BoxShape.circle, - border: Border.all( - color: Colors.grey.shade100, - ), - ), - child: Padding( - padding: const EdgeInsets.all(0.0), - child: Icon( - StreamIcons.close, - size: 24, - ), - ), + ], + emptyBuilder: (_) { + return LayoutBuilder( + builder: (context, viewportConstraints) { + return SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: viewportConstraints.maxHeight, + ), + child: Center( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(24), + child: Icon( + StreamIcons.search, + size: 96, + color: Colors.grey, ), ), - ) - ], - ), - SizedBox(height: 4), - Text( - user.name.split(' ')[0], - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 12, + Text('No user matches these keywords...'), + ], ), ), - ], + ), ); }, - ), - ), - Container( - width: double.maxFinite, - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.centerLeft, - end: Alignment.centerRight, - colors: [ - Colors.black.withOpacity(0.02), - Colors.white.withOpacity(0.05), - ], - stops: [0, 1], - ), - ), - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 8, - ), - child: Text( - _isSearchActive - ? 'Matches for \"$_userNameQuery\"' - : 'On the platform', - style: TextStyle( - color: Colors.black.withOpacity(0.5), - ), - ), - ), + ); + }, ), - Expanded( - child: UserListView( - selectedUsers: _selectedUsers, - groupAlphabetically: _isSearchActive ? false : true, - onUserTap: (user, _) { - if (!_selectedUsers.contains(user)) { - setState(() { - _selectedUsers.add(user); - }); - } else { - setState(() { - _selectedUsers.remove(user); - }); - } - }, - pagination: PaginationParams( - limit: 25, - ), - filter: { - if (_userNameQuery.isNotEmpty) - 'name': { - r'$autocomplete': _userNameQuery, - }, - 'id': { - r'$ne': StreamChat.of(context).user.id, - } - }, - sort: [ - SortOption( - 'name', - direction: 1, - ), - ], - emptyBuilder: (_) { - return LayoutBuilder( - builder: (context, viewportConstraints) { - return SingleChildScrollView( - physics: AlwaysScrollableScrollPhysics(), - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: Center( - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(24), - child: Icon( - StreamIcons.search, - size: 96, - color: Colors.grey, - ), - ), - Text('No user matches these keywords...'), - ], - ), - ), - ), - ); - }, - ); - }, - ), - ), - ], + ), ), ), ); } } + +class _HeaderDelegate extends SliverPersistentHeaderDelegate { + final Widget child; + final double height; + + const _HeaderDelegate({ + @required this.child, + @required this.height, + }); + + @override + Widget build( + BuildContext context, double shrinkOffset, bool overlapsContent) { + return Container( + color: Colors.white, + child: child, + ); + } + + @override + double get maxExtent => height; + + @override + double get minExtent => height; + + @override + bool shouldRebuild(_HeaderDelegate oldDelegate) => true; +} From 3cda66a255f7a70d6aa57f8ba2fa1f2133ad1ce8 Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Wed, 25 Nov 2020 11:41:07 +0530 Subject: [PATCH 08/15] Minor merge fixes --- example/lib/group_chat_details_screen.dart | 314 ++++++++++--------- example/lib/new_group_chat_screen.dart | 344 ++++++++++----------- 2 files changed, 331 insertions(+), 327 deletions(-) diff --git a/example/lib/group_chat_details_screen.dart b/example/lib/group_chat_details_screen.dart index 050ea993..fc8a6890 100644 --- a/example/lib/group_chat_details_screen.dart +++ b/example/lib/group_chat_details_screen.dart @@ -52,169 +52,175 @@ class _GroupChatDetailsScreenState extends State { @override Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Color.fromRGBO(252, 252, 252, 1), - appBar: AppBar( - elevation: 1, - backgroundColor: Colors.white, - leading: const StreamBackButton(), - title: Text( - 'Name of Group Chat', - style: TextStyle( - color: Colors.black, - fontSize: 16, - ), - ), - centerTitle: true, - bottom: PreferredSize( - preferredSize: Size.fromHeight(kToolbarHeight), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 16), - child: Row( - children: [ - Text( - 'NAME', - style: TextStyle( - fontSize: 12, - color: Colors.black.withOpacity(0.5), - ), - ), - SizedBox(width: 16), - Expanded( - child: TextField( - controller: _groupNameController, - decoration: InputDecoration( - isDense: true, - border: InputBorder.none, - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, - errorBorder: InputBorder.none, - disabledBorder: InputBorder.none, - contentPadding: const EdgeInsets.all(0), - hintText: 'Choose a group chat name', - hintStyle: TextStyle( - fontSize: 14, color: Colors.black.withOpacity(.5)), - ), - ), - ), - ], + return WillPopScope( + onWillPop: () async { + Navigator.pop(context, _selectedUsers); + return false; + }, + child: Scaffold( + backgroundColor: Color.fromRGBO(252, 252, 252, 1), + appBar: AppBar( + elevation: 1, + backgroundColor: Colors.white, + leading: const StreamBackButton(), + title: Text( + 'Name of Group Chat', + style: TextStyle( + color: Colors.black, + fontSize: 16, ), ), - ), - actions: [ - NeumorphicButton( - child: IconButton( - padding: const EdgeInsets.all(0), - icon: StreamSvgIcon.check( - size: 24, - color: _isGroupNameEmpty ? Colors.grey : Color(0xFF006CFF), - ), - onPressed: _isGroupNameEmpty - ? null - : () async { - final groupName = _groupNameController.text; - final client = StreamChat.of(context).client; - final channel = client - .channel('messaging', id: Uuid().v4(), extraData: { - 'members': [ - client.state.user.id, - ..._selectedUsers.map((e) => e.id), - ], - 'name': groupName, - }); - await channel.watch(); - Navigator.of(context) - ..pop() - ..pushReplacement( - MaterialPageRoute( - builder: (context) { - return StreamChannel( - child: ChannelPage(), - channel: channel, - ); - }, - ), - ); - }, - ), - ), - ], - ), - body: Column( - children: [ - Container( - width: double.maxFinite, - color: Colors.grey.shade50, + centerTitle: true, + bottom: PreferredSize( + preferredSize: Size.fromHeight(kToolbarHeight), child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 8, + padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 16), + child: Row( + children: [ + Text( + 'NAME', + style: TextStyle( + fontSize: 12, + color: Colors.black.withOpacity(0.5), + ), + ), + SizedBox(width: 16), + Expanded( + child: TextField( + controller: _groupNameController, + decoration: InputDecoration( + isDense: true, + border: InputBorder.none, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + errorBorder: InputBorder.none, + disabledBorder: InputBorder.none, + contentPadding: const EdgeInsets.all(0), + hintText: 'Choose a group chat name', + hintStyle: TextStyle( + fontSize: 14, color: Colors.black.withOpacity(.5)), + ), + ), + ), + ], ), - child: Text( - '$_totalUsers ${_totalUsers > 1 ? 'Members' : 'Member'}', - style: TextStyle( - fontWeight: FontWeight.w500, + ), + ), + actions: [ + StreamNeumorphicButton( + child: IconButton( + padding: const EdgeInsets.all(0), + icon: StreamSvgIcon.check( + size: 24, + color: _isGroupNameEmpty ? Colors.grey : Color(0xFF006CFF), + ), + onPressed: _isGroupNameEmpty + ? null + : () async { + final groupName = _groupNameController.text; + final client = StreamChat.of(context).client; + final channel = client + .channel('messaging', id: Uuid().v4(), extraData: { + 'members': [ + client.state.user.id, + ..._selectedUsers.map((e) => e.id), + ], + 'name': groupName, + }); + await channel.watch(); + Navigator.of(context) + ..pop() + ..pushReplacement( + MaterialPageRoute( + builder: (context) { + return StreamChannel( + child: ChannelPage(), + channel: channel, + ); + }, + ), + ); + }, + ), + ), + ], + ), + body: Column( + children: [ + Container( + width: double.maxFinite, + color: Colors.grey.shade50, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 8, + ), + child: Text( + '$_totalUsers ${_totalUsers > 1 ? 'Members' : 'Member'}', + style: TextStyle( + fontWeight: FontWeight.w500, + ), ), ), ), - ), - Expanded( - child: ListView.separated( - itemCount: _selectedUsers.length + 1, - separatorBuilder: (_, __) => Container( - height: 1, - color: Theme.of(context).brightness == Brightness.dark - ? Colors.white.withOpacity(0.1) - : Colors.black.withOpacity(0.1), - ), - itemBuilder: (_, index) { - if (index == _selectedUsers.length) { - return Container( - height: 1, - color: Theme.of(context).brightness == Brightness.dark - ? Colors.white.withOpacity(0.1) - : Colors.black.withOpacity(0.1), + Expanded( + child: ListView.separated( + itemCount: _selectedUsers.length + 1, + separatorBuilder: (_, __) => Container( + height: 1, + color: Theme.of(context).brightness == Brightness.dark + ? Colors.white.withOpacity(0.1) + : Colors.black.withOpacity(0.1), + ), + itemBuilder: (_, index) { + if (index == _selectedUsers.length) { + return Container( + height: 1, + color: Theme.of(context).brightness == Brightness.dark + ? Colors.white.withOpacity(0.1) + : Colors.black.withOpacity(0.1), + ); + } + final user = _selectedUsers[index]; + return ListTile( + key: ObjectKey(user), + leading: UserAvatar( + user: user, + constraints: BoxConstraints.tightFor( + width: 40, + height: 40, + ), + ), + title: Text( + user.name, + style: TextStyle(fontWeight: FontWeight.bold), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 8, + ), + trailing: IconButton( + icon: Icon( + Icons.clear_rounded, + color: Colors.black, + ), + padding: const EdgeInsets.all(0), + splashRadius: 24, + onPressed: () { + setState(() { + _selectedUsers.remove(user); + }); + if (_selectedUsers.isEmpty) { + Navigator.pop(context, _selectedUsers); + } + }, + ), ); - } - final user = _selectedUsers[index]; - return ListTile( - key: ObjectKey(user), - leading: UserAvatar( - user: user, - constraints: BoxConstraints.tightFor( - width: 40, - height: 40, - ), - ), - title: Text( - user.name, - style: TextStyle(fontWeight: FontWeight.bold), - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 8, - ), - trailing: IconButton( - icon: Icon( - Icons.clear_rounded, - color: Colors.black, - ), - padding: const EdgeInsets.all(0), - splashRadius: 24, - onPressed: () { - setState(() { - _selectedUsers.remove(user); - }); - if (_selectedUsers.isEmpty) { - Navigator.pop(context); - } - }, - ), - ); - }, + }, + ), ), - ), - ], + ], + ), ), ); } diff --git a/example/lib/new_group_chat_screen.dart b/example/lib/new_group_chat_screen.dart index 003aa431..1587c464 100644 --- a/example/lib/new_group_chat_screen.dart +++ b/example/lib/new_group_chat_screen.dart @@ -50,198 +50,196 @@ class _NewGroupChatScreenState extends State { @override Widget build(BuildContext context) { return SafeArea( - child: UsersBloc( - child: Scaffold( - backgroundColor: Color.fromRGBO(252, 252, 252, 1), - body: NestedScrollView( - floatHeaderSlivers: true, - headerSliverBuilder: - (BuildContext context, bool innerBoxIsScrolled) { - return [ - SliverAppBar( - // floating: true, - pinned: true, - forceElevated: true, - elevation: 1, - backgroundColor: Colors.white, - leading: const StreamBackButton(), - title: Text( - 'Add Group Members', - style: TextStyle( - color: Colors.black, - fontSize: 16, + child: Scaffold( + backgroundColor: Color.fromRGBO(252, 252, 252, 1), + body: NestedScrollView( + floatHeaderSlivers: true, + headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { + return [ + SliverAppBar( + // floating: true, + pinned: true, + forceElevated: true, + elevation: 1, + backgroundColor: Colors.white, + leading: const StreamBackButton(), + title: Text( + 'Add Group Members', + style: TextStyle( + color: Colors.black, + fontSize: 16, + ), + ), + centerTitle: true, + actions: [ + if (_selectedUsers.isNotEmpty) + IconButton( + icon: StreamSvgIcon.right( + color: Color(0xFF006CFF), + ), + onPressed: () async { + final updatedList = await Navigator.push( + context, + MaterialPageRoute( + builder: (_) => GroupChatDetailsScreen( + selectedUsers: + _selectedUsers.toList(growable: false), + ), + ), + ); + if (updatedList != null) { + setState(() { + _selectedUsers + ..clear() + ..addAll(updatedList); + }); + } + }, + ) + ], + ), + SliverToBoxAdapter( + child: Container( + height: 36, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all( + color: Colors.grey.shade300, + ), + borderRadius: BorderRadius.circular(24), + ), + margin: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 8, + ), + child: TextField( + controller: _controller, + decoration: InputDecoration( + prefixIcon: StreamSvgIcon.search( + color: Colors.black, + size: 24, + ), + hintText: 'Search', + hintStyle: TextStyle( + color: Colors.black.withOpacity(0.5), + fontSize: 14, + ), + contentPadding: const EdgeInsets.all(0), + border: OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(24), + ), ), ), - centerTitle: true, - actions: [ - if (_selectedUsers.isNotEmpty) - IconButton( - icon: StreamSvgIcon.right( - color: Color(0xFF006CFF), - ), - onPressed: () async { - final updatedList = await Navigator.push( - context, - MaterialPageRoute( - builder: (_) => GroupChatDetailsScreen( - selectedUsers: - _selectedUsers.toList(growable: false), - ), - ), - ); - if (updatedList != null) { - setState(() { - _selectedUsers - ..clear() - ..addAll(updatedList); - }); - } - }, - ) - ], ), + ), + if (_selectedUsers.isNotEmpty) SliverToBoxAdapter( child: Container( - height: 36, - decoration: BoxDecoration( - color: Colors.white, - border: Border.all( - color: Colors.grey.shade300, - ), - borderRadius: BorderRadius.circular(24), - ), - margin: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 8, - ), - child: TextField( - controller: _controller, - decoration: InputDecoration( - prefixIcon: StreamSvgIcon.search( - color: Colors.black, - size: 24, - ), - hintText: 'Search', - hintStyle: TextStyle( - color: Colors.black.withOpacity(0.5), - fontSize: 14, - ), - contentPadding: const EdgeInsets.all(0), - border: OutlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(24), - ), - ), - ), - ), - ), - if (_selectedUsers.isNotEmpty) - SliverToBoxAdapter( - child: Container( - height: 104, - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: _selectedUsers.length, - padding: const EdgeInsets.all(8), - separatorBuilder: (_, __) => SizedBox(width: 16), - itemBuilder: (_, index) { - final user = _selectedUsers.elementAt(index); - return Column( - children: [ - Stack( - children: [ - UserAvatar( - onlineIndicatorAlignment: - Alignment(0.9, 0.9), - user: user, - showOnlineStatus: true, - borderRadius: BorderRadius.circular(32), - constraints: BoxConstraints.tightFor( - height: 64, - width: 64, - ), + height: 104, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: _selectedUsers.length, + padding: const EdgeInsets.all(8), + separatorBuilder: (_, __) => SizedBox(width: 16), + itemBuilder: (_, index) { + final user = _selectedUsers.elementAt(index); + return Column( + children: [ + Stack( + children: [ + UserAvatar( + onlineIndicatorAlignment: Alignment(0.9, 0.9), + user: user, + showOnlineStatus: true, + borderRadius: BorderRadius.circular(32), + constraints: BoxConstraints.tightFor( + height: 64, + width: 64, ), - Positioned( - top: -4, - right: -4, - child: GestureDetector( - onTap: () { - if (_selectedUsers.contains(user)) { - setState(() => - _selectedUsers.remove(user)); - } - }, - child: Container( - decoration: BoxDecoration( - color: Colors.white, - shape: BoxShape.circle, - border: Border.all( - color: Colors.grey.shade100, - ), + ), + Positioned( + top: -4, + right: -4, + child: GestureDetector( + onTap: () { + if (_selectedUsers.contains(user)) { + setState( + () => _selectedUsers.remove(user)); + } + }, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + border: Border.all( + color: Colors.grey.shade100, ), - child: Padding( - padding: const EdgeInsets.all(0.0), - child: StreamSvgIcon.close( - color: Colors.black, - size: 24, - ), + ), + child: Padding( + padding: const EdgeInsets.all(0.0), + child: StreamSvgIcon.close( + color: Colors.black, + size: 24, ), ), ), - ) - ], + ), + ) + ], + ), + SizedBox(height: 4), + Text( + user.name.split(' ')[0], + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 12, ), - SizedBox(height: 4), - Text( - user.name.split(' ')[0], - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 12, - ), - ), - ], - ); - }, - ), + ), + ], + ); + }, ), ), - SliverPersistentHeader( - pinned: true, - delegate: _HeaderDelegate( - height: 30, - child: Container( - width: double.maxFinite, - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.centerLeft, - end: Alignment.centerRight, - colors: [ - Colors.black.withOpacity(0.02), - Colors.white.withOpacity(0.05), - ], - stops: [0, 1], - ), + ), + SliverPersistentHeader( + pinned: true, + delegate: _HeaderDelegate( + height: 30, + child: Container( + width: double.maxFinite, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + Colors.black.withOpacity(0.02), + Colors.white.withOpacity(0.05), + ], + stops: [0, 1], ), - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 8, - ), - child: Text( - _isSearchActive - ? 'Matches for \"$_userNameQuery\"' - : 'On the platform', - style: TextStyle( - color: Colors.black.withOpacity(0.5), - ), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 8, + ), + child: Text( + _isSearchActive + ? 'Matches for \"$_userNameQuery\"' + : 'On the platform', + style: TextStyle( + color: Colors.black.withOpacity(0.5), ), ), ), ), ), - ]; - }, - body: UserListView( + ), + ]; + }, + body: UsersBloc( + child: UserListView( selectedUsers: _selectedUsers, pullToRefresh: false, groupAlphabetically: _isSearchActive ? false : true, From 177d50a51153d71b183a98784c925945e8cc7dcf Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Wed, 25 Nov 2020 15:41:06 +0530 Subject: [PATCH 09/15] Hide keyboard on scrolling user list --- example/lib/group_chat_details_screen.dart | 108 ++++++++++--------- example/lib/new_chat_screen.dart | 116 ++++++++++---------- example/lib/new_group_chat_screen.dart | 120 +++++++++++---------- 3 files changed, 178 insertions(+), 166 deletions(-) diff --git a/example/lib/group_chat_details_screen.dart b/example/lib/group_chat_details_screen.dart index fc8a6890..36999c7f 100644 --- a/example/lib/group_chat_details_screen.dart +++ b/example/lib/group_chat_details_screen.dart @@ -164,59 +164,63 @@ class _GroupChatDetailsScreenState extends State { ), ), Expanded( - child: ListView.separated( - itemCount: _selectedUsers.length + 1, - separatorBuilder: (_, __) => Container( - height: 1, - color: Theme.of(context).brightness == Brightness.dark - ? Colors.white.withOpacity(0.1) - : Colors.black.withOpacity(0.1), - ), - itemBuilder: (_, index) { - if (index == _selectedUsers.length) { - return Container( - height: 1, - color: Theme.of(context).brightness == Brightness.dark - ? Colors.white.withOpacity(0.1) - : Colors.black.withOpacity(0.1), + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onPanDown: (_) => FocusScope.of(context).unfocus(), + child: ListView.separated( + itemCount: _selectedUsers.length + 1, + separatorBuilder: (_, __) => Container( + height: 1, + color: Theme.of(context).brightness == Brightness.dark + ? Colors.white.withOpacity(0.1) + : Colors.black.withOpacity(0.1), + ), + itemBuilder: (_, index) { + if (index == _selectedUsers.length) { + return Container( + height: 1, + color: Theme.of(context).brightness == Brightness.dark + ? Colors.white.withOpacity(0.1) + : Colors.black.withOpacity(0.1), + ); + } + final user = _selectedUsers[index]; + return ListTile( + key: ObjectKey(user), + leading: UserAvatar( + user: user, + constraints: BoxConstraints.tightFor( + width: 40, + height: 40, + ), + ), + title: Text( + user.name, + style: TextStyle(fontWeight: FontWeight.bold), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 8, + ), + trailing: IconButton( + icon: Icon( + Icons.clear_rounded, + color: Colors.black, + ), + padding: const EdgeInsets.all(0), + splashRadius: 24, + onPressed: () { + setState(() { + _selectedUsers.remove(user); + }); + if (_selectedUsers.isEmpty) { + Navigator.pop(context, _selectedUsers); + } + }, + ), ); - } - final user = _selectedUsers[index]; - return ListTile( - key: ObjectKey(user), - leading: UserAvatar( - user: user, - constraints: BoxConstraints.tightFor( - width: 40, - height: 40, - ), - ), - title: Text( - user.name, - style: TextStyle(fontWeight: FontWeight.bold), - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 8, - ), - trailing: IconButton( - icon: Icon( - Icons.clear_rounded, - color: Colors.black, - ), - padding: const EdgeInsets.all(0), - splashRadius: 24, - onPressed: () { - setState(() { - _selectedUsers.remove(user); - }); - if (_selectedUsers.isEmpty) { - Navigator.pop(context, _selectedUsers); - } - }, - ), - ); - }, + }, + ), ), ), ], diff --git a/example/lib/new_chat_screen.dart b/example/lib/new_chat_screen.dart index 690e831f..5e6af8fc 100644 --- a/example/lib/new_chat_screen.dart +++ b/example/lib/new_chat_screen.dart @@ -243,67 +243,71 @@ class _NewChatScreenState extends State { ), Expanded( child: _showUserList - ? UsersBloc( - child: UserListView( - selectedUsers: _selectedUsers, - groupAlphabetically: _isSearchActive ? false : true, - onUserTap: (user, _) { - _controller.clear(); - if (!_selectedUsers.contains(user)) { - _chipInputTextFieldState - ..addItem(user) - ..pauseItemAddition(); - } else { - _chipInputTextFieldState.removeItem(user); - } - }, - pagination: PaginationParams( - limit: 25, - ), - filter: { - if (_userNameQuery.isNotEmpty) - 'name': { - r'$autocomplete': _userNameQuery, - }, - 'id': { - r'$ne': StreamChat.of(context).user.id, + ? GestureDetector( + behavior: HitTestBehavior.opaque, + onPanDown: (_) => FocusScope.of(context).unfocus(), + child: UsersBloc( + child: UserListView( + selectedUsers: _selectedUsers, + groupAlphabetically: _isSearchActive ? false : true, + onUserTap: (user, _) { + _controller.clear(); + if (!_selectedUsers.contains(user)) { + _chipInputTextFieldState + ..addItem(user) + ..pauseItemAddition(); + } else { + _chipInputTextFieldState.removeItem(user); + } }, - }, - sort: [ - SortOption( - 'name', - direction: 1, + pagination: PaginationParams( + limit: 25, ), - ], - emptyBuilder: (_) { - return LayoutBuilder( - builder: (context, viewportConstraints) { - return SingleChildScrollView( - physics: AlwaysScrollableScrollPhysics(), - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: Center( - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(24), - child: StreamSvgIcon.search( - size: 96, - color: Colors.grey, + filter: { + if (_userNameQuery.isNotEmpty) + 'name': { + r'$autocomplete': _userNameQuery, + }, + 'id': { + r'$ne': StreamChat.of(context).user.id, + }, + }, + sort: [ + SortOption( + 'name', + direction: 1, + ), + ], + emptyBuilder: (_) { + return LayoutBuilder( + builder: (context, viewportConstraints) { + return SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: viewportConstraints.maxHeight, + ), + child: Center( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(24), + child: StreamSvgIcon.search( + size: 96, + color: Colors.grey, + ), ), - ), - Text( - 'No user matches these keywords...'), - ], + Text( + 'No user matches these keywords...'), + ], + ), ), ), - ), - ); - }, - ); - }, + ); + }, + ); + }, + ), ), ) : MessageListView(), diff --git a/example/lib/new_group_chat_screen.dart b/example/lib/new_group_chat_screen.dart index 1587c464..e954f08f 100644 --- a/example/lib/new_group_chat_screen.dart +++ b/example/lib/new_group_chat_screen.dart @@ -238,68 +238,72 @@ class _NewGroupChatScreenState extends State { ), ]; }, - body: UsersBloc( - child: UserListView( - selectedUsers: _selectedUsers, - pullToRefresh: false, - groupAlphabetically: _isSearchActive ? false : true, - onUserTap: (user, _) { - if (!_selectedUsers.contains(user)) { - setState(() { - _selectedUsers.add(user); - }); - } else { - setState(() { - _selectedUsers.remove(user); - }); - } - }, - pagination: PaginationParams( - limit: 25, - ), - filter: { - if (_userNameQuery.isNotEmpty) - 'name': { - r'$autocomplete': _userNameQuery, - }, - 'id': { - r'$ne': StreamChat.of(context).user.id, - } - }, - sort: [ - SortOption( - 'name', - direction: 1, + body: GestureDetector( + behavior: HitTestBehavior.opaque, + onPanDown: (_) => FocusScope.of(context).unfocus(), + child: UsersBloc( + child: UserListView( + selectedUsers: _selectedUsers, + pullToRefresh: false, + groupAlphabetically: _isSearchActive ? false : true, + onUserTap: (user, _) { + if (!_selectedUsers.contains(user)) { + setState(() { + _selectedUsers.add(user); + }); + } else { + setState(() { + _selectedUsers.remove(user); + }); + } + }, + pagination: PaginationParams( + limit: 25, ), - ], - emptyBuilder: (_) { - return LayoutBuilder( - builder: (context, viewportConstraints) { - return SingleChildScrollView( - physics: AlwaysScrollableScrollPhysics(), - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: Center( - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(24), - child: StreamSvgIcon.search( - size: 96, - color: Colors.grey, + filter: { + if (_userNameQuery.isNotEmpty) + 'name': { + r'$autocomplete': _userNameQuery, + }, + 'id': { + r'$ne': StreamChat.of(context).user.id, + } + }, + sort: [ + SortOption( + 'name', + direction: 1, + ), + ], + emptyBuilder: (_) { + return LayoutBuilder( + builder: (context, viewportConstraints) { + return SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: viewportConstraints.maxHeight, + ), + child: Center( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(24), + child: StreamSvgIcon.search( + size: 96, + color: Colors.grey, + ), ), - ), - Text('No user matches these keywords...'), - ], + Text('No user matches these keywords...'), + ], + ), ), ), - ), - ); - }, - ); - }, + ); + }, + ); + }, + ), ), ), ), From ef403d53c9595f69a609567a34f82c3a344b4963 Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Wed, 25 Nov 2020 15:49:59 +0530 Subject: [PATCH 10/15] remove comments --- example/lib/new_group_chat_screen.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/example/lib/new_group_chat_screen.dart b/example/lib/new_group_chat_screen.dart index e954f08f..dfa4b000 100644 --- a/example/lib/new_group_chat_screen.dart +++ b/example/lib/new_group_chat_screen.dart @@ -57,7 +57,6 @@ class _NewGroupChatScreenState extends State { headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { return [ SliverAppBar( - // floating: true, pinned: true, forceElevated: true, elevation: 1, From 850657cc638a0a95640bcbff5961cf81b00e7884 Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Wed, 25 Nov 2020 15:54:19 +0530 Subject: [PATCH 11/15] fix icon --- example/lib/new_group_chat_screen.dart | 2 +- lib/src/stream_svg_icon.dart | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/example/lib/new_group_chat_screen.dart b/example/lib/new_group_chat_screen.dart index dfa4b000..11b6aa14 100644 --- a/example/lib/new_group_chat_screen.dart +++ b/example/lib/new_group_chat_screen.dart @@ -73,7 +73,7 @@ class _NewGroupChatScreenState extends State { actions: [ if (_selectedUsers.isNotEmpty) IconButton( - icon: StreamSvgIcon.right( + icon: StreamSvgIcon.arrow_right( color: Color(0xFF006CFF), ), onPressed: () async { diff --git a/lib/src/stream_svg_icon.dart b/lib/src/stream_svg_icon.dart index 2851df3a..7ed1fca8 100644 --- a/lib/src/stream_svg_icon.dart +++ b/lib/src/stream_svg_icon.dart @@ -373,4 +373,16 @@ class StreamSvgIcon extends StatelessWidget { height: size, ); } + + factory StreamSvgIcon.arrow_right({ + double size, + Color color, + }) { + return StreamSvgIcon( + assetName: 'Icon_arrow_right.svg', + color: color, + width: size, + height: size, + ); + } } From cd84555f74b3c18654c16585ebf9d61b6a530754 Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Wed, 25 Nov 2020 16:43:35 +0530 Subject: [PATCH 12/15] extract searchTextField into a separate widget --- example/lib/new_group_chat_screen.dart | 35 +---------- example/lib/search_text_field.dart | 81 ++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 32 deletions(-) create mode 100644 example/lib/search_text_field.dart diff --git a/example/lib/new_group_chat_screen.dart b/example/lib/new_group_chat_screen.dart index 11b6aa14..ece3ca9a 100644 --- a/example/lib/new_group_chat_screen.dart +++ b/example/lib/new_group_chat_screen.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'group_chat_details_screen.dart'; +import 'search_text_field.dart'; class NewGroupChatScreen extends StatefulWidget { @override @@ -98,38 +99,8 @@ class _NewGroupChatScreenState extends State { ], ), SliverToBoxAdapter( - child: Container( - height: 36, - decoration: BoxDecoration( - color: Colors.white, - border: Border.all( - color: Colors.grey.shade300, - ), - borderRadius: BorderRadius.circular(24), - ), - margin: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 8, - ), - child: TextField( - controller: _controller, - decoration: InputDecoration( - prefixIcon: StreamSvgIcon.search( - color: Colors.black, - size: 24, - ), - hintText: 'Search', - hintStyle: TextStyle( - color: Colors.black.withOpacity(0.5), - fontSize: 14, - ), - contentPadding: const EdgeInsets.all(0), - border: OutlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(24), - ), - ), - ), + child: SearchTextField( + controller: _controller, ), ), if (_selectedUsers.isNotEmpty) diff --git a/example/lib/search_text_field.dart b/example/lib/search_text_field.dart new file mode 100644 index 00000000..7fcd3727 --- /dev/null +++ b/example/lib/search_text_field.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; + +class SearchTextField extends StatelessWidget { + final TextEditingController controller; + final ValueChanged onChanged; + final String hintText; + final VoidCallback onTap; + + const SearchTextField({ + Key key, + @required this.controller, + this.onChanged, + this.onTap, + this.hintText = 'Search', + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + height: 36, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all( + color: Colors.grey.shade300, + ), + borderRadius: BorderRadius.circular(24), + ), + margin: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 8, + ), + child: Row( + children: [ + Expanded( + child: TextField( + onTap: onTap, + controller: controller, + onChanged: onChanged, + decoration: InputDecoration( + prefixIcon: StreamSvgIcon.search( + color: Colors.black, + size: 24, + ), + hintText: hintText, + hintStyle: TextStyle( + color: Colors.black.withOpacity(0.5), + fontSize: 14, + ), + contentPadding: const EdgeInsets.all(0), + border: OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(24), + ), + ), + ), + ), + // Material( + // + // child: IconButton( + // icon: StreamSvgIcon.close( + // color: Colors.grey, + // ), + // splashRadius: 24, + // onPressed: () { + // if (controller.text.isNotEmpty) { + // Future.microtask( + // () => [ + // controller.clear(), + // onChanged(''), + // ], + // ); + // } + // }, + // ), + // ), + ], + ), + ); + } +} From 806b34f0922c642e488ab8476f670af195c2d19d Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Wed, 25 Nov 2020 16:59:54 +0530 Subject: [PATCH 13/15] minor changes --- example/lib/search_text_field.dart | 42 ++++++++++++++++-------------- lib/src/stream_svg_icon.dart | 12 +++++++++ 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/example/lib/search_text_field.dart b/example/lib/search_text_field.dart index 7fcd3727..8461d735 100644 --- a/example/lib/search_text_field.dart +++ b/example/lib/search_text_field.dart @@ -6,6 +6,7 @@ class SearchTextField extends StatelessWidget { final ValueChanged onChanged; final String hintText; final VoidCallback onTap; + final bool showCloseButton; const SearchTextField({ Key key, @@ -13,6 +14,7 @@ class SearchTextField extends StatelessWidget { this.onChanged, this.onTap, this.hintText = 'Search', + this.showCloseButton = true, }) : super(key: key); @override @@ -55,25 +57,27 @@ class SearchTextField extends StatelessWidget { ), ), ), - // Material( - // - // child: IconButton( - // icon: StreamSvgIcon.close( - // color: Colors.grey, - // ), - // splashRadius: 24, - // onPressed: () { - // if (controller.text.isNotEmpty) { - // Future.microtask( - // () => [ - // controller.clear(), - // onChanged(''), - // ], - // ); - // } - // }, - // ), - // ), + if (showCloseButton) + Material( + color: Colors.transparent, + child: IconButton( + padding: const EdgeInsets.all(0), + icon: StreamSvgIcon.close_small( + color: Colors.grey, + ), + splashRadius: 24, + onPressed: () { + if (controller.text.isNotEmpty) { + Future.microtask( + () => [ + controller.clear(), + onChanged(''), + ], + ); + } + }, + ), + ), ], ), ); diff --git a/lib/src/stream_svg_icon.dart b/lib/src/stream_svg_icon.dart index 894b868a..1b0c9ae9 100644 --- a/lib/src/stream_svg_icon.dart +++ b/lib/src/stream_svg_icon.dart @@ -387,4 +387,16 @@ class StreamSvgIcon extends StatelessWidget { height: size, ); } + + factory StreamSvgIcon.close_small({ + double size, + Color color, + }) { + return StreamSvgIcon( + assetName: 'Icon_close_sml.svg', + color: color, + width: size, + height: size, + ); + } } From ed3fc88f623738feb69bca81a4bfc760872283e6 Mon Sep 17 00:00:00 2001 From: xsahil03x Date: Wed, 25 Nov 2020 22:37:11 +0530 Subject: [PATCH 14/15] Configure routes in example project. --- example/lib/choose_user_page.dart | 22 ++------ example/lib/group_chat_details_screen.dart | 19 +++---- example/lib/main.dart | 62 +++++++++------------ example/lib/new_chat_screen.dart | 18 +++--- example/lib/new_group_chat_screen.dart | 11 ++-- example/lib/routes/app_routes.dart | 64 ++++++++++++++++++++++ example/lib/routes/routes.dart | 10 ++++ 7 files changed, 124 insertions(+), 82 deletions(-) create mode 100644 example/lib/routes/app_routes.dart create mode 100644 example/lib/routes/routes.dart diff --git a/example/lib/choose_user_page.dart b/example/lib/choose_user_page.dart index f1a53773..0e16f012 100644 --- a/example/lib/choose_user_page.dart +++ b/example/lib/choose_user_page.dart @@ -8,6 +8,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'notifications_service.dart'; +import 'routes/routes.dart'; const kStreamApiKey = 'STREAM_API_KEY'; const kStreamUserId = 'STREAM_USER_ID'; @@ -154,18 +155,10 @@ class ChooseUserPage extends StatelessWidget { if (!kIsWeb) { initNotifications(client); } - - Navigator.pop(context); - await Navigator.pushReplacement( + Navigator.pushReplacementNamed( context, - MaterialPageRoute( - builder: (context) { - return StreamChat( - client: client, - child: ChannelListPage(), - ); - }, - ), + Routes.CHANNEL_LIST, + arguments: client, ); }, leading: UserAvatar( @@ -188,12 +181,7 @@ class ChooseUserPage extends StatelessWidget { }), ListTile( onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => AdvancedOptionsPage(), - ), - ); + Navigator.pushNamed(context, Routes.ADVANCED_OPTIONS); }, leading: CircleAvatar( child: StreamSvgIcon.settings( diff --git a/example/lib/group_chat_details_screen.dart b/example/lib/group_chat_details_screen.dart index 36999c7f..88de33a9 100644 --- a/example/lib/group_chat_details_screen.dart +++ b/example/lib/group_chat_details_screen.dart @@ -3,6 +3,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:uuid/uuid.dart'; import 'main.dart'; +import 'routes/routes.dart'; class GroupChatDetailsScreen extends StatefulWidget { final List selectedUsers; @@ -128,18 +129,12 @@ class _GroupChatDetailsScreenState extends State { 'name': groupName, }); await channel.watch(); - Navigator.of(context) - ..pop() - ..pushReplacement( - MaterialPageRoute( - builder: (context) { - return StreamChannel( - child: ChannelPage(), - channel: channel, - ); - }, - ), - ); + Navigator.pushNamedAndRemoveUntil( + context, + Routes.CHANNEL_PAGE, + ModalRoute.withName(Routes.CHANNEL_LIST), + arguments: channel, + ); }, ), ), diff --git a/example/lib/main.dart b/example/lib/main.dart index d2be225a..6cf35e0e 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -10,6 +10,8 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'notifications_service.dart'; +import 'routes/app_routes.dart'; +import 'routes/routes.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -47,19 +49,19 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp( - debugShowCheckedModeBanner: false, - theme: ThemeData.light(), - darkTheme: ThemeData.dark(), - //TODO change to system once dark theme is implemented - themeMode: ThemeMode.light, - builder: (context, widget) { - return StreamChat( - child: widget, - client: client, - ); - }, - home: client.state.user == null ? ChooseUserPage() : ChannelListPage(), + return StreamChat( + client: client, + child: MaterialApp( + debugShowCheckedModeBanner: false, + theme: ThemeData.light(), + darkTheme: ThemeData.dark(), + //TODO change to system once dark theme is implemented + themeMode: ThemeMode.light, + onGenerateRoute: AppRoutes.generateRoute, + initialRoute: client.state.user == null + ? Routes.CHOOSE_USER + : Routes.CHANNEL_LIST, + ), ); } } @@ -71,11 +73,7 @@ class ChannelListPage extends StatelessWidget { return Scaffold( appBar: ChannelListHeader( onNewChatButtonTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => NewChatScreen(), - ), - ); + Navigator.pushNamed(context, Routes.NEW_CHAT); }, ), drawer: _buildDrawer(context, user), @@ -83,9 +81,7 @@ class ChannelListPage extends StatelessWidget { body: ChannelsBloc( child: ChannelListView( onStartChatPressed: () { - Navigator.of(context).push(MaterialPageRoute(builder: (context) { - return NewChatScreen(); - })); + Navigator.pushNamed(context, Routes.NEW_CHAT); }, swipeToAction: true, filter: { @@ -147,11 +143,10 @@ class ChannelListPage extends StatelessWidget { color: Colors.black.withOpacity(.5), ), onTap: () { - Navigator.of(context) - ..pop() - ..push(MaterialPageRoute(builder: (context) { - return NewChatScreen(); - })); + Navigator.popAndPushNamed( + context, + Routes.NEW_CHAT, + ); }, title: Text( 'New direct message', @@ -165,11 +160,10 @@ class ChannelListPage extends StatelessWidget { color: Colors.black.withOpacity(.5), ), onTap: () { - Navigator.of(context) - ..pop() - ..push(MaterialPageRoute(builder: (context) { - return NewGroupChatScreen(); - })); + Navigator.popAndPushNamed( + context, + Routes.NEW_GROUP_CHAT, + ); }, title: Text( 'New group', @@ -188,11 +182,9 @@ class ChannelListPage extends StatelessWidget { final secureStorage = FlutterSecureStorage(); await secureStorage.deleteAll(); Navigator.pop(context); - await Navigator.pushReplacement( + Navigator.pushReplacementNamed( context, - MaterialPageRoute( - builder: (context) => ChooseUserPage(), - ), + Routes.CHOOSE_USER, ); }, leading: StreamSvgIcon.user( diff --git a/example/lib/new_chat_screen.dart b/example/lib/new_chat_screen.dart index 5e6af8fc..364c8b7e 100644 --- a/example/lib/new_chat_screen.dart +++ b/example/lib/new_chat_screen.dart @@ -6,6 +6,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'chips_input_text_field.dart'; import 'main.dart'; import 'new_group_chat_screen.dart'; +import 'routes/routes.dart'; class NewChatScreen extends StatefulWidget { @override @@ -182,9 +183,9 @@ class _NewChatScreenState extends State { Container( child: InkWell( onTap: () { - Navigator.push( + Navigator.pushNamed( context, - MaterialPageRoute(builder: (_) => NewGroupChatScreen()), + Routes.NEW_GROUP_CHAT, ); }, child: Padding( @@ -334,16 +335,11 @@ class _NewChatScreenState extends State { channel.update({ 'draft': false, }); - Navigator.pushReplacement( + Navigator.pushNamedAndRemoveUntil( context, - MaterialPageRoute( - builder: (context) { - return StreamChannel( - child: ChannelPage(), - channel: channel, - ); - }, - ), + Routes.CHANNEL_PAGE, + ModalRoute.withName(Routes.CHANNEL_LIST), + arguments: channel, ); } } diff --git a/example/lib/new_group_chat_screen.dart b/example/lib/new_group_chat_screen.dart index ece3ca9a..ff77e44d 100644 --- a/example/lib/new_group_chat_screen.dart +++ b/example/lib/new_group_chat_screen.dart @@ -5,6 +5,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'group_chat_details_screen.dart'; import 'search_text_field.dart'; +import 'routes/routes.dart'; class NewGroupChatScreen extends StatefulWidget { @override @@ -78,14 +79,10 @@ class _NewGroupChatScreenState extends State { color: Color(0xFF006CFF), ), onPressed: () async { - final updatedList = await Navigator.push( + final updatedList = await Navigator.pushNamed( context, - MaterialPageRoute( - builder: (_) => GroupChatDetailsScreen( - selectedUsers: - _selectedUsers.toList(growable: false), - ), - ), + Routes.NEW_GROUP_CHAT_DETAILS, + arguments: _selectedUsers.toList(growable: false), ); if (updatedList != null) { setState(() { diff --git a/example/lib/routes/app_routes.dart b/example/lib/routes/app_routes.dart new file mode 100644 index 00000000..19352319 --- /dev/null +++ b/example/lib/routes/app_routes.dart @@ -0,0 +1,64 @@ +import 'routes.dart'; +import 'package:flutter/material.dart'; +import '../choose_user_page.dart'; +import '../advanced_options_page.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +import '../main.dart'; +import '../group_chat_details_screen.dart'; +import '../new_group_chat_screen.dart'; +import '../new_chat_screen.dart'; + +class AppRoutes { + /// Add entry for new route here + static Route generateRoute(RouteSettings settings) { + final args = settings.arguments; + switch (settings.name) { + case Routes.CHANNEL_LIST: + return MaterialPageRoute( + settings: const RouteSettings(name: Routes.CHANNEL_LIST), + builder: (_) { + return ChannelListPage(); + }); + case Routes.CHOOSE_USER: + return MaterialPageRoute( + settings: const RouteSettings(name: Routes.CHOOSE_USER), + builder: (_) { + return ChooseUserPage(); + }); + case Routes.ADVANCED_OPTIONS: + return MaterialPageRoute( + settings: const RouteSettings(name: Routes.ADVANCED_OPTIONS), + builder: (_) => AdvancedOptionsPage(), + ); + case Routes.CHANNEL_PAGE: + return MaterialPageRoute( + settings: const RouteSettings(name: Routes.CHANNEL_PAGE), + builder: (_) { + return StreamChannel( + channel: args as Channel, + child: ChannelPage(), + ); + }); + case Routes.NEW_CHAT: + return MaterialPageRoute( + settings: const RouteSettings(name: Routes.NEW_CHAT), + builder: (_) { + return NewChatScreen(); + }); + case Routes.NEW_GROUP_CHAT: + return MaterialPageRoute( + settings: const RouteSettings(name: Routes.NEW_GROUP_CHAT), + builder: (_) { + return NewGroupChatScreen(); + }); + case Routes.NEW_GROUP_CHAT_DETAILS: + return MaterialPageRoute( + settings: const RouteSettings(name: Routes.NEW_GROUP_CHAT_DETAILS), + builder: (_) { + return GroupChatDetailsScreen( + selectedUsers: args, + ); + }); + } + } +} diff --git a/example/lib/routes/routes.dart b/example/lib/routes/routes.dart new file mode 100644 index 00000000..f4ace9a4 --- /dev/null +++ b/example/lib/routes/routes.dart @@ -0,0 +1,10 @@ +/// Define all the route names here +class Routes { + static const String CHANNEL_LIST = '/channel_list'; + static const String CHOOSE_USER = '/choose_user'; + static const String ADVANCED_OPTIONS = '/advance_options'; + static const String CHANNEL_PAGE = '/channel_page'; + static const String NEW_CHAT = '/new_chat'; + static const String NEW_GROUP_CHAT = '/new_group_chat'; + static const String NEW_GROUP_CHAT_DETAILS = '/new_group_chat_details'; +} From 1c42db37dae8fb6f8cf98439e28dadff65aa7620 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 26 Nov 2020 11:56:56 +0530 Subject: [PATCH 15/15] Fix search icon Signed-off-by: xsahil03x --- example/lib/search_text_field.dart | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/example/lib/search_text_field.dart b/example/lib/search_text_field.dart index 8461d735..f46a48b0 100644 --- a/example/lib/search_text_field.dart +++ b/example/lib/search_text_field.dart @@ -40,9 +40,16 @@ class SearchTextField extends StatelessWidget { controller: controller, onChanged: onChanged, decoration: InputDecoration( - prefixIcon: StreamSvgIcon.search( - color: Colors.black, - size: 24, + prefixIconConstraints: BoxConstraints.tight(Size(40, 24)), + prefixIcon: Padding( + padding: const EdgeInsets.only( + left: 8, + right: 8, + ), + child: StreamSvgIcon.search( + color: Colors.black, + size: 24, + ), ), hintText: hintText, hintStyle: TextStyle(