Line data Source code
1 : import 'dart:convert'; 2 : 3 : import 'package:flutter/foundation.dart'; 4 : import 'package:flutter/material.dart'; 5 : import 'package:stream_chat/stream_chat.dart'; 6 : import 'package:stream_chat_flutter_core/src/message_search_bloc.dart'; 7 : import 'package:stream_chat_flutter_core/src/typedef.dart'; 8 : 9 : /// 10 : /// [MessageSearchListCore] is a simplified class that allows searching for 11 : /// messages across channels while exposing UI builders. 12 : /// A [MessageSearchListController] is used to load and paginate data. 13 : /// 14 : /// ```dart 15 : /// class MessageSearchPage extends StatelessWidget { 16 : /// @override 17 : /// Widget build(BuildContext context) { 18 : /// return Scaffold( 19 : /// body: MessageSearchListCore( 20 : /// messageQuery: _channelQuery, 21 : /// filters: { 22 : /// 'members': { 23 : /// r'$in': [user.id] 24 : /// } 25 : /// }, 26 : /// paginationParams: PaginationParams(limit: 20), 27 : /// ), 28 : /// ); 29 : /// } 30 : /// } 31 : /// ``` 32 : /// 33 : /// Make sure to have a [MessageSearchBloc] ancestor in order to provide the 34 : /// information about the messages. 35 : /// The widget uses a [ListView.separated] to render the list of messages. 36 : /// 37 : class MessageSearchListCore extends StatefulWidget { 38 : /// Instantiate a new [MessageSearchListView]. 39 : /// The following parameters must be supplied and not null: 40 : /// * [emptyBuilder] 41 : /// * [errorBuilder] 42 : /// * [loadingBuilder] 43 : /// * [childBuilder] 44 1 : const MessageSearchListCore({ 45 : Key? key, 46 : required this.emptyBuilder, 47 : required this.errorBuilder, 48 : required this.loadingBuilder, 49 : required this.childBuilder, 50 : required this.filters, 51 : this.messageQuery, 52 : this.sortOptions, 53 : this.paginationParams, 54 : this.messageFilters, 55 : this.messageSearchListController, 56 1 : }) : super(key: key); 57 : 58 : /// A [MessageSearchListController] allows reloading and pagination. 59 : /// Use [MessageSearchListController.loadData] and 60 : /// [MessageSearchListController.paginateData] respectively for reloading and 61 : /// pagination. 62 : final MessageSearchListController? messageSearchListController; 63 : 64 : /// Message String to search on 65 : final String? messageQuery; 66 : 67 : /// The query filters to use. 68 : /// You can query on any of the custom fields you've defined on the [Channel]. 69 : /// You can also filter other built-in channel fields. 70 : final Filter filters; 71 : 72 : /// The sorting used for the channels matching the filters. 73 : /// Sorting is based on field and direction, multiple sorting options can be 74 : /// provided. 75 : /// You can sort based on last_updated, last_message_at, updated_at, created_ 76 : /// at or member_count. Direction can be ascending or descending. 77 : final List<SortOption>? sortOptions; 78 : 79 : /// Pagination parameters 80 : /// limit: the number of users to return (max is 30) 81 : /// offset: the offset (max is 1000) 82 : /// message_limit: how many messages should be included to each channel 83 : final PaginationParams? paginationParams; 84 : 85 : /// The message query filters to use. 86 : /// You can query on any of the custom fields you've defined on the [Channel]. 87 : /// You can also filter other built-in channel fields. 88 : final Filter? messageFilters; 89 : 90 : /// The builder that is used when the search messages are fetched 91 : final Widget Function(List<GetMessageResponse>) childBuilder; 92 : 93 : /// The builder used when the channel list is empty. 94 : final WidgetBuilder emptyBuilder; 95 : 96 : /// The builder that will be used in case of error 97 : final ErrorBuilder errorBuilder; 98 : 99 : /// The builder that will be used in case of loading 100 : final WidgetBuilder loadingBuilder; 101 : 102 1 : @override 103 1 : MessageSearchListCoreState createState() => MessageSearchListCoreState(); 104 : } 105 : 106 : /// The current state of the [MessageSearchListCore]. 107 : class MessageSearchListCoreState extends State<MessageSearchListCore> { 108 : MessageSearchBlocState? _messageSearchBloc; 109 : 110 1 : @override 111 : void didChangeDependencies() { 112 2 : final newMessageSearchBloc = MessageSearchBloc.of(context); 113 : 114 2 : if (newMessageSearchBloc != _messageSearchBloc) { 115 1 : _messageSearchBloc = newMessageSearchBloc; 116 1 : loadData(); 117 2 : if (widget.messageSearchListController != null) { 118 4 : widget.messageSearchListController!.loadData = loadData; 119 4 : widget.messageSearchListController!.paginateData = paginateData; 120 : } 121 : } 122 : 123 1 : super.didChangeDependencies(); 124 : } 125 : 126 1 : @override 127 2 : Widget build(BuildContext context) => _buildListView(_messageSearchBloc!); 128 : 129 1 : Widget _buildListView(MessageSearchBlocState messageSearchBloc) => 130 1 : StreamBuilder<List<GetMessageResponse>>( 131 1 : stream: messageSearchBloc.messagesStream, 132 1 : builder: (context, snapshot) { 133 1 : if (snapshot.hasError) { 134 4 : return widget.errorBuilder(context, snapshot.error!); 135 : } 136 1 : if (!snapshot.hasData) { 137 3 : return widget.loadingBuilder(context); 138 : } 139 1 : final items = snapshot.data!; 140 1 : if (items.isEmpty) { 141 3 : return widget.emptyBuilder(context); 142 : } 143 3 : return widget.childBuilder(items); 144 : }, 145 : ); 146 : 147 : /// Fetches initial messages and updates the widget 148 3 : Future<void> loadData() => _messageSearchBloc!.search( 149 2 : filter: widget.filters, 150 2 : sort: widget.sortOptions, 151 2 : query: widget.messageQuery, 152 2 : pagination: widget.paginationParams, 153 2 : messageFilter: widget.messageFilters, 154 : ); 155 : 156 : /// Fetches more messages with updated pagination and updates the widget 157 3 : Future<void> paginateData() => _messageSearchBloc!.search( 158 2 : filter: widget.filters, 159 2 : sort: widget.sortOptions, 160 3 : pagination: widget.paginationParams!.copyWith( 161 3 : offset: _messageSearchBloc!.messageResponses?.length ?? 0, 162 : ), 163 2 : query: widget.messageQuery, 164 2 : messageFilter: widget.messageFilters, 165 : ); 166 : 167 1 : @override 168 : void didUpdateWidget(MessageSearchListCore oldWidget) { 169 1 : super.didUpdateWidget(oldWidget); 170 6 : if (widget.filters.toString() != oldWidget.filters.toString() || 171 6 : jsonEncode(widget.sortOptions) != jsonEncode(oldWidget.sortOptions) || 172 4 : widget.messageQuery?.toString() != oldWidget.messageQuery?.toString() || 173 3 : widget.messageFilters?.toString() != 174 1 : oldWidget.messageFilters?.toString() || 175 5 : widget.paginationParams?.toJson().toString() != 176 3 : oldWidget.paginationParams?.toJson().toString()) { 177 1 : loadData(); 178 : } 179 : } 180 : } 181 : 182 : /// Controller used for paginating data in [ChannelListView] 183 : class MessageSearchListController { 184 : /// Call this function to reload data 185 : AsyncCallback? loadData; 186 : 187 : /// Call this function to load further data 188 : AsyncCallback? paginateData; 189 : }