feat: Apply team lints to core package (#334)

* Add analyzer file to core package

* Apply team lint rules to core package

* clean up analyzer warnings in stream chat flutter

* remove unused line

* add ignore

* sync analysis options

* [Core -> MessageWidget] Fix variable dynamic types

Signed-off-by: Sahil Kumar <[email protected]>

Co-authored-by: Salvatore Giordano <[email protected]>
Co-authored-by: Sahil Kumar <[email protected]>
This commit is contained in:
Neevash Ramdial (Nash)
2021-03-22 09:49:27 +01:00
committed by GitHub
co-authored by Salvatore Giordano Sahil Kumar
parent 6d259074eb
commit cba0b1a391
17 changed files with 425 additions and 256 deletions
+3 -5
View File
@@ -1,11 +1,11 @@
analyzer: analyzer:
exclude: exclude:
- lib/**/*.g.dart - lib/**/*.g.dart
- lib/**/*.freezed.dart - lib/**/*.freezed.dart
- example/* - example/*
- test/* - test/*
linter: linter:
rules: rules:
- always_use_package_imports - always_use_package_imports
- avoid_empty_else - avoid_empty_else
- avoid_relative_lib_imports - avoid_relative_lib_imports
@@ -14,7 +14,6 @@ linter:
- cancel_subscriptions - cancel_subscriptions
- close_sinks - close_sinks
- control_flow_in_finally - control_flow_in_finally
- diagnostic_describe_all_properties
- empty_statements - empty_statements
- hash_and_equals - hash_and_equals
- invariant_booleans - invariant_booleans
@@ -33,7 +32,6 @@ linter:
- use_key_in_widget_constructors - use_key_in_widget_constructors
- valid_regexps - valid_regexps
- always_declare_return_types - always_declare_return_types
- always_put_required_named_parameters_first
- always_require_non_null_named_parameters - always_require_non_null_named_parameters
- annotate_overrides - annotate_overrides
- avoid_bool_literals_in_conditional_expressions - avoid_bool_literals_in_conditional_expressions
@@ -1210,9 +1210,10 @@ class ChannelClientState {
this._channel, this._channel,
ChannelState channelState, ChannelState channelState,
//ignore: unnecessary_parenthesis //ignore: unnecessary_parenthesis
) : _debouncedUpdatePersistenceChannelState = ((ChannelState state) { ) : _debouncedUpdatePersistenceChannelState = ((ChannelState state) =>
_channel?._client?.chatPersistenceClient?.updateChannelState(state); _channel?._client?.chatPersistenceClient
}).debounced(const Duration(seconds: 1)) { ?.updateChannelState(state))
.debounced(const Duration(seconds: 1)) {
retryQueue = RetryQueue( retryQueue = RetryQueue(
channel: _channel, channel: _channel,
logger: Logger('RETRY QUEUE ${_channel.cid}'), logger: Logger('RETRY QUEUE ${_channel.cid}'),
@@ -0,0 +1,145 @@
analyzer:
exclude:
- lib/**/*.g.dart
- lib/**/*.freezed.dart
- example/*
- test/*
linter:
rules:
- always_use_package_imports
- avoid_empty_else
- avoid_relative_lib_imports
- avoid_slow_async_io
- avoid_types_as_parameter_names
- cancel_subscriptions
- close_sinks
- control_flow_in_finally
- empty_statements
- hash_and_equals
- invariant_booleans
- iterable_contains_unrelated_type
- list_remove_unrelated_type
- literal_only_boolean_expressions
- no_adjacent_strings_in_list
- no_duplicate_case_values
- no_logic_in_create_state
- prefer_void_to_null
- test_types_in_equals
- throw_in_finally
- unnecessary_statements
- unrelated_type_equality_checks
- omit_local_variable_types
- use_key_in_widget_constructors
- valid_regexps
- always_declare_return_types
- always_require_non_null_named_parameters
- annotate_overrides
- avoid_bool_literals_in_conditional_expressions
- avoid_catching_errors
- avoid_init_to_null
- avoid_null_checks_in_equality_operators
- avoid_positional_boolean_parameters
- avoid_private_typedef_functions
- avoid_redundant_argument_values
- avoid_return_types_on_setters
- avoid_returning_null
- avoid_returning_null_for_void
- avoid_shadowing_type_parameters
- avoid_single_cascade_in_expression_statements
- avoid_unnecessary_containers
- avoid_unused_constructor_parameters
- await_only_futures
- camel_case_extensions
- camel_case_types
- cascade_invocations
- constant_identifier_names
- curly_braces_in_flow_control_structures
- directives_ordering
- empty_catches
- empty_constructor_bodies
- exhaustive_cases
- file_names
- implementation_imports
- join_return_with_assignment
- leading_newlines_in_multiline_strings
- library_names
- library_prefixes
- lines_longer_than_80_chars
- missing_whitespace_between_adjacent_strings
- non_constant_identifier_names
- null_closures
- one_member_abstracts
- only_throw_errors
- package_api_docs
- package_prefixed_library_names
- parameter_assignments
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
- prefer_asserts_with_message
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
- prefer_constructors_over_static_methods
- prefer_contains
- prefer_equal_for_default_values
- prefer_expression_function_bodies
- prefer_final_fields
- prefer_final_in_for_each
- prefer_final_locals
- prefer_function_declarations_over_variables
- prefer_generic_function_type_aliases
- prefer_if_elements_to_conditional_expressions
- prefer_if_null_operators
- prefer_initializing_formals
- prefer_inlined_adds
- prefer_int_literals
- prefer_interpolation_to_compose_strings
- prefer_is_empty
- prefer_is_not_empty
- prefer_is_not_operator
- prefer_null_aware_operators
- prefer_single_quotes
- prefer_spread_collections
- prefer_typing_uninitialized_variables
- provide_deprecation_message
- public_member_api_docs
- recursive_getters
- sized_box_for_whitespace
- slash_for_doc_comments
- sort_child_properties_last
- sort_constructors_first
- sort_unnamed_constructors_first
- type_annotate_public_apis
- type_init_formals
- unnecessary_await_in_return
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_getters_setters
- unnecessary_lambdas
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_in_if_null_operators
- unnecessary_nullable_for_final_variable_declarations
- unnecessary_parenthesis
- unnecessary_raw_strings
- unnecessary_string_escapes
- unnecessary_string_interpolations
- unnecessary_this
- use_is_even_rather_than_modulo
- use_late_for_private_fields_and_variables
- use_rethrow_when_possible
- use_setters_to_change_properties
- use_to_and_as_if_applicable
- package_names
- sort_pub_dependencies
# To be added when null-safe:
# - cast_nullable_to_non_nullable
#- unnecessary_null_checks
# - tighten_type_of_initializing_formals
# - null_check_on_nullable_type_parameter
@@ -5,11 +5,11 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter_core/src/channels_bloc.dart'; import 'package:stream_chat_flutter_core/src/channels_bloc.dart';
import 'package:stream_chat_flutter_core/src/stream_chat_core.dart';
import 'package:stream_chat_flutter_core/src/typedef.dart'; import 'package:stream_chat_flutter_core/src/typedef.dart';
import 'stream_chat_core.dart'; /// [ChannelListCore] is a simplified class that allows fetching a list of
/// channels while exposing UI builders.
/// [ChannelListCore] is a simplified class that allows fetching a list of channels while exposing UI builders.
/// A [ChannelListController] is used to reload and paginate data. /// A [ChannelListController] is used to reload and paginate data.
/// ///
/// ///
@@ -52,10 +52,11 @@ import 'stream_chat_core.dart';
/// } /// }
/// ``` /// ```
/// ///
/// Make sure to have a [StreamChatCore] ancestor in order to provide the information about the channels. /// Make sure to have a [StreamChatCore] ancestor in order to provide the
/// information about the channels.
class ChannelListCore extends StatefulWidget { class ChannelListCore extends StatefulWidget {
/// Instantiate a new ChannelListView /// Instantiate a new ChannelListView
ChannelListCore({ const ChannelListCore({
Key key, Key key,
@required this.errorBuilder, @required this.errorBuilder,
@required this.emptyBuilder, @required this.emptyBuilder,
@@ -65,18 +66,31 @@ class ChannelListCore extends StatefulWidget {
this.options, this.options,
this.sort, this.sort,
this.pagination = const PaginationParams( this.pagination = const PaginationParams(
offset: 0,
limit: 25, limit: 25,
), ),
this.channelListController, this.channelListController,
}) : assert(errorBuilder != null), }) : assert(
assert(emptyBuilder != null), errorBuilder != null,
assert(loadingBuilder != null), 'Parameter errorBuilder should not be null',
assert(listBuilder != null), ),
assert(
emptyBuilder != null,
'Parameter emptyBuilder should not be null',
),
assert(
loadingBuilder != null,
'Parameter loadingBuilder should not be null',
),
assert(
listBuilder != null,
'Parameter listBuilder should not be null',
),
super(key: key); super(key: key);
/// A [ChannelListController] allows reloading and pagination. /// A [ChannelListController] allows reloading and pagination.
/// Use [ChannelListController.loadData] and [ChannelListController.paginateData] respectively for reloading and pagination. /// Use [ChannelListController.loadData] and
/// [ChannelListController.paginateData] respectively for reloading and
/// pagination.
final ChannelListController channelListController; final ChannelListController channelListController;
/// The builder that will be used in case of error /// The builder that will be used in case of error
@@ -103,9 +117,10 @@ class ChannelListCore extends StatefulWidget {
final Map<String, dynamic> options; final Map<String, dynamic> options;
/// The sorting used for the channels matching the filters. /// The sorting used for the channels matching the filters.
/// Sorting is based on field and direction, multiple sorting options can be provided. /// Sorting is based on field and direction, multiple sorting options can be
/// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count. /// provided.
/// Direction can be ascending or descending. /// You can sort based on last_updated, last_message_at, updated_at, created
/// _at or member_count. Direction can be ascending or descending.
final List<SortOption<ChannelModel>> sort; final List<SortOption<ChannelModel>> sort;
/// Pagination parameters /// Pagination parameters
@@ -128,24 +143,23 @@ class _ChannelListCoreState extends State<ChannelListCore> {
StreamBuilder<List<Channel>> _buildListView( StreamBuilder<List<Channel>> _buildListView(
ChannelsBlocState channelsBlocState, ChannelsBlocState channelsBlocState,
) { ) =>
return StreamBuilder<List<Channel>>( StreamBuilder<List<Channel>>(
stream: channelsBlocState.channelsStream, stream: channelsBlocState.channelsStream,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasError) { if (snapshot.hasError) {
return _buildErrorWidget(snapshot, context, channelsBlocState); return _buildErrorWidget(snapshot, context, channelsBlocState);
} }
if (!snapshot.hasData) { if (!snapshot.hasData) {
return widget.loadingBuilder(context); return widget.loadingBuilder(context);
} }
final channels = snapshot.data; final channels = snapshot.data;
if (channels.isEmpty) { if (channels.isEmpty) {
return widget.emptyBuilder(context); return widget.emptyBuilder(context);
} }
return widget.listBuilder(context, channels); return widget.listBuilder(context, channels);
}, },
); );
}
Widget _buildErrorWidget( Widget _buildErrorWidget(
AsyncSnapshot<List<Channel>> snapshot, AsyncSnapshot<List<Channel>> snapshot,
@@ -223,14 +237,15 @@ class _ChannelListCoreState extends State<ChannelListCore> {
} }
} }
/// Controller used for loading more data and controlling pagination in [ChannelListCore]. /// Controller used for loading more data and controlling pagination in
/// [ChannelListCore].
class ChannelListController { class ChannelListController {
/// This function calls Stream's servers to load a list of channels. If there is existing data, /// This function calls Stream's servers to load a list of channels.
/// calling this function causes a reload. /// If there is existing data, calling this function causes a reload.
AsyncCallback loadData; AsyncCallback loadData;
/// This function is used to load another page of data. Note, [loadData] should be /// This function is used to load another page of data. Note, [loadData]
/// used to populate the initial page of data. Calling [paginateData] performs a query /// should be used to populate the initial page of data. Calling
/// to load subsequent pages. /// [paginateData] performs a query to load subsequent pages.
AsyncCallback paginateData; AsyncCallback paginateData;
} }
@@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
@@ -16,26 +17,29 @@ import 'package:stream_chat_flutter_core/src/stream_chat_core.dart';
/// ///
/// API docs: https://getstream.io/chat/docs/flutter-dart/query_channels/ /// API docs: https://getstream.io/chat/docs/flutter-dart/query_channels/
class ChannelsBloc extends StatefulWidget { class ChannelsBloc extends StatefulWidget {
/// Creates a new [ChannelsBloc]. The parameter [child] must be supplied and not null. /// Creates a new [ChannelsBloc]. The parameter [child] must be supplied and
/// not null.
const ChannelsBloc({ const ChannelsBloc({
Key key, Key key,
@required this.child, @required this.child,
this.lockChannelsOrder = false, this.lockChannelsOrder = false,
this.channelsComparator, this.channelsComparator,
this.shouldAddChannel, this.shouldAddChannel,
}) : assert(child != null), }) : assert(child != null, 'Parameter child should not be null.'),
super(key: key); super(key: key);
/// The widget child /// The widget child
final Widget child; final Widget child;
/// Set this to true to prevent channels to be brought to the top of the list when a new message arrives /// Set this to true to prevent channels to be brought to the top of the list
/// when a new message arrives
final bool lockChannelsOrder; final bool lockChannelsOrder;
/// Comparator used to sort the channels when a message.new event is received /// Comparator used to sort the channels when a message.new event is received
final Comparator<Channel> channelsComparator; final Comparator<Channel> channelsComparator;
/// Function used to evaluate if a channel should be added to the list when a message.new event is received /// Function used to evaluate if a channel should be added to the list when a
/// message.new event is received
final bool Function(Event) shouldAddChannel; final bool Function(Event) shouldAddChannel;
@override @override
@@ -170,13 +174,14 @@ class ChannelsBlocState extends State<ChannelsBloc>
_channelsController.add(newChannels); _channelsController.add(newChannels);
} }
})); }));
// ignore: cascade_invocations
_subscriptions.add(client _subscriptions.add(client
.on( .on(
EventType.channelDeleted, EventType.channelDeleted,
EventType.notificationRemovedFromChannel, EventType.notificationRemovedFromChannel,
) )
.listen((e) { .listen((e) {
// ignore: cascade_invocations
final channel = e.channel; final channel = e.channel;
_channelsController.add(List.from( _channelsController.add(List.from(
(channels ?? [])..removeWhere((c) => c.cid == channel.cid))); (channels ?? [])..removeWhere((c) => c.cid == channel.cid)));
@@ -1,13 +1,14 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
// ignore: constant_identifier_names
enum _LoadingStatus { LOADING, STABLE } enum _LoadingStatus { LOADING, STABLE }
/// Wrapper around a [Scrollable] which triggers [onEndOfPage]/[onStartOfPage] the Scrollable /// Wrapper around a [Scrollable] which triggers [onEndOfPage]/[onStartOfPage] the Scrollable
/// reaches to the start or end of the view extent. /// reaches to the start or end of the view extent.
class LazyLoadScrollView extends StatefulWidget { class LazyLoadScrollView extends StatefulWidget {
/// Creates a new instance of [LazyLoadScrollView]. The parameter [child] must be /// Creates a new instance of [LazyLoadScrollView]. The parameter [child]
/// supplied and not null. /// must be supplied and not null.
const LazyLoadScrollView({ const LazyLoadScrollView({
Key key, Key key,
@required this.child, @required this.child,
@@ -17,7 +18,7 @@ class LazyLoadScrollView extends StatefulWidget {
this.onPageScrollEnd, this.onPageScrollEnd,
this.onInBetweenOfPage, this.onInBetweenOfPage,
this.scrollOffset = 100, this.scrollOffset = 100,
}) : assert(child != null), }) : assert(child != null, 'Parameter child should not be null'),
super(key: key); super(key: key);
/// The [Widget] that this widget watches for changes on /// The [Widget] that this widget watches for changes on
@@ -47,15 +48,13 @@ class LazyLoadScrollView extends StatefulWidget {
class _LazyLoadScrollViewState extends State<LazyLoadScrollView> { class _LazyLoadScrollViewState extends State<LazyLoadScrollView> {
_LoadingStatus _loadMoreStatus = _LoadingStatus.STABLE; _LoadingStatus _loadMoreStatus = _LoadingStatus.STABLE;
double _scrollPosition = 0.0; double _scrollPosition = 0;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => NotificationListener(
return NotificationListener( onNotification: _onNotification,
child: widget.child, child: widget.child,
onNotification: _onNotification, );
);
}
bool _onNotification(Notification notification) { bool _onNotification(Notification notification) {
if (notification is ScrollStartNotification) { if (notification is ScrollStartNotification) {
@@ -1,12 +1,15 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter_core/src/stream_channel.dart';
import 'package:stream_chat_flutter_core/src/typedef.dart'; import 'package:stream_chat_flutter_core/src/typedef.dart';
import 'stream_channel.dart';
/// [MessageListCore] is a simplified class that allows fetching a list of messages while exposing UI builders. /// [MessageListCore] is a simplified class that allows fetching a list of
/// messages while exposing UI builders.
///
/// A [MessageListController] is used to paginate data. /// A [MessageListController] is used to paginate data.
/// ///
/// ```dart /// ```dart
@@ -50,12 +53,14 @@ import 'stream_channel.dart';
/// ``` /// ```
/// ///
/// ///
/// Make sure to have a [StreamChannel] ancestor in order to provide the information about the channels. /// Make sure to have a [StreamChannel] ancestor in order to provide the
/// information about the channels.
///
/// The widget uses a [ListView.custom] to render the list of channels. /// The widget uses a [ListView.custom] to render the list of channels.
/// ///
class MessageListCore extends StatefulWidget { class MessageListCore extends StatefulWidget {
/// Instantiate a new [MessageListView]. /// Instantiate a new [MessageListView].
MessageListCore({ const MessageListCore({
Key key, Key key,
@required this.loadingBuilder, @required this.loadingBuilder,
@required this.emptyBuilder, @required this.emptyBuilder,
@@ -65,10 +70,16 @@ class MessageListCore extends StatefulWidget {
this.parentMessage, this.parentMessage,
this.messageListController, this.messageListController,
this.messageFilter, this.messageFilter,
}) : assert(loadingBuilder != null), }) : assert(loadingBuilder != null, 'loadingBuilder should not be null'),
assert(emptyBuilder != null), assert(emptyBuilder != null, 'emptyBuilder should not be null'),
assert(messageListBuilder != null), assert(
assert(errorWidgetBuilder != null), messageListBuilder != null,
'messageListBuilder should not be null',
),
assert(
errorWidgetBuilder != null,
'errorWidgetBuilder should not be null',
),
super(key: key); super(key: key);
/// A [MessageListController] allows pagination. /// A [MessageListController] allows pagination.
@@ -84,12 +95,15 @@ class MessageListCore extends StatefulWidget {
/// Function used to build an empty widget /// Function used to build an empty widget
final WidgetBuilder emptyBuilder; final WidgetBuilder emptyBuilder;
/// Callback triggered when an error occurs while performing the given request. /// Callback triggered when an error occurs while performing the given
/// This parameter can be used to display an error message to users in the event /// request.
/// of a connection failure. ///
/// This parameter can be used to display an error message to users in the
/// event of a connection failure.
final ErrorBuilder errorWidgetBuilder; final ErrorBuilder errorWidgetBuilder;
/// If true will show a scroll to bottom message when there are new messages and the scroll offset is not zero /// If true will show a scroll to bottom message when there are new messages
/// and the scroll offset is not zero.
final bool showScrollToBottom; final bool showScrollToBottom;
/// If the current message belongs to a `thread`, this property represents the /// If the current message belongs to a `thread`, this property represents the
@@ -112,11 +126,7 @@ class _MessageListCoreState extends State<MessageListCore> {
OwnUser get _currentUser => streamChannel.channel.client.state.user; OwnUser get _currentUser => streamChannel.channel.client.state.user;
int initialIndex;
double initialAlignment;
List<Message> messages = <Message>[]; List<Message> messages = <Message>[];
bool initialMessageHighlightComplete = false; bool initialMessageHighlightComplete = false;
@override @override
@@ -1,8 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter_core/src/stream_chat_core.dart';
import 'stream_chat_core.dart';
/// [MessageSearchBloc] is used to manage a list of messages with pagination. /// [MessageSearchBloc] is used to manage a list of messages with pagination.
/// This class can be used to load messages, perform queries, etc. /// This class can be used to load messages, perform queries, etc.
@@ -16,7 +15,7 @@ class MessageSearchBloc extends StatefulWidget {
const MessageSearchBloc({ const MessageSearchBloc({
Key key, Key key,
@required this.child, @required this.child,
}) : assert(child != null), }) : assert(child != null, 'Parameter child should not be null.'),
super(key: key); super(key: key);
/// The widget child /// The widget child
@@ -1,13 +1,15 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter_core/src/message_search_bloc.dart';
import 'package:stream_chat_flutter_core/src/typedef.dart'; import 'package:stream_chat_flutter_core/src/typedef.dart';
import 'message_search_bloc.dart';
/// ///
/// [MessageSearchListCore] is a simplified class that allows searching for messages across channels while exposing UI builders. /// [MessageSearchListCore] is a simplified class that allows searching for
/// A [MessageSearchListController] is used to load and paginate data. /// messages across channels while exposing UI builders.
/// A [MessageSearchListController] is used to load and paginate data.
/// ///
/// ```dart /// ```dart
/// class MessageSearchPage extends StatelessWidget { /// class MessageSearchPage extends StatelessWidget {
@@ -28,7 +30,8 @@ import 'message_search_bloc.dart';
/// } /// }
/// ``` /// ```
/// ///
/// Make sure to have a [MessageSearchBloc] ancestor in order to provide the information about the messages. /// Make sure to have a [MessageSearchBloc] ancestor in order to provide the
/// information about the messages.
/// The widget uses a [ListView.separated] to render the list of messages. /// The widget uses a [ListView.separated] to render the list of messages.
/// ///
class MessageSearchListCore extends StatefulWidget { class MessageSearchListCore extends StatefulWidget {
@@ -50,14 +53,16 @@ class MessageSearchListCore extends StatefulWidget {
this.paginationParams, this.paginationParams,
this.messageFilters, this.messageFilters,
this.messageSearchListController, this.messageSearchListController,
}) : assert(emptyBuilder != null), }) : assert(emptyBuilder != null, 'emptyBuilder should not be null'),
assert(errorBuilder != null), assert(errorBuilder != null, 'errorBuilder should not be null'),
assert(loadingBuilder != null), assert(loadingBuilder != null, 'loadingBuilder should not be null'),
assert(childBuilder != null), assert(childBuilder != null, 'childBuilder should not be null'),
super(key: key); super(key: key);
/// A [MessageSearchListController] allows reloading and pagination. /// A [MessageSearchListController] allows reloading and pagination.
/// Use [MessageSearchListController.loadData] and [MessageSearchListController.paginateData] respectively for reloading and pagination. /// Use [MessageSearchListController.loadData] and
/// [MessageSearchListController.paginateData] respectively for reloading and
/// pagination.
final MessageSearchListController messageSearchListController; final MessageSearchListController messageSearchListController;
/// Message String to search on /// Message String to search on
@@ -69,9 +74,10 @@ class MessageSearchListCore extends StatefulWidget {
final Map<String, dynamic> filters; final Map<String, dynamic> filters;
/// The sorting used for the channels matching the filters. /// The sorting used for the channels matching the filters.
/// Sorting is based on field and direction, multiple sorting options can be provided. /// Sorting is based on field and direction, multiple sorting options can be
/// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count. /// provided.
/// Direction can be ascending or descending. /// You can sort based on last_updated, last_message_at, updated_at, created_
/// at or member_count. Direction can be ascending or descending.
final List<SortOption> sortOptions; final List<SortOption> sortOptions;
/// Pagination parameters /// Pagination parameters
@@ -103,10 +109,9 @@ class MessageSearchListCore extends StatefulWidget {
class _MessageSearchListCoreState extends State<MessageSearchListCore> { class _MessageSearchListCoreState extends State<MessageSearchListCore> {
@override @override
void initState() { void didChangeDependencies() {
super.initState(); super.didChangeDependencies();
final messageSearchBloc = MessageSearchBloc.of(context); MessageSearchBloc.of(context).search(
messageSearchBloc.search(
filter: widget.filters, filter: widget.filters,
sort: widget.sortOptions, sort: widget.sortOptions,
query: widget.messageQuery, query: widget.messageQuery,
@@ -126,37 +131,34 @@ class _MessageSearchListCoreState extends State<MessageSearchListCore> {
return _buildListView(messageSearchBloc); return _buildListView(messageSearchBloc);
} }
Widget _buildListView(MessageSearchBlocState messageSearchBloc) { Widget _buildListView(MessageSearchBlocState messageSearchBloc) =>
return StreamBuilder<List<GetMessageResponse>>( StreamBuilder<List<GetMessageResponse>>(
stream: messageSearchBloc.messagesStream, stream: messageSearchBloc.messagesStream,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasError) { if (snapshot.hasError) {
if (snapshot.error is Error) { if (snapshot.error is Error) {
print((snapshot.error as Error).stackTrace); print((snapshot.error as Error).stackTrace);
}
return widget.errorBuilder(context, snapshot.error);
} }
return widget.errorBuilder(context, snapshot.error); if (!snapshot.hasData) {
} return widget.loadingBuilder(context);
}
if (!snapshot.hasData) { final items = snapshot.data;
return widget.loadingBuilder(context);
}
final items = snapshot.data; if (items.isEmpty) {
return widget.emptyBuilder(context);
}
if (items.isEmpty) { return widget.childBuilder(snapshot.data);
return widget.emptyBuilder(context); },
} );
return widget.childBuilder(snapshot.data);
},
);
}
void loadData() { void loadData() {
final messageSearchBloc = MessageSearchBloc.of(context); MessageSearchBloc.of(context).search(
messageSearchBloc.search(
filter: widget.filters, filter: widget.filters,
sort: widget.sortOptions, sort: widget.sortOptions,
query: widget.messageQuery, query: widget.messageQuery,
@@ -189,8 +191,7 @@ class _MessageSearchListCoreState extends State<MessageSearchListCore> {
widget.messageQuery?.toString() != oldWidget.messageQuery?.toString() || widget.messageQuery?.toString() != oldWidget.messageQuery?.toString() ||
widget.messageFilters?.toString() != widget.messageFilters?.toString() !=
oldWidget.messageFilters?.toString()) { oldWidget.messageFilters?.toString()) {
final messageSearchBloc = MessageSearchBloc.of(context); MessageSearchBloc.of(context).search(
messageSearchBloc.search(
filter: widget.filters, filter: widget.filters,
sort: widget.sortOptions, sort: widget.sortOptions,
query: widget.messageQuery, query: widget.messageQuery,
@@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
@@ -25,8 +26,8 @@ class StreamChannel extends StatefulWidget {
@required this.channel, @required this.channel,
this.showLoading = true, this.showLoading = true,
this.initialMessageId, this.initialMessageId,
}) : assert(child != null), }) : assert(child != null, 'Child should not be null'),
assert(channel != null), assert(channel != null, 'Channel should not be null'),
super(key: key); super(key: key);
// ignore: public_member_api_docs // ignore: public_member_api_docs
@@ -49,7 +50,8 @@ class StreamChannel extends StatefulWidget {
if (streamChannelState == null) { if (streamChannelState == null) {
throw Exception( throw Exception(
'You must have a StreamChannel widget at the top of your widget tree'); 'You must have a StreamChannel widget at the top of your widget tree',
);
} }
return streamChannelState; return streamChannelState;
@@ -204,14 +206,13 @@ class StreamChannelState extends State<StreamChannel> {
int before = 20, int before = 20,
int after = 20, int after = 20,
bool preferOffline = false, bool preferOffline = false,
}) { }) =>
return queryAtMessage( queryAtMessage(
messageId: messageId, messageId: messageId,
before: before, before: before,
after: after, after: after,
preferOffline: preferOffline, preferOffline: preferOffline,
); );
}
/// ///
Future<void> queryAtMessage({ Future<void> queryAtMessage({
@@ -254,15 +255,14 @@ class StreamChannelState extends State<StreamChannel> {
String messageId, { String messageId, {
int limit = 20, int limit = 20,
bool preferOffline = false, bool preferOffline = false,
}) { }) =>
return channel.query( channel.query(
messagesPagination: PaginationParams( messagesPagination: PaginationParams(
lessThan: messageId, lessThan: messageId,
limit: limit, limit: limit,
), ),
preferOffline: preferOffline, preferOffline: preferOffline,
); );
}
/// ///
Future<ChannelState> queryAfterMessage( Future<ChannelState> queryAfterMessage(
@@ -366,9 +366,10 @@ class StreamChannelState extends State<StreamChannel> {
); );
} }
final initialized = snapshot.data[0]; final initialized = snapshot.data[0];
// ignore: avoid_bool_literals_in_conditional_expressions
final dataLoaded = initialMessageId == null ? true : snapshot.data[1]; final dataLoaded = initialMessageId == null ? true : snapshot.data[1];
if (widget.showLoading && (!initialized || !dataLoaded)) { if (widget.showLoading && (!initialized || !dataLoaded)) {
return Center( return const Center(
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
); );
} }
@@ -3,13 +3,12 @@ import 'dart:async';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter_core/src/typedef.dart';
import 'typedef.dart';
/// Widget used to provide information about the chat to the widget tree. /// Widget used to provide information about the chat to the widget tree.
/// This Widget is used to react to life cycle changes and system updates. /// This Widget is used to react to life cycle changes and system updates.
/// When the app goes into the background, the websocket connection is kept alive /// When the app goes into the background, the websocket connection is kept
/// for two minutes before being terminated. /// alive for two minutes before being terminated.
/// ///
/// Conversely, when app is resumed or restarted, a new connection is initiated. /// Conversely, when app is resumed or restarted, a new connection is initiated.
/// ///
@@ -36,16 +35,16 @@ import 'typedef.dart';
class StreamChatCore extends StatefulWidget { class StreamChatCore extends StatefulWidget {
/// Constructor used for creating a new instance of [StreamChatCore]. /// Constructor used for creating a new instance of [StreamChatCore].
/// ///
/// [StreamChatCore] is a stateful widget which reacts to system events and updates /// [StreamChatCore] is a stateful widget which reacts to system events and
/// Stream's connection status accordingly. /// updates Stream's connection status accordingly.
StreamChatCore({ const StreamChatCore({
Key key, Key key,
@required this.client, @required this.client,
@required this.child, @required this.child,
this.onBackgroundEventReceived, this.onBackgroundEventReceived,
this.backgroundKeepAlive = const Duration(minutes: 1), this.backgroundKeepAlive = const Duration(minutes: 1),
}) : assert(client != null), }) : assert(client != null, 'Stream Chat Client should not be null'),
assert(child != null), assert(child != null, 'Child should not be null'),
super(key: key); super(key: key);
/// Instance of Stream Chat Client containing information about the current /// Instance of Stream Chat Client containing information about the current
@@ -55,7 +54,8 @@ class StreamChatCore extends StatefulWidget {
/// Widget descendant. /// Widget descendant.
final Widget child; final Widget child;
/// The amount of time that will pass before disconnecting the client in the background /// The amount of time that will pass before disconnecting the client in
/// the background
final Duration backgroundKeepAlive; final Duration backgroundKeepAlive;
/// Handler called whenever the [client] receives a new [Event] while the app /// Handler called whenever the [client] receives a new [Event] while the app
@@ -90,9 +90,7 @@ class StreamChatCoreState extends State<StreamChatCore>
Timer _disconnectTimer; Timer _disconnectTimer;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => widget.child;
return widget.child;
}
/// The current user /// The current user
User get user => widget.client.state.user; User get user => widget.client.state.user;
@@ -2,8 +2,8 @@ import 'package:flutter/widgets.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
/// A signature for a callback which exposes an error and returns a function. /// A signature for a callback which exposes an error and returns a function.
/// This Callback can be used in cases where an API failure occurs and the widget /// This Callback can be used in cases where an API failure occurs and the
/// is unable to render data. /// widget is unable to render data.
typedef ErrorBuilder = Widget Function(BuildContext context, Object error); typedef ErrorBuilder = Widget Function(BuildContext context, Object error);
/// A Signature for a handler function which will expose a [event]. /// A Signature for a handler function which will expose a [event].
@@ -1,11 +1,13 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter_core/src/users_bloc.dart'; import 'package:stream_chat_flutter_core/src/users_bloc.dart';
/// ///
/// [UserListCore] is a simplified class that allows fetching users while exposing UI builders. /// [UserListCore] is a simplified class that allows fetching users while
/// exposing UI builders.
/// A [UserListController] is used to load and paginate data. /// A [UserListController] is used to load and paginate data.
/// ///
/// ```dart /// ```dart
@@ -50,30 +52,31 @@ import 'package:stream_chat_flutter_core/src/users_bloc.dart';
/// [UsersBloc] must be the ancestor of this widget. This is necessary since /// [UsersBloc] must be the ancestor of this widget. This is necessary since
/// [UserListCore] depends on functionality contained within [UsersBloc]. /// [UserListCore] depends on functionality contained within [UsersBloc].
/// ///
/// The parameters [listBuilder], [loadingBuilder], [emptyBuilder] and [errorBuilder] must all be supplied /// The parameters [listBuilder], [loadingBuilder], [emptyBuilder] and
/// and not null. /// [errorBuilder] must all be supplied and not null.
class UserListCore extends StatefulWidget { class UserListCore extends StatefulWidget {
/// Instantiate a new [UserListCore] /// Instantiate a new [UserListCore]
const UserListCore({ const UserListCore({
Key key,
@required this.errorBuilder, @required this.errorBuilder,
@required this.emptyBuilder, @required this.emptyBuilder,
@required this.loadingBuilder, @required this.loadingBuilder,
@required this.listBuilder, @required this.listBuilder,
Key key,
this.filter, this.filter,
this.options, this.options,
this.sort, this.sort,
this.pagination, this.pagination,
this.groupAlphabetically = false, this.groupAlphabetically = false,
this.userListController, this.userListController,
}) : assert(errorBuilder != null), }) : assert(errorBuilder != null, ''),
assert(emptyBuilder != null), assert(emptyBuilder != null, ''),
assert(loadingBuilder != null), assert(loadingBuilder != null, ''),
assert(listBuilder != null), assert(listBuilder != null, ''),
super(key: key); super(key: key);
/// A [UserListController] allows reloading and pagination. /// A [UserListController] allows reloading and pagination.
/// Use [UserListController.loadData] and [UserListController.paginateData] respectively for reloading and pagination. /// Use [UserListController.loadData] and [UserListController.paginateData]
/// respectively for reloading and pagination.
final UserListController userListController; final UserListController userListController;
/// The builder that will be used in case of error /// The builder that will be used in case of error
@@ -100,9 +103,9 @@ class UserListCore extends StatefulWidget {
final Map<String, dynamic> options; final Map<String, dynamic> options;
/// The sorting used for the channels matching the filters. /// The sorting used for the channels matching the filters.
/// Sorting is based on field and direction, multiple sorting options can be provided. /// Sorting is based on field and direction, multiple sorting options can be
/// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count. /// provided. You can sort based on last_updated, last_message_at, updated_at,
/// Direction can be ascending or descending. /// created_at or member_count. Direction can be ascending or descending.
final List<SortOption> sort; final List<SortOption> sort;
/// Pagination parameters /// Pagination parameters
@@ -123,10 +126,9 @@ class UserListCore extends StatefulWidget {
class _UserListCoreState extends State<UserListCore> class _UserListCoreState extends State<UserListCore>
with WidgetsBindingObserver { with WidgetsBindingObserver {
@override @override
void initState() { void didChangeDependencies() {
super.initState(); super.didChangeDependencies();
final usersBloc = UsersBloc.of(context); UsersBloc.of(context).queryUsers(
usersBloc.queryUsers(
filter: widget.filter, filter: widget.filter,
sort: widget.sort, sort: widget.sort,
pagination: widget.pagination, pagination: widget.pagination,
@@ -151,68 +153,66 @@ class _UserListCoreState extends State<UserListCore>
Stream<List<ListItem>> _buildUserStream( Stream<List<ListItem>> _buildUserStream(
UsersBlocState usersBlocState, UsersBlocState usersBlocState,
) { ) =>
return usersBlocState.usersStream.map( usersBlocState.usersStream.map(
(users) { (users) {
if (widget.groupAlphabetically) { if (widget.groupAlphabetically) {
var temp = users; var temp = users;
if (!isListAlreadySorted) { if (!isListAlreadySorted) {
temp = users..sort((curr, next) => curr.name.compareTo(next.name)); temp = users
..sort((curr, next) => curr.name.compareTo(next.name));
}
final groupedUsers = <String, List<User>>{};
for (final e in temp) {
final alphabet = e.name[0]?.toUpperCase();
groupedUsers[alphabet] = [...groupedUsers[alphabet] ?? [], e];
}
final items = <ListItem>[];
for (final key in groupedUsers.keys) {
items
..add(ListHeaderItem(key))
..addAll(groupedUsers[key].map((e) => ListUserItem(e)));
}
return items;
} }
final groupedUsers = <String, List<User>>{}; return users.map((e) => ListUserItem(e)).toList();
for (final e in temp) { },
final alphabet = e.name[0]?.toUpperCase(); );
groupedUsers[alphabet] = [...groupedUsers[alphabet] ?? [], e];
}
final items = <ListItem>[];
for (final key in groupedUsers.keys) {
items.add(ListHeaderItem(key));
items.addAll(groupedUsers[key].map((e) => ListUserItem(e)));
}
return items;
}
return users.map((e) => ListUserItem(e)).toList();
},
);
}
StreamBuilder<List<ListItem>> _buildListView( StreamBuilder<List<ListItem>> _buildListView(
UsersBlocState usersBlocState, UsersBlocState usersBlocState,
) { ) =>
return StreamBuilder( StreamBuilder(
stream: _buildUserStream(usersBlocState), stream: _buildUserStream(usersBlocState),
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasError) { if (snapshot.hasError) {
if (snapshot.error is Error) { if (snapshot.error is Error) {
print((snapshot.error as Error).stackTrace); print((snapshot.error as Error).stackTrace);
}
return widget.errorBuilder(snapshot.error);
} }
return widget.errorBuilder(snapshot.error); if (!snapshot.hasData) {
} return widget.loadingBuilder(context);
}
if (!snapshot.hasData) { final items = snapshot.data;
return widget.loadingBuilder(context);
}
final items = snapshot.data; if (items.isEmpty) {
return widget.emptyBuilder(context);
}
if (items.isEmpty) { if (items.isEmpty) {
return widget.emptyBuilder(context); return widget.emptyBuilder(context);
} }
if (items.isEmpty) { return widget.listBuilder(context, items);
return widget.emptyBuilder(context); },
} );
return widget.listBuilder(context, items);
},
);
}
void loadData() { void loadData() {
final _usersBloc = UsersBloc.of(context); UsersBloc.of(context).queryUsers(
_usersBloc.queryUsers(
filter: widget.filter, filter: widget.filter,
sort: widget.sort, sort: widget.sort,
pagination: widget.pagination, pagination: widget.pagination,
@@ -241,8 +241,7 @@ class _UserListCoreState extends State<UserListCore>
widget.pagination?.toJson()?.toString() != widget.pagination?.toJson()?.toString() !=
oldWidget.pagination?.toJson()?.toString() || oldWidget.pagination?.toJson()?.toString() ||
widget.options?.toString() != oldWidget.options?.toString()) { widget.options?.toString() != oldWidget.options?.toString()) {
final usersBloc = UsersBloc.of(context); UsersBloc.of(context).queryUsers(
usersBloc.queryUsers(
filter: widget.filter, filter: widget.filter,
sort: widget.sort, sort: widget.sort,
pagination: widget.pagination, pagination: widget.pagination,
@@ -253,8 +252,8 @@ class _UserListCoreState extends State<UserListCore>
} }
/// Represents an item in a the user stream list. /// Represents an item in a the user stream list.
/// Header items are prefixed with the key `HEADER` While users are prefixed with /// Header items are prefixed with the key `HEADER` While users are prefixed
/// `USER`. /// with `USER`.
abstract class ListItem { abstract class ListItem {
// ignore: public_member_api_docs // ignore: public_member_api_docs
String get key { String get key {
@@ -280,26 +279,26 @@ abstract class ListItem {
if (this is ListUserItem) { if (this is ListUserItem) {
return userItem((this as ListUserItem).user); return userItem((this as ListUserItem).user);
} }
return SizedBox(); return const SizedBox();
} }
} }
// ignore: public_member_api_docs // ignore: public_member_api_docs
class ListHeaderItem extends ListItem { class ListHeaderItem extends ListItem {
// ignore: public_member_api_docs // ignore: public_member_api_docs
final String heading; ListHeaderItem(this.heading);
// ignore: public_member_api_docs // ignore: public_member_api_docs
ListHeaderItem(this.heading); final String heading;
} }
// ignore: public_member_api_docs // ignore: public_member_api_docs
class ListUserItem extends ListItem { class ListUserItem extends ListItem {
// ignore: public_member_api_docs // ignore: public_member_api_docs
final User user; ListUserItem(this.user);
// ignore: public_member_api_docs // ignore: public_member_api_docs
ListUserItem(this.user); final User user;
} }
/// Controller used for paginating data in [ChannelListView] /// Controller used for paginating data in [ChannelListView]
@@ -1,8 +1,8 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
import 'stream_chat_core.dart';
/// Widget dedicated to the management of a users list with pagination. /// Widget dedicated to the management of a users list with pagination.
/// ///
@@ -14,9 +14,12 @@ class UsersBloc extends StatefulWidget {
/// Instantiate a new [UsersBloc]. The parameter [child] must be supplied and /// Instantiate a new [UsersBloc]. The parameter [child] must be supplied and
/// not null. /// not null.
const UsersBloc({ const UsersBloc({
Key key,
@required this.child, @required this.child,
}) : assert(child != null), Key key,
}) : assert(
child != null,
'When constructing a UsersBloc, the parameter '
'child should not be null.'),
super(key: key); super(key: key);
/// The widget child /// The widget child
@@ -1,5 +1,7 @@
library stream_chat_flutter_core; library stream_chat_flutter_core;
export 'package:stream_chat/stream_chat.dart';
export 'src/channel_list_core.dart'; export 'src/channel_list_core.dart';
export 'src/channels_bloc.dart'; export 'src/channels_bloc.dart';
export 'src/lazy_load_scroll_view.dart'; export 'src/lazy_load_scroll_view.dart';
@@ -8,7 +10,6 @@ export 'src/message_search_bloc.dart';
export 'src/message_search_list_core.dart'; export 'src/message_search_list_core.dart';
export 'src/stream_channel.dart'; export 'src/stream_channel.dart';
export 'src/stream_chat_core.dart'; export 'src/stream_chat_core.dart';
export 'src/typedef.dart';
export 'src/user_list_core.dart'; export 'src/user_list_core.dart';
export 'src/users_bloc.dart'; export 'src/users_bloc.dart';
export 'src/typedef.dart';
export 'package:stream_chat/stream_chat.dart';
@@ -10,14 +10,13 @@ environment:
flutter: ">=1.17.0" flutter: ">=1.17.0"
dependencies: dependencies:
stream_chat: ^1.5.0
flutter: flutter:
sdk: flutter sdk: flutter
rxdart: ^0.25.0 rxdart: ^0.25.0
stream_chat: ^1.5.0
dev_dependencies: dev_dependencies:
mockito: ^4.1.4 fake_async: ^1.1.0
flutter_test: flutter_test:
sdk: flutter sdk: flutter
fake_async: ^1.1.0 mockito: ^4.1.4
@@ -3,8 +3,8 @@ import 'dart:async';
import 'package:fake_async/fake_async.dart'; import 'package:fake_async/fake_async.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
import 'mocks.dart'; import 'mocks.dart';
@@ -110,17 +110,14 @@ void main() {
onBackgroundEventReceived: showLocalNotificationMock, onBackgroundEventReceived: showLocalNotificationMock,
backgroundKeepAlive: const Duration(seconds: 4), backgroundKeepAlive: const Duration(seconds: 4),
child: Builder( child: Builder(
builder: (context) { builder: (context) => Container(),
return Container();
},
), ),
), ),
); );
final sc = scKey.currentState; scKey.currentState.didChangeAppLifecycleState(AppLifecycleState.paused);
sc.didChangeAppLifecycleState(AppLifecycleState.paused);
_async.elapse(Duration(seconds: 5)); _async.elapse(const Duration(seconds: 5));
verify(client.disconnect()).called(1); verify(client.disconnect()).called(1);
eventStreamController.close(); eventStreamController.close();
@@ -129,7 +126,8 @@ void main() {
); );
testWidgets( testWidgets(
'StreamChatCore should handle notifications when on background and connected', 'StreamChatCore should handle notifications when on background and '
'connected',
(WidgetTester tester) async { (WidgetTester tester) async {
final client = MockClient(); final client = MockClient();
final clientState = MockClientState(); final clientState = MockClientState();
@@ -154,15 +152,12 @@ void main() {
onBackgroundEventReceived: showLocalNotificationMock, onBackgroundEventReceived: showLocalNotificationMock,
backgroundKeepAlive: const Duration(seconds: 4), backgroundKeepAlive: const Duration(seconds: 4),
child: Builder( child: Builder(
builder: (context) { builder: (context) => Container(),
return Container();
},
), ),
), ),
); );
final sc = scKey.currentState; scKey.currentState.didChangeAppLifecycleState(AppLifecycleState.paused);
sc.didChangeAppLifecycleState(AppLifecycleState.paused);
final event = Event( final event = Event(
type: EventType.messageNew, type: EventType.messageNew,
message: Message(text: 'hey'), message: Message(text: 'hey'),