@@ -4,6 +4,7 @@ import 'package:example/localizations.dart';
44import 'package:example/routes/routes.dart' ;
55import 'package:example/search_text_field.dart' ;
66import 'package:flutter/material.dart' ;
7+ import 'package:flutter/rendering.dart' ;
78import 'package:stream_chat_flutter/stream_chat_flutter.dart' ;
89
910import 'channel_page.dart' ;
@@ -16,6 +17,8 @@ class ChannelList extends StatefulWidget {
1617}
1718
1819class _ChannelList extends State <ChannelList > {
20+ ScrollController _scrollController = ScrollController ();
21+
1922 TextEditingController ? _controller;
2023
2124 String _channelQuery = '' ;
@@ -46,6 +49,7 @@ class _ChannelList extends State<ChannelList> {
4649 void dispose () {
4750 _controller? .removeListener (_channelQueryListener);
4851 _controller? .dispose ();
52+ _scrollController.dispose ();
4953 super .dispose ();
5054 }
5155
@@ -61,145 +65,144 @@ class _ChannelList extends State<ChannelList> {
6165 }
6266 return true ;
6367 },
64- child: NestedScrollView (
65- floatHeaderSlivers: true ,
66- headerSliverBuilder: (_, __) => [
67- SliverToBoxAdapter (
68- child: SearchTextField (
69- controller: _controller,
70- showCloseButton: _isSearchActive,
71- hintText: AppLocalizations .of (context).search,
68+ child: NotificationListener <ScrollUpdateNotification >(
69+ onNotification: (ScrollNotification scrollInfo) {
70+ if (_scrollController.position.userScrollDirection ==
71+ ScrollDirection .reverse) {
72+ FocusScope .of (context).unfocus ();
73+ }
74+ return true ;
75+ },
76+ child: NestedScrollView (
77+ controller: _scrollController,
78+ floatHeaderSlivers: false ,
79+ headerSliverBuilder: (_, __) => [
80+ SliverToBoxAdapter (
81+ child: SearchTextField (
82+ controller: _controller,
83+ showCloseButton: _isSearchActive,
84+ hintText: AppLocalizations .of (context).search,
85+ ),
7286 ),
73- ),
74- ],
75- body: AnimatedSwitcher (
76- duration: const Duration (milliseconds: 350 ),
77- child: GestureDetector (
78- behavior: HitTestBehavior .opaque,
79- onPanDown: (_) => FocusScope .of (context).unfocus (),
80- child: _isSearchActive
81- ? MessageSearchBloc (
82- child: MessageSearchListView (
83- showErrorTile: true ,
84- messageQuery: _channelQuery,
85- filters: Filter .in_ ('members' , [user! .id]),
86- sortOptions: [
87- SortOption (
88- 'created_at' ,
89- direction: SortOption .ASC ,
90- ),
91- ],
92- pullToRefresh: false ,
93- limit: 20 ,
94- emptyBuilder: (_) {
95- return LayoutBuilder (
96- builder: (context, viewportConstraints) {
97- return SingleChildScrollView (
98- physics: AlwaysScrollableScrollPhysics (),
99- child: ConstrainedBox (
100- constraints: BoxConstraints (
101- minHeight: viewportConstraints.maxHeight,
102- ),
103- child: Center (
104- child: Column (
105- children: [
106- Padding (
107- padding: const EdgeInsets .all (24 ),
108- child: StreamSvgIcon .search (
109- size: 96 ,
110- color: Colors .grey,
111- ),
112- ),
113- Text (
114- AppLocalizations .of (context).noResults,
115- ),
116- ],
87+ ],
88+ body: _isSearchActive
89+ ? MessageSearchListView (
90+ showErrorTile: true ,
91+ messageQuery: _channelQuery,
92+ filters: Filter .in_ ('members' , [user! .id]),
93+ sortOptions: [
94+ SortOption (
95+ 'created_at' ,
96+ direction: SortOption .ASC ,
97+ ),
98+ ],
99+ pullToRefresh: false ,
100+ limit: 30 ,
101+ emptyBuilder: (_) {
102+ return LayoutBuilder (
103+ builder: (context, viewportConstraints) {
104+ return SingleChildScrollView (
105+ physics: AlwaysScrollableScrollPhysics (),
106+ child: ConstrainedBox (
107+ constraints: BoxConstraints (
108+ minHeight: viewportConstraints.maxHeight,
109+ ),
110+ child: Center (
111+ child: Column (
112+ children: [
113+ Padding (
114+ padding: const EdgeInsets .all (24 ),
115+ child: StreamSvgIcon .search (
116+ size: 96 ,
117+ color: Colors .grey,
118+ ),
119+ ),
120+ Text (
121+ AppLocalizations .of (context).noResults,
117122 ),
118- ) ,
123+ ] ,
119124 ),
120- );
121- },
122- );
123- },
124- onItemTap: (messageResponse) async {
125- FocusScope .of (context).requestFocus (FocusNode ());
126- final client = StreamChat .of (context).client;
127- final message = messageResponse.message;
128- final channel = client.channel (
129- messageResponse.channel! .type,
130- id: messageResponse.channel! .id,
131- );
132- if (channel.state == null ) {
133- await channel.watch ();
134- }
135- Navigator .pushNamed (
136- context,
137- Routes .CHANNEL_PAGE ,
138- arguments: ChannelPageArgs (
139- channel: channel,
140- initialMessage: message,
125+ ),
141126 ),
142127 );
143128 },
144- ),
145- )
146- : ChannelsBloc (
147- child: ChannelListView (
148- onChannelTap: (channel, _) {
149- Navigator .pushNamed (
150- context,
151- Routes .CHANNEL_PAGE ,
152- arguments: ChannelPageArgs (
129+ );
130+ },
131+ onItemTap: (messageResponse) async {
132+ FocusScope .of (context).requestFocus (FocusNode ());
133+ final client = StreamChat .of (context).client;
134+ final message = messageResponse.message;
135+ final channel = client.channel (
136+ messageResponse.channel! .type,
137+ id: messageResponse.channel! .id,
138+ );
139+ if (channel.state == null ) {
140+ await channel.watch ();
141+ }
142+ Navigator .pushNamed (
143+ context,
144+ Routes .CHANNEL_PAGE ,
145+ arguments: ChannelPageArgs (
146+ channel: channel,
147+ initialMessage: message,
148+ ),
149+ );
150+ },
151+ )
152+ : ChannelListView (
153+ onChannelTap: (channel, _) {
154+ Navigator .pushNamed (
155+ context,
156+ Routes .CHANNEL_PAGE ,
157+ arguments: ChannelPageArgs (
158+ channel: channel,
159+ ),
160+ );
161+ },
162+ onStartChatPressed: () {
163+ Navigator .pushNamed (context, Routes .NEW_CHAT );
164+ },
165+ swipeToAction: true ,
166+ filter: Filter .in_ ('members' , [user! .id]),
167+ presence: true ,
168+ limit: 30 ,
169+ onViewInfoTap: (channel) {
170+ Navigator .pop (context);
171+ if (channel.memberCount == 2 && channel.isDistinct) {
172+ Navigator .push (
173+ context,
174+ MaterialPageRoute (
175+ builder: (context) => StreamChannel (
153176 channel: channel,
154- ),
155- );
156- },
157- onStartChatPressed: () {
158- Navigator .pushNamed (context, Routes .NEW_CHAT );
159- },
160- swipeToAction: true ,
161- filter: Filter .in_ ('members' , [user! .id]),
162- presence: true ,
163- limit: 20 ,
164- onViewInfoTap: (channel) {
165- Navigator .pop (context);
166- if (channel.memberCount == 2 && channel.isDistinct) {
167- Navigator .push (
168- context,
169- MaterialPageRoute (
170- builder: (context) => StreamChannel (
171- channel: channel,
172- child: ChatInfoScreen (
173- messageTheme: StreamChatTheme .of (context)
174- .ownMessageTheme,
175- user: channel.state! .members
176- .where ((m) =>
177- m.userId !=
178- channel.client.state.currentUser! .id)
179- .first
180- .user,
181- ),
182- ),
177+ child: ChatInfoScreen (
178+ messageTheme:
179+ StreamChatTheme .of (context).ownMessageTheme,
180+ user: channel.state! .members
181+ .where ((m) =>
182+ m.userId !=
183+ channel.client.state.currentUser! .id)
184+ .first
185+ .user,
183186 ),
184- );
185- } else {
186- Navigator . push (
187- context,
188- MaterialPageRoute (
189- builder : ( context) => StreamChannel (
190- channel : channel,
191- child : GroupInfoScreen (
192- messageTheme : StreamChatTheme . of (context)
193- .ownMessageTheme,
194- ),
195- ) ,
187+ ),
188+ ),
189+ );
190+ } else {
191+ Navigator . push (
192+ context,
193+ MaterialPageRoute (
194+ builder : (context) => StreamChannel (
195+ channel : channel,
196+ child : GroupInfoScreen (
197+ messageTheme :
198+ StreamChatTheme . of (context).ownMessageTheme ,
196199 ),
197- );
198- }
199- },
200- ),
201- ) ,
202- ),
200+ ),
201+ ),
202+ );
203+ }
204+ } ,
205+ ),
203206 ),
204207 ),
205208 );
0 commit comments