Merge pull request #60 from GetStream/fix/channel-list-scroll-performance

fix(ui): channel list performance
This commit is contained in:
Salvatore Giordano
2021-12-09 17:04:12 +01:00
committed by GitHub
2 changed files with 138 additions and 131 deletions
+133 -130
View File
@@ -4,6 +4,7 @@ import 'package:example/localizations.dart';
import 'package:example/routes/routes.dart'; import 'package:example/routes/routes.dart';
import 'package:example/search_text_field.dart'; import 'package:example/search_text_field.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'channel_page.dart'; import 'channel_page.dart';
@@ -16,6 +17,8 @@ class ChannelList extends StatefulWidget {
} }
class _ChannelList extends State<ChannelList> { class _ChannelList extends State<ChannelList> {
ScrollController _scrollController = ScrollController();
TextEditingController? _controller; TextEditingController? _controller;
String _channelQuery = ''; String _channelQuery = '';
@@ -46,6 +49,7 @@ class _ChannelList extends State<ChannelList> {
void dispose() { void dispose() {
_controller?.removeListener(_channelQueryListener); _controller?.removeListener(_channelQueryListener);
_controller?.dispose(); _controller?.dispose();
_scrollController.dispose();
super.dispose(); super.dispose();
} }
@@ -61,145 +65,144 @@ class _ChannelList extends State<ChannelList> {
} }
return true; return true;
}, },
child: NestedScrollView( child: NotificationListener<ScrollUpdateNotification>(
floatHeaderSlivers: true, onNotification: (ScrollNotification scrollInfo) {
headerSliverBuilder: (_, __) => [ if (_scrollController.position.userScrollDirection ==
SliverToBoxAdapter( ScrollDirection.reverse) {
child: SearchTextField( FocusScope.of(context).unfocus();
controller: _controller, }
showCloseButton: _isSearchActive, return true;
hintText: AppLocalizations.of(context).search, },
child: NestedScrollView(
controller: _scrollController,
floatHeaderSlivers: false,
headerSliverBuilder: (_, __) => [
SliverToBoxAdapter(
child: SearchTextField(
controller: _controller,
showCloseButton: _isSearchActive,
hintText: AppLocalizations.of(context).search,
),
), ),
), ],
], body: _isSearchActive
body: AnimatedSwitcher( ? MessageSearchListView(
duration: const Duration(milliseconds: 350), showErrorTile: true,
child: GestureDetector( messageQuery: _channelQuery,
behavior: HitTestBehavior.opaque, filters: Filter.in_('members', [user!.id]),
onPanDown: (_) => FocusScope.of(context).unfocus(), sortOptions: [
child: _isSearchActive SortOption(
? MessageSearchBloc( 'created_at',
child: MessageSearchListView( direction: SortOption.ASC,
showErrorTile: true, ),
messageQuery: _channelQuery, ],
filters: Filter.in_('members', [user!.id]), pullToRefresh: false,
sortOptions: [ limit: 30,
SortOption( emptyBuilder: (_) {
'created_at', return LayoutBuilder(
direction: SortOption.ASC, builder: (context, viewportConstraints) {
), return SingleChildScrollView(
], physics: AlwaysScrollableScrollPhysics(),
pullToRefresh: false, child: ConstrainedBox(
limit: 20, constraints: BoxConstraints(
emptyBuilder: (_) { minHeight: viewportConstraints.maxHeight,
return LayoutBuilder( ),
builder: (context, viewportConstraints) { child: Center(
return SingleChildScrollView( child: Column(
physics: AlwaysScrollableScrollPhysics(), children: [
child: ConstrainedBox( Padding(
constraints: BoxConstraints( padding: const EdgeInsets.all(24),
minHeight: viewportConstraints.maxHeight, child: StreamSvgIcon.search(
), size: 96,
child: Center( color: Colors.grey,
child: Column( ),
children: [
Padding(
padding: const EdgeInsets.all(24),
child: StreamSvgIcon.search(
size: 96,
color: Colors.grey,
),
),
Text(
AppLocalizations.of(context).noResults,
),
],
), ),
), Text(
AppLocalizations.of(context).noResults,
),
],
), ),
); ),
},
);
},
onItemTap: (messageResponse) async {
FocusScope.of(context).requestFocus(FocusNode());
final client = StreamChat.of(context).client;
final message = messageResponse.message;
final channel = client.channel(
messageResponse.channel!.type,
id: messageResponse.channel!.id,
);
if (channel.state == null) {
await channel.watch();
}
Navigator.pushNamed(
context,
Routes.CHANNEL_PAGE,
arguments: ChannelPageArgs(
channel: channel,
initialMessage: message,
), ),
); );
}, },
), );
) },
: ChannelsBloc( onItemTap: (messageResponse) async {
child: ChannelListView( FocusScope.of(context).requestFocus(FocusNode());
onChannelTap: (channel, _) { final client = StreamChat.of(context).client;
Navigator.pushNamed( final message = messageResponse.message;
context, final channel = client.channel(
Routes.CHANNEL_PAGE, messageResponse.channel!.type,
arguments: ChannelPageArgs( id: messageResponse.channel!.id,
);
if (channel.state == null) {
await channel.watch();
}
Navigator.pushNamed(
context,
Routes.CHANNEL_PAGE,
arguments: ChannelPageArgs(
channel: channel,
initialMessage: message,
),
);
},
)
: ChannelListView(
onChannelTap: (channel, _) {
Navigator.pushNamed(
context,
Routes.CHANNEL_PAGE,
arguments: ChannelPageArgs(
channel: channel,
),
);
},
onStartChatPressed: () {
Navigator.pushNamed(context, Routes.NEW_CHAT);
},
swipeToAction: true,
filter: Filter.in_('members', [user!.id]),
presence: true,
limit: 30,
onViewInfoTap: (channel) {
Navigator.pop(context);
if (channel.memberCount == 2 && channel.isDistinct) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => StreamChannel(
channel: channel, channel: channel,
child: ChatInfoScreen(
messageTheme:
StreamChatTheme.of(context).ownMessageTheme,
user: channel.state!.members
.where((m) =>
m.userId !=
channel.client.state.currentUser!.id)
.first
.user,
),
), ),
); ),
}, );
onStartChatPressed: () { } else {
Navigator.pushNamed(context, Routes.NEW_CHAT); Navigator.push(
}, context,
swipeToAction: true, MaterialPageRoute(
filter: Filter.in_('members', [user!.id]), builder: (context) => StreamChannel(
presence: true, channel: channel,
limit: 20, child: GroupInfoScreen(
onViewInfoTap: (channel) { messageTheme:
Navigator.pop(context); StreamChatTheme.of(context).ownMessageTheme,
if (channel.memberCount == 2 && channel.isDistinct) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => StreamChannel(
channel: channel,
child: ChatInfoScreen(
messageTheme: StreamChatTheme.of(context)
.ownMessageTheme,
user: channel.state!.members
.where((m) =>
m.userId !=
channel.client.state.currentUser!.id)
.first
.user,
),
),
), ),
); ),
} else { ),
Navigator.push( );
context, }
MaterialPageRoute( },
builder: (context) => StreamChannel( ),
channel: channel,
child: GroupInfoScreen(
messageTheme: StreamChatTheme.of(context)
.ownMessageTheme,
),
),
),
);
}
},
),
),
),
), ),
), ),
); );
@@ -100,7 +100,11 @@ class _ChannelListPageState extends State<ChannelListPage> {
body: IndexedStack( body: IndexedStack(
index: _currentIndex, index: _currentIndex,
children: [ children: [
ChannelList(), ChannelsBloc(
child: MessageSearchBloc(
child: ChannelList(),
),
),
UserMentionsPage(), UserMentionsPage(),
], ],
), ),