11import 'package:example/localizations.dart' ;
2+ import 'package:example/routes/routes.dart' ;
23import 'package:flutter/material.dart' ;
34import 'package:stream_chat_flutter/stream_chat_flutter.dart' ;
5+ import 'package:video_player/video_player.dart' ;
46
5- class ChannelFileDisplayScreen extends StatefulWidget {
6- /// The sorting used for the channels matching the filters.
7- /// Sorting is based on field and direction, multiple sorting options can be provided.
8- /// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count.
9- /// Direction can be ascending or descending.
10- final List <SortOption >? sortOptions;
11-
12- /// Pagination parameters
13- /// limit: the number of users to return (max is 30)
14- /// offset: the offset (max is 1000)
15- /// message_limit: how many messages should be included to each channel
16- final PaginationParams paginationParams;
7+ import 'channel_page.dart' ;
178
18- /// The builder used when the file list is empty.
19- final WidgetBuilder ? emptyBuilder ;
9+ class ChannelFileDisplayScreen extends StatefulWidget {
10+ final StreamMessageThemeData messageTheme ;
2011
2112 const ChannelFileDisplayScreen ({
22- this .sortOptions,
23- this .paginationParams = const PaginationParams (limit: 20 ),
24- this .emptyBuilder,
25- });
13+ Key ? key,
14+ required this .messageTheme,
15+ }) : super (key: key);
2616
2717 @override
28- _ChannelFileDisplayScreenState createState () =>
18+ State < ChannelFileDisplayScreen > createState () =>
2919 _ChannelFileDisplayScreenState ();
3020}
3121
3222class _ChannelFileDisplayScreenState extends State <ChannelFileDisplayScreen > {
33- @override
34- void initState () {
35- super .initState ();
36- final messageSearchBloc = MessageSearchBloc .of (context);
37- messageSearchBloc.search (
38- filter: Filter .in_ (
39- 'cid' ,
40- [StreamChannel .of (context).channel.cid! ],
41- ),
42- messageFilter: Filter .in_ (
43- 'attachments.type' ,
44- ['file' ],
23+ final Map <String ?, VideoPlayerController ?> controllerCache = {};
24+
25+ late final controller = StreamMessageSearchListController (
26+ client: StreamChat .of (context).client,
27+ filter: Filter .in_ (
28+ 'cid' ,
29+ [StreamChannel .of (context).channel.cid! ],
30+ ),
31+ messageFilter: Filter .in_ (
32+ 'attachments.type' ,
33+ ['file' ],
34+ ),
35+ sort: [
36+ SortOption (
37+ 'created_at' ,
38+ direction: SortOption .ASC ,
4539 ),
46- sort: widget.sortOptions,
47- pagination: widget.paginationParams,
48- );
49- }
40+ ],
41+ limit: 20 ,
42+ );
5043
5144 @override
5245 Widget build (BuildContext context) {
@@ -58,120 +51,109 @@ class _ChannelFileDisplayScreenState extends State<ChannelFileDisplayScreen> {
5851 title: Text (
5952 AppLocalizations .of (context).files,
6053 style: TextStyle (
61- color: StreamChatTheme .of (context).colorTheme.textHighEmphasis,
62- fontSize: 16.0 ),
63- ),
64- leading: Center (
65- child: InkWell (
66- onTap: () {
67- Navigator .of (context).pop ();
68- },
69- child: Container (
70- width: 24.0 ,
71- height: 24.0 ,
72- child: StreamSvgIcon .left (
73- color: StreamChatTheme .of (context).colorTheme.textHighEmphasis,
74- size: 24.0 ,
75- ),
76- ),
54+ color: StreamChatTheme .of (context).colorTheme.textHighEmphasis,
55+ fontSize: 16.0 ,
7756 ),
7857 ),
58+ leading: StreamBackButton (),
7959 backgroundColor: StreamChatTheme .of (context).colorTheme.barsBg,
8060 ),
81- body: _buildMediaGrid (),
82- );
83- }
84-
85- Widget _buildMediaGrid () {
86- final messageSearchBloc = MessageSearchBloc .of (context);
87-
88- return StreamBuilder <List <GetMessageResponse >>(
89- builder: (context, snapshot) {
90- if (snapshot.data == null ) {
91- return Center (
92- child: const CircularProgressIndicator (),
93- );
94- }
95-
96- if (snapshot.data! .isEmpty) {
97- if (widget.emptyBuilder != null ) {
98- return widget.emptyBuilder !(context);
99- }
100- return Center (
101- child: Column (
102- mainAxisAlignment: MainAxisAlignment .center,
103- children: [
104- StreamSvgIcon .files (
105- size: 136.0 ,
106- color: StreamChatTheme .of (context).colorTheme.disabled,
107- ),
108- SizedBox (height: 16.0 ),
109- Text (
110- AppLocalizations .of (context).noFiles,
111- style: TextStyle (
112- fontSize: 14.0 ,
113- color:
114- StreamChatTheme .of (context).colorTheme.textHighEmphasis,
115- ),
116- ),
117- SizedBox (height: 8.0 ),
118- Text (
119- AppLocalizations .of (context).filesAppearHere,
120- textAlign: TextAlign .center,
121- style: TextStyle (
122- fontSize: 14.0 ,
123- color: StreamChatTheme .of (context)
124- .colorTheme
125- .textHighEmphasis
126- .withOpacity (0.5 ),
61+ body: ValueListenableBuilder (
62+ valueListenable: controller,
63+ builder: (
64+ BuildContext context,
65+ PagedValue <String , GetMessageResponse > value,
66+ Widget ? child,
67+ ) {
68+ return value.when (
69+ (items, nextPageKey, error) {
70+ if (items.isEmpty) {
71+ return Center (
72+ child: Column (
73+ mainAxisAlignment: MainAxisAlignment .center,
74+ children: [
75+ StreamSvgIcon .files (
76+ size: 136.0 ,
77+ color: StreamChatTheme .of (context).colorTheme.disabled,
78+ ),
79+ SizedBox (height: 16.0 ),
80+ Text (
81+ AppLocalizations .of (context).noFiles,
82+ style: TextStyle (
83+ fontSize: 14.0 ,
84+ color: StreamChatTheme .of (context)
85+ .colorTheme
86+ .textHighEmphasis,
87+ ),
88+ ),
89+ SizedBox (height: 8.0 ),
90+ Text (
91+ AppLocalizations .of (context).filesAppearHere,
92+ textAlign: TextAlign .center,
93+ style: TextStyle (
94+ fontSize: 14.0 ,
95+ color: StreamChatTheme .of (context)
96+ .colorTheme
97+ .textHighEmphasis
98+ .withOpacity (0.5 ),
99+ ),
100+ ),
101+ ],
127102 ),
128- ),
129- ],
130- ),
131- );
132- }
103+ );
104+ }
105+ final media = < Attachment , Message > {};
133106
134- final media = < Attachment , Message > {};
107+ for (var item in items) {
108+ item.message.attachments
109+ .where ((e) => e.type == 'file' )
110+ .forEach ((e) {
111+ media[e] = item.message;
112+ });
113+ }
135114
136- for (var item in snapshot.data! ) {
137- item.message.attachments.where ((e) => e.type == 'file' ).forEach ((e) {
138- media[e] = item.message;
139- });
140- }
141-
142- return LazyLoadScrollView (
143- onEndOfPage: () => messageSearchBloc.search (
144- filter: Filter .in_ (
145- 'cid' ,
146- [StreamChannel .of (context).channel.cid! ],
147- ),
148- messageFilter: Filter .in_ (
149- 'attachments.type' ,
150- ['file' ],
151- ),
152- sort: widget.sortOptions,
153- pagination: widget.paginationParams.copyWith (
154- offset: messageSearchBloc.messageResponses? .length ?? 0 ,
155- ),
156- ),
157- child: ListView .builder (
158- itemBuilder: (context, position) {
159- return Padding (
160- padding: const EdgeInsets .all (1.0 ),
161- child: Padding (
162- padding: const EdgeInsets .all (8.0 ),
163- child: StreamFileAttachment (
164- message: media.values.toList ()[position],
165- attachment: media.keys.toList ()[position],
166- ),
115+ return LazyLoadScrollView (
116+ onEndOfPage: () async {
117+ if (nextPageKey != null ) {
118+ controller.loadMore (nextPageKey);
119+ }
120+ },
121+ child: ListView .builder (
122+ itemBuilder: (context, position) {
123+ return Padding (
124+ padding: const EdgeInsets .all (1.0 ),
125+ child: Padding (
126+ padding: const EdgeInsets .all (8.0 ),
127+ child: StreamFileAttachment (
128+ message: media.values.toList ()[position],
129+ attachment: media.keys.toList ()[position],
130+ ),
131+ ),
132+ );
133+ },
134+ itemCount: media.length,
167135 ),
168136 );
169137 },
170- itemCount: media.length,
171- ),
172- );
173- },
174- stream: messageSearchBloc.messagesStream,
138+ loading: () => Center (
139+ child: const CircularProgressIndicator (),
140+ ),
141+ error: (_) => Offstage (),
142+ );
143+ },
144+ ),
175145 );
176146 }
147+
148+ @override
149+ void dispose () {
150+ controller.dispose ();
151+ super .dispose ();
152+ }
153+
154+ @override
155+ void initState () {
156+ controller.doInitialLoad ();
157+ super .initState ();
158+ }
177159}
0 commit comments