Merge remote-tracking branch 'origin/develop' into fix/channel-image

# Conflicts:
#	packages/stream_chat_flutter/test/src/channel_image_test.dart
This commit is contained in:
xsahil03x
2021-07-16 14:46:29 +05:30
47 changed files with 1163 additions and 1051 deletions
-2
View File
@@ -1,10 +1,8 @@
analyzer:
exclude:
- packages/*/lib/**/*.g.dart
- packages/*/example/**
- packages/*/lib/src/emoji
- packages/*/lib/**/*.freezed.dart
- packages/*/test/**
linter:
rules:
+76 -80
View File
@@ -13,7 +13,7 @@ Future<void> main() async {
await client.connectUser(
User(
id: 'cool-shadow-7',
extraData: {
extraData: const {
'image':
'https://getstream.io/random_png/?id=cool-shadow-7&amp;name=Cool+shadow',
},
@@ -57,12 +57,10 @@ class StreamExample extends StatelessWidget {
final Channel channel;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Stream Chat Dart Example',
home: HomeScreen(channel: channel),
);
}
Widget build(BuildContext context) => MaterialApp(
title: 'Stream Chat Dart Example',
home: HomeScreen(channel: channel),
);
}
/// Main screen of our application. The layout is comprised of an [AppBar]
@@ -167,83 +165,81 @@ class _MessageViewState extends State<MessageView> {
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: ListView.builder(
controller: _scrollController,
itemCount: _messages.length,
reverse: true,
itemBuilder: (BuildContext context, int index) {
final item = _messages[index];
if (item.user?.id == widget.channel.client.uid) {
return Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.all(8),
child: Text(item.text ?? ''),
),
);
} else {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8),
child: Text(item.text ?? ''),
),
);
}
},
Widget build(BuildContext context) => Column(
children: [
Expanded(
child: ListView.builder(
controller: _scrollController,
itemCount: _messages.length,
reverse: true,
itemBuilder: (BuildContext context, int index) {
final item = _messages[index];
if (item.user?.id == widget.channel.client.uid) {
return Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.all(8),
child: Text(item.text ?? ''),
),
);
} else {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8),
child: Text(item.text ?? ''),
),
);
}
},
),
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: const InputDecoration(
hintText: 'Enter your message',
),
),
),
Material(
type: MaterialType.circle,
color: Colors.blue,
clipBehavior: Clip.hardEdge,
child: InkWell(
onTap: () async {
// We can send a new message by calling `sendMessage` on
// the current channel. After sending a message, the
// TextField is cleared and the list view is scrolled
// to show the new item.
if (_controller.value.text.isNotEmpty) {
await widget.channel.sendMessage(
Message(text: _controller.value.text),
);
_controller.clear();
_updateList();
}
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Center(
child: Icon(
Icons.send,
color: Colors.white,
),
Padding(
padding: const EdgeInsets.all(8),
child: Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: const InputDecoration(
hintText: 'Enter your message',
),
),
),
)
],
),
)
],
);
}
Material(
type: MaterialType.circle,
color: Colors.blue,
clipBehavior: Clip.hardEdge,
child: InkWell(
onTap: () async {
// We can send a new message by calling `sendMessage` on
// the current channel. After sending a message, the
// TextField is cleared and the list view is scrolled
// to show the new item.
if (_controller.value.text.isNotEmpty) {
await widget.channel.sendMessage(
Message(text: _controller.value.text),
);
_controller.clear();
_updateList();
}
},
child: const Padding(
padding: EdgeInsets.all(8),
child: Center(
child: Icon(
Icons.send,
color: Colors.white,
),
),
),
),
)
],
),
)
],
);
}
/// Helper extension for quickly retrieving
@@ -555,7 +555,7 @@ void main() {
});
test(
'should directly update the state with message as deleted if the state is sending or failed',
'''should directly update the state with message as deleted if the state is sending or failed''',
() async {
const messageId = 'test-message-id';
final message = Message(
@@ -981,7 +981,7 @@ void main() {
);
test(
'should override previous reaction if present and `enforceUnique` is true',
'''should override previous reaction if present and `enforceUnique` is true''',
() async {
const userId = 'test-user-id';
const messageId = 'test-message-id';
@@ -1875,7 +1875,7 @@ void main() {
});
test(
'should send `typingStart` event if there is not already a typingEvent or the difference between the two is >= 2 seconds',
'''should send `typingStart` event if there is not already a typingEvent or the difference between the two is >= 2 seconds''',
() async {
final typingEvent = Event(type: EventType.typingStart);
@@ -47,12 +47,6 @@ void main() {
final user = User(id: 'test-user-id');
final token = Token.development(user.id).rawValue;
final event = Event(
type: EventType.healthCheck,
connectionId: 'fake-connection-id',
me: OwnUser.fromUser(user),
);
expectLater(
// skipping first seed status -> ConnectionStatus.disconnected
client.wsConnectionStatusStream.skip(1),
@@ -74,12 +68,6 @@ void main() {
return Token.development(userId).rawValue;
}
final event = Event(
type: EventType.healthCheck,
connectionId: 'fake-connection-id',
me: OwnUser.fromUser(user),
);
expectLater(
// skipping first seed status -> ConnectionStatus.disconnected
client.wsConnectionStatusStream.skip(1),
@@ -106,12 +94,6 @@ void main() {
..accessToken = token,
);
final event = Event(
type: EventType.healthCheck,
connectionId: 'fake-connection-id',
me: OwnUser.fromUser(user),
);
expectLater(
// skipping first seed status -> ConnectionStatus.disconnected
client.wsConnectionStatusStream.skip(1),
@@ -431,7 +413,7 @@ void main() {
});
test(
'`.connectUser` should connect successfully if persistence contains event',
'''`.connectUser` should connect successfully if persistence contains event''',
() async {
final user = User(id: 'test-user-id');
final token = Token.development(user.id).rawValue;
@@ -452,7 +434,7 @@ void main() {
);
test(
'`.connectUserWithProvider` should connect successfully if persistence contains event',
'''`.connectUserWithProvider` should connect successfully if persistence contains event''',
() async {
final user = User(id: 'test-user-id');
Future<String> tokenProvider(String userId) async {
@@ -476,7 +458,7 @@ void main() {
);
test(
'`.connectGuestUser` should connect successfully if persistence contains event',
'''`.connectGuestUser` should connect successfully if persistence contains event''',
() async {
final user = User(id: 'test-user-id');
final token = Token.development(user.id).rawValue;
@@ -507,7 +489,7 @@ void main() {
);
test(
'`.connectAnonymousUser` should connect successfully if persistence contains event',
'''`.connectAnonymousUser` should connect successfully if persistence contains event''',
() async {
final user = User(id: 'test-user-id');
@@ -561,7 +543,7 @@ void main() {
group('`.sync`', () {
test(
'should update persistence connectionInfo and lastSync when sync succeeds',
'''should update persistence connectionInfo and lastSync when sync succeeds''',
() async {
const cids = ['test-cid-1', 'test-cid-2', 'test-cid-3'];
final lastSyncAt = DateTime.now();
@@ -735,7 +717,7 @@ void main() {
);
test(
'should never rethrow network call if persistence already emitted some channels',
'''should never rethrow network call if persistence already emitted some channels''',
() async {
final persistentChannelStates = List.generate(
3,
@@ -943,7 +925,7 @@ void main() {
});
test(
'should rethrow if `.queryChannelsOnline` throws and persistence channels are empty',
'''should rethrow if `.queryChannelsOnline` throws and persistence channels are empty''',
() async {
when(() => api.channel.queryChannels(
filter: any(named: 'filter'),
@@ -57,7 +57,7 @@ void main() {
});
test(
'connectionIdInterceptor should be added if connectionIdManager is provided',
'''connectionIdInterceptor should be added if connectionIdManager is provided''',
() {
const apiKey = 'api-key';
final client = StreamHttpClient(
@@ -45,7 +45,7 @@ void main() {
});
test(
'`setTokenOrProvider` should throw if both token and provider is not provided',
'''`setTokenOrProvider` should throw if both token and provider is not provided''',
() async {
expect(tokenManager.userId, isNull);
@@ -192,7 +192,7 @@ void main() {
test('custom with no operator', () {
const key = 'testKey';
const values = ['testValue'];
final filter = Filter.custom(key: key, value: values);
const filter = Filter.custom(key: key, value: values);
final encoded = json.encode(filter);
expect(
encoded,
@@ -13,7 +13,7 @@ void main() {
expect(reaction.type, 'wow');
expect(
reaction.user?.toJson(),
User(id: '2de0297c-f3f2-489d-b930-ef77342edccf', extraData: {
User(id: '2de0297c-f3f2-489d-b930-ef77342edccf', extraData: const {
'image': 'https://randomuser.me/api/portraits/women/45.jpg',
'name': 'Daisy Morgan'
}).toJson(),
@@ -28,7 +28,8 @@ void main() {
messageId: '76cd8c82-b557-4e48-9d12-87995d3a0e04',
createdAt: DateTime.parse('2020-01-28T22:17:31.108742Z'),
type: 'wow',
user: User(id: '2de0297c-f3f2-489d-b930-ef77342edccf', extraData: {
user:
User(id: '2de0297c-f3f2-489d-b930-ef77342edccf', extraData: const {
'image': 'https://randomuser.me/api/portraits/women/45.jpg',
'name': 'Daisy Morgan'
}),
@@ -154,7 +154,6 @@ void main() {
});
test('updateChannelState', () async {
const cid = 'test:cid';
final channelState = ChannelState();
persistenceClient.updateChannelState(channelState);
});
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:stream_chat_persistence/stream_chat_persistence.dart';
/// A chat-persisted StreamChatClient
final chatPersistentClient = StreamChatPersistenceClient(
logLevel: Level.INFO,
);
@@ -9,73 +10,84 @@ final chatPersistentClient = StreamChatPersistenceClient(
void main() async {
WidgetsFlutterBinding.ensureInitialized();
/// Create a new instance of [StreamChatClient] passing the apikey obtained from your
/// project dashboard.
/// Create a new instance of [StreamChatClient] passing the apikey obtained
/// from your project dashboard.
final client = StreamChatClient(
's2dxdhpxd94g',
logLevel: Level.INFO,
)..chatPersistenceClient = chatPersistentClient;
/// Set the current user and connect the websocket. In a production scenario, this should be done using
/// a backend to generate a user token using our server SDK.
/// Set the current user and connect the websocket. In a production
/// scenario, this should be done using a backend to generate a user token
/// using our server SDK.
///
/// Please see the following for more information:
/// https://getstream.io/chat/docs/ios_user_setup_and_tokens/
await client.connectUser(
User(id: 'super-band-9'),
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A',
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.'
'0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A',
);
final channel = client.channel('messaging', id: 'godevs');
await channel.watch();
runApp(MyApp(client, channel));
runApp(
MyApp(
client: client,
channel: channel,
),
);
}
/// Example application using Stream Chat Flutter widgets.
/// Stream Chat Flutter is a set of Flutter widgets which provide full chat functionalities
/// for building Flutter applications using Stream.
/// If you'd prefer using minimal wrapper widgets for your app, please see our other
///
/// Stream Chat Flutter is a set of Flutter widgets which provide full chat
/// functionalities for building Flutter applications using Stream. If you'd
/// prefer using minimal wrapper widgets for your app, please see our other
/// package, `stream_chat_flutter_core`.
class MyApp extends StatelessWidget {
/// Example using Stream's Flutter package.
///
/// If you'd prefer using minimal wrapper widgets for your app, please see
/// our other package, `stream_chat_flutter_core`.
const MyApp({
Key? key,
required this.client,
required this.channel,
}) : super(key: key);
/// Instance of Stream Client.
/// Stream's [StreamChatClient] can be used to connect to our servers and set the default
/// user for the application. Performing these actions trigger a websocket connection
/// allowing for real-time updates.
///
/// Stream's [StreamChatClient] can be used to connect to our servers and
/// set the default user for the application. Performing these actions
/// trigger a websocket connection allowing for real-time updates.
final StreamChatClient client;
/// Instance of the Channel
final Channel channel;
/// Example using Stream's Flutter package.
/// If you'd prefer using minimal wrapper widgets for your app, please see our other
/// package, `stream_chat_flutter_core`.
MyApp(this.client, this.channel);
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
themeMode: ThemeMode.system,
builder: (context, widget) {
return StreamChat(
Widget build(BuildContext context) => MaterialApp(
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
builder: (context, widget) => StreamChat(
client: client,
child: widget,
);
},
home: StreamChannel(
channel: channel,
child: ChannelPage(),
),
);
}
),
home: StreamChannel(
channel: channel,
child: const ChannelPage(),
),
);
}
/// A list of messages sent in the current channel.
///
/// This is implemented using [MessageListView], a widget that provides query functionalities
/// fetching the messages from the api and showing them in a listView
/// This is implemented using [MessageListView], a widget that provides query
/// functionalities fetching the messages from the api and showing them in a
/// listView.
class ChannelPage extends StatelessWidget {
/// Creates the page that shows the list of messages
const ChannelPage({
@@ -83,17 +95,15 @@ class ChannelPage extends StatelessWidget {
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: ChannelHeader(),
body: Column(
children: <Widget>[
Expanded(
child: MessageListView(),
),
MessageInput(),
],
),
);
}
Widget build(BuildContext context) => Scaffold(
appBar: const ChannelHeader(),
body: Column(
children: const <Widget>[
Expanded(
child: MessageListView(),
),
MessageInput(),
],
),
);
}
@@ -10,30 +10,39 @@ void main() async {
await client.connectUser(
User(id: 'super-band-9'),
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A',
'''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''',
);
runApp(MyApp(client));
runApp(
MyApp(
client: client,
),
);
}
class MyApp extends StatelessWidget {
const MyApp({
Key? key,
required this.client,
}) : super(key: key);
final StreamChatClient client;
MyApp(this.client);
@override
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, child) => StreamChat(
client: client,
child: child,
),
home: SplitView(),
);
}
Widget build(BuildContext context) => MaterialApp(
builder: (context, child) => StreamChat(
client: client,
child: child,
),
home: const SplitView(),
);
}
class SplitView extends StatefulWidget {
const SplitView({
Key? key,
}) : super(key: key);
@override
_SplitViewState createState() => _SplitViewState();
}
@@ -42,69 +51,67 @@ class _SplitViewState extends State<SplitView> {
Channel? selectedChannel;
@override
Widget build(BuildContext context) {
return Flex(
direction: Axis.horizontal,
children: <Widget>[
Flexible(
flex: 1,
child: ChannelListPage(
onTap: (channel) {
setState(() {
selectedChannel = channel;
});
},
Widget build(BuildContext context) => Flex(
direction: Axis.horizontal,
children: <Widget>[
Flexible(
child: ChannelListPage(
onTap: (channel) {
setState(() {
selectedChannel = channel;
});
},
),
),
),
Flexible(
flex: 2,
child: Scaffold(
body: selectedChannel != null
? StreamChannel(
key: ValueKey(selectedChannel!.cid),
channel: selectedChannel!,
child: ChannelPage(),
)
: Center(
child: Text(
'Pick a channel to show the messages 💬',
style: Theme.of(context).textTheme.headline5,
Flexible(
flex: 2,
child: Scaffold(
body: selectedChannel != null
? StreamChannel(
key: ValueKey(selectedChannel!.cid),
channel: selectedChannel!,
child: const ChannelPage(),
)
: Center(
child: Text(
'Pick a channel to show the messages 💬',
style: Theme.of(context).textTheme.headline5,
),
),
),
),
),
),
],
);
}
],
);
}
class ChannelListPage extends StatelessWidget {
const ChannelListPage({
Key? key,
this.onTap,
}) : super(key: key);
final void Function(Channel)? onTap;
ChannelListPage({this.onTap});
@override
Widget build(BuildContext context) {
return Scaffold(
body: ChannelsBloc(
child: ChannelListView(
onChannelTap: onTap != null
? (channel, _) {
onTap!(channel);
}
: null,
filter: Filter.in_(
'members',
[StreamChat.of(context).user!.id],
),
sort: [SortOption('last_message_at')],
pagination: PaginationParams(
limit: 20,
Widget build(BuildContext context) => Scaffold(
body: ChannelsBloc(
child: ChannelListView(
onChannelTap: onTap != null
? (channel, _) {
onTap!(channel);
}
: null,
filter: Filter.in_(
'members',
[StreamChat.of(context).user!.id],
),
sort: const [SortOption('last_message_at')],
pagination: const PaginationParams(
limit: 20,
),
),
),
),
);
}
);
}
class ChannelPage extends StatelessWidget {
@@ -113,27 +120,21 @@ class ChannelPage extends StatelessWidget {
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Navigator(
onGenerateRoute: (settings) {
return MaterialPageRoute(
builder: (context) {
return Scaffold(
appBar: ChannelHeader(
showBackButton: false,
),
body: Column(
children: <Widget>[
Expanded(
child: MessageListView(),
),
MessageInput(),
],
),
);
},
);
},
);
}
Widget build(BuildContext context) => Navigator(
onGenerateRoute: (settings) => MaterialPageRoute(
builder: (context) => Scaffold(
appBar: const ChannelHeader(
showBackButton: false,
),
body: Column(
children: const <Widget>[
Expanded(
child: MessageListView(),
),
MessageInput(),
],
),
),
),
);
}
@@ -4,25 +4,30 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// First step of the [tutorial](https://getstream.io/chat/flutter/tutorial/)
///
/// There are three important things to notice that are common to all Flutter application using StreamChat:
/// There are three important things to notice that are common to all Flutter
/// application using StreamChat:
///
/// 1. The Dart API [StreamChatClient] is initialized with your API Key
/// 2. The current user is set by calling [StreamChatClient.connectUser]
/// 3. The client is then passed to the top-level [StreamChat] widget
/// [StreamChat] is an inherited widget and must be the parent of all Chat related widgets.
/// [StreamChat] is an inherited widget and must be the parent of all
/// Chat related widgets.
///
/// Please note that while Flutter can be used to build both mobile and web applications;
/// in this tutorial we focus on mobile, make sure when running the app you use a mobile device.
/// Please note that while Flutter can be used to build both mobile and web
/// applications, in this tutorial we focus on mobile. Make sure when running
/// the app that you use a mobile device.
///
/// Let's have a look at what we've built:
///
/// - We set up the Chat [StreamChatClient] with the API key
///
/// - We set the the current user for Chat with [StreamChatClient.connectUser] and a pre-generated user token
/// - We set the the current user for Chat with [StreamChatClient.connectUser]
/// and a pre-generated user token
///
/// - We make [StreamChat] the root Widget of our application
///
/// - We create a single [ChannelPage] widget under [StreamChat] with three widgets: [ChannelHeader], [MessageListView] and [MessageInput]
/// - We create a single [ChannelPage] widget under [StreamChat] with three
/// widgets: [ChannelHeader], [MessageListView] and [MessageInput]
///
/// If you now run the simulator you will see a single channel UI.
void main() async {
@@ -33,26 +38,38 @@ void main() async {
await client.connectUser(
User(id: 'super-band-9'),
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A',
'''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''',
);
final channel = client.channel('messaging', id: 'godevs');
// ignore: unawaited_futures
// ignore: unawaited_futures, cascade_invocations
channel.watch();
runApp(MyApp(client, channel));
runApp(
MyApp(
client: client,
channel: channel,
),
);
}
class MyApp extends StatelessWidget {
const MyApp({
Key? key,
required this.client,
required this.channel,
}) : super(key: key);
final StreamChatClient client;
final Channel channel;
MyApp(this.client, this.channel);
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return MaterialApp(
// ignore: prefer_expression_function_bodies
builder: (context, widget) {
return StreamChat(
client: client,
@@ -61,7 +78,7 @@ class MyApp extends StatelessWidget {
},
home: StreamChannel(
channel: channel,
child: ChannelPage(),
child: const ChannelPage(),
),
);
}
@@ -73,11 +90,12 @@ class ChannelPage extends StatelessWidget {
}) : super(key: key);
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return Scaffold(
appBar: ChannelHeader(),
appBar: const ChannelHeader(),
body: Column(
children: <Widget>[
children: const <Widget>[
Expanded(
child: MessageListView(),
),
@@ -5,20 +5,29 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// Second step of the [tutorial](https://getstream.io/chat/flutter/tutorial/)
///
/// Most chat applications handle more than just one single conversation.
/// Apps like Facebook Messenger, Whatsapp and Telegram allows you to have multiple one to one and group conversations.
/// Apps like Facebook Messenger, Whatsapp and Telegram allows you to have
/// multiple one-to-one and group conversations.
///
/// Lets find out how we can change our application chat screen to display the list of conversations and navigate between them.
/// Lets find out how we can change our application chat screen to display
/// the list of conversations and navigate between them.
///
/// > Note: the SDK uses Flutters [Navigator] to move from one route to another, this allows us to avoid any boiler-plate code.
/// > Of course you can take total control of how navigation works by customizing widgets like [Channel] and [ChannelList].
/// > Note: the SDK uses Flutters [Navigator] to move from one route to
/// another. This allows us to avoid any boiler-plate code.
/// > Of course, you can take total control of how navigation works by
/// customizing widgets like [Channel] and [ChannelList].
///
/// If you run the application, you will see that the first screen shows a list of conversations, you can open each by tapping and go back to the list.
/// If you run the application, you will see that the first screen shows a
/// list of conversations, you can open each by tapping and go back to the list.
///
/// Every single widget involved in this UI can be customized or swapped with your own.
/// Every single widget involved in this UI can be customized or swapped
/// with your own.
///
/// The [ChannelListPage] widget retrieves the list of channels based on a custom query and ordering.
/// In this case we are showing the list of channels the current user is a member and we order them based on the time they had a new message.
/// [ChannelListView] handles pagination and updates automatically out of the box when new channels are created or when a new message is added to a channel.
/// The [ChannelListPage] widget retrieves the list of channels based on a
/// custom query and ordering. In this case we are showing the list of
/// channels in which the current user is a member and we order them based
/// on the time they had a new message. [ChannelListView] handles pagination
/// and updates automatically when new channels are created or when a new
/// message is added to a channel.
void main() async {
final client = StreamChatClient(
's2dxdhpxd94g',
@@ -27,31 +36,44 @@ void main() async {
await client.connectUser(
User(id: 'super-band-9'),
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A',
'''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''',
);
runApp(MyApp(client));
runApp(
MyApp(
client: client,
),
);
}
class MyApp extends StatelessWidget {
const MyApp({
Key? key,
required this.client,
}) : super(key: key);
final StreamChatClient client;
MyApp(this.client);
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, child) => StreamChat(
client: client,
child: child,
),
home: ChannelListPage(),
home: const ChannelListPage(),
);
}
}
class ChannelListPage extends StatelessWidget {
const ChannelListPage({
Key? key,
}) : super(key: key);
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return Scaffold(
body: ChannelsBloc(
@@ -60,11 +82,11 @@ class ChannelListPage extends StatelessWidget {
'members',
[StreamChat.of(context).user!.id],
),
sort: [SortOption('last_message_at')],
pagination: PaginationParams(
sort: const [SortOption('last_message_at')],
pagination: const PaginationParams(
limit: 20,
),
channelWidget: ChannelPage(),
channelWidget: const ChannelPage(),
),
),
);
@@ -77,11 +99,12 @@ class ChannelPage extends StatelessWidget {
}) : super(key: key);
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return Scaffold(
appBar: ChannelHeader(),
appBar: const ChannelHeader(),
body: Column(
children: <Widget>[
children: const <Widget>[
Expanded(
child: MessageListView(),
),
@@ -6,22 +6,30 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// Third step of the [tutorial](https://getstream.io/chat/flutter/tutorial/)
///
/// So far youve learned how to use the default widgets.
/// The library has been designed with composition in mind and to allow all common customizations to be very easy.
/// This means that you can change any component in your application by swapping the default widgets with the ones you build yourself.
/// The library has been designed with composition in mind and to allow all
/// common customizations to be very easy.
/// This means that you can change any component in your application by
/// swapping the default widgets with the ones you build yourself.
///
/// Lets see how we can make some changes to the SDKs UI components.
/// We start by changing how channel previews are shown in the channel list and include the number of unread messages for each.
/// We start by changing how channel previews are shown in the channel list
/// and include the number of unread messages for each.
///
/// We're passing a custom widget to [ChannelListView.channelPreviewBuilder], this will override the default [ChannelPreview] and allows you to create one yourself.
/// We're passing a custom widget to [ChannelListView.channelPreviewBuilder];
/// this will override the default [ChannelPreview] and allows you to create
/// one yourself.
///
/// There are a couple interesting things we do in this widget:
///
/// - Instead of creating a whole new style for the channel name, we inherit the text style from the parent theme ([StreamChatTheme.of]) and only change the color attribute
/// - Instead of creating a whole new style for the channel name, we inherit
/// the text style from the parent theme ([StreamChatTheme.of]) and only
/// change the color attribute
///
/// - We loop over the list of channel messages to search for the first not deleted message ([Channel.state.messages])
/// - We loop over the list of channel messages to search for the first not
/// deleted message ([Channel.state.messages])
///
/// - We retrieve the count of unread messages from [Channel.state]
void main() async {
Future<void> main() async {
final client = StreamChatClient(
's2dxdhpxd94g',
logLevel: Level.INFO,
@@ -29,31 +37,44 @@ void main() async {
await client.connectUser(
User(id: 'super-band-9'),
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A',
'''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''',
);
runApp(MyApp(client));
runApp(
MyApp(
client: client,
),
);
}
class MyApp extends StatelessWidget {
const MyApp({
Key? key,
required this.client,
}) : super(key: key);
final StreamChatClient client;
MyApp(this.client);
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, child) => StreamChat(
client: client,
child: child,
),
home: ChannelListPage(),
home: const ChannelListPage(),
);
}
}
class ChannelListPage extends StatelessWidget {
const ChannelListPage({
Key? key,
}) : super(key: key);
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return Scaffold(
body: ChannelsBloc(
@@ -64,10 +85,10 @@ class ChannelListPage extends StatelessWidget {
),
channelPreviewBuilder: _channelPreviewBuilder,
// sort: [SortOption('last_message_at')],
pagination: PaginationParams(
pagination: const PaginationParams(
limit: 20,
),
channelWidget: ChannelPage(),
channelWidget: const ChannelPage(),
),
),
);
@@ -78,7 +99,7 @@ class ChannelListPage extends StatelessWidget {
(message) => !message.isDeleted,
);
final subtitle = (lastMessage == null ? 'nothing yet' : lastMessage.text!);
final subtitle = lastMessage == null ? 'nothing yet' : lastMessage.text!;
final opacity = (channel.state?.unreadCount ?? 0) > 0 ? 1.0 : 0.5;
return ListTile(
@@ -88,7 +109,7 @@ class ChannelListPage extends StatelessWidget {
MaterialPageRoute(
builder: (_) => StreamChannel(
channel: channel,
child: ChannelPage(),
child: const ChannelPage(),
),
),
);
@@ -111,7 +132,7 @@ class ChannelListPage extends StatelessWidget {
radius: 10,
child: Text(channel.state!.unreadCount.toString()),
)
: SizedBox(),
: const SizedBox(),
);
}
}
@@ -122,11 +143,12 @@ class ChannelPage extends StatelessWidget {
}) : super(key: key);
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return Scaffold(
appBar: ChannelHeader(),
appBar: const ChannelHeader(),
body: Column(
children: <Widget>[
children: const <Widget>[
Expanded(
child: MessageListView(),
),
@@ -4,13 +4,17 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// Fourth step of the [tutorial](https://getstream.io/chat/flutter/tutorial/)
///
/// Stream Chat supports message threads out of the box. Threads allows users to create sub-conversations inside the same channel.
/// Stream Chat supports message threads out of the box. Threads allows users
/// to create sub-conversations inside the same channel.
///
/// Using threaded conversations is very simple and mostly a matter of plugging the [MessageListView] to another widget that renders the widget.
/// To make this simple, such a widget only needs to build [MessageListView] with the parent attribute set to the threads root message.
/// Using threaded conversations is very simple and mostly a matter of
/// plugging the [MessageListView] to another widget that renders the widget.
/// To make this simple, such a widget only needs to build [MessageListView]
/// with the parent attribute set to the threads root message.
///
/// Now we can open threads and create new ones as well, if you long press a message you can tap on Reply and it will open the same [ThreadPage].
void main() async {
/// Now we can open threads and create new ones as well. If you long-press a
/// message, you can tap on "Reply" and it will open the same [ThreadPage].
Future<void> main() async {
final client = StreamChatClient(
's2dxdhpxd94g',
logLevel: Level.INFO,
@@ -18,33 +22,44 @@ void main() async {
await client.connectUser(
User(id: 'super-band-9'),
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A',
'''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''',
);
runApp(MyApp(client));
runApp(
MyApp(
client: client,
),
);
}
class MyApp extends StatelessWidget {
const MyApp({
Key? key,
required this.client,
}) : super(key: key);
final StreamChatClient client;
MyApp(this.client);
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, child) => StreamChat(
client: client,
child: child,
),
home: Container(
child: ChannelListPage(),
),
home: const ChannelListPage(),
);
}
}
class ChannelListPage extends StatelessWidget {
const ChannelListPage({
Key? key,
}) : super(key: key);
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return Scaffold(
body: ChannelsBloc(
@@ -53,11 +68,11 @@ class ChannelListPage extends StatelessWidget {
'members',
[StreamChat.of(context).user!.id],
),
sort: [SortOption('last_message_at')],
pagination: PaginationParams(
sort: const [SortOption('last_message_at')],
pagination: const PaginationParams(
limit: 20,
),
channelWidget: ChannelPage(),
channelWidget: const ChannelPage(),
),
),
);
@@ -70,21 +85,20 @@ class ChannelPage extends StatelessWidget {
}) : super(key: key);
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return Scaffold(
appBar: ChannelHeader(),
appBar: const ChannelHeader(),
body: Column(
children: <Widget>[
Expanded(
child: MessageListView(
threadBuilder: (_, parentMessage) {
return ThreadPage(
parent: parentMessage,
);
},
threadBuilder: (_, parentMessage) => ThreadPage(
parent: parentMessage,
),
),
),
MessageInput(),
const MessageInput(),
],
),
);
@@ -92,14 +106,15 @@ class ChannelPage extends StatelessWidget {
}
class ThreadPage extends StatelessWidget {
final Message? parent;
ThreadPage({
const ThreadPage({
Key? key,
this.parent,
}) : super(key: key);
final Message? parent;
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return Scaffold(
appBar: ThreadHeader(
@@ -4,18 +4,23 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// Fifth step of the [tutorial](https://getstream.io/chat/flutter/tutorial/)
///
/// Customizing how messages are rendered is another very common use-case that the SDK supports easily.
/// Customizing how messages are rendered is another very common use-case that
/// the SDK supports easily.
///
/// Replace the built-in message component with your own is done by passing it as a builder function to the [MessageListView] widget.
/// Replacing the built-in message component with your own is done by passing
/// it as a builder function to the [MessageListView] widget.
///
/// The message builder function will get the usual [BuildContext] argument as well as the [Message] object and its position inside the list.
/// The message builder function will get the usual [BuildContext] argument
/// as well as the [Message] object and its position inside the list.
///
/// If you look at the code you can see that we use [StreamChat.of] to retrieve the current user so that we can style messages own messages in a different way.
/// If you look at the code you can see that we use [StreamChat.of] to
/// retrieve the current user so that we can style messages in a different way.
///
/// Since custom widgets and builders are always children of [StreamChat] or part of a [Channel],
/// you can use [StreamChat.of], [StreamChannel.of] and [StreamChatTheme.of] to use the API client directly
/// or to retrieve outer scope needed such as messages from the [Channel.state].
void main() async {
/// Since custom widgets and builders are always children of [StreamChat] or
/// part of a [Channel], you can use [StreamChat.of], [StreamChannel.of],
/// and [StreamChatTheme.of] to use the API client directly or to retrieve
/// outer scope needed such as messages from the [Channel.state].
Future<void> main() async {
final client = StreamChatClient(
's2dxdhpxd94g',
logLevel: Level.INFO,
@@ -23,31 +28,44 @@ void main() async {
await client.connectUser(
User(id: 'super-band-9'),
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A',
'''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''',
);
runApp(MyApp(client));
runApp(
MyApp(
client: client,
),
);
}
class MyApp extends StatelessWidget {
const MyApp({
Key? key,
required this.client,
}) : super(key: key);
final StreamChatClient client;
MyApp(this.client);
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return MaterialApp(
builder: (context, child) => StreamChat(
client: client,
child: child,
),
home: ChannelListPage(),
home: const ChannelListPage(),
);
}
}
class ChannelListPage extends StatelessWidget {
const ChannelListPage({
Key? key,
}) : super(key: key);
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return Scaffold(
body: ChannelsBloc(
@@ -56,11 +74,11 @@ class ChannelListPage extends StatelessWidget {
'members',
[StreamChat.of(context).user!.id],
),
sort: [SortOption('last_message_at')],
pagination: PaginationParams(
sort: const [SortOption('last_message_at')],
pagination: const PaginationParams(
limit: 20,
),
channelWidget: ChannelPage(),
channelWidget: const ChannelPage(),
),
),
);
@@ -73,9 +91,10 @@ class ChannelPage extends StatelessWidget {
}) : super(key: key);
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return Scaffold(
appBar: ChannelHeader(),
appBar: const ChannelHeader(),
body: Column(
children: <Widget>[
Expanded(
@@ -83,7 +102,7 @@ class ChannelPage extends StatelessWidget {
messageBuilder: _messageBuilder,
),
),
MessageInput(),
const MessageInput(),
],
),
);
@@ -101,12 +120,14 @@ class ChannelPage extends StatelessWidget {
final color = isCurrentUser ? Colors.blueGrey : Colors.blue;
return Padding(
padding: EdgeInsets.all(5.0),
padding: const EdgeInsets.all(5),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: color, width: 1),
borderRadius: BorderRadius.all(
Radius.circular(5.0),
border: Border.all(
color: color,
),
borderRadius: const BorderRadius.all(
Radius.circular(5),
),
),
child: ListTile(
@@ -115,7 +136,7 @@ class ChannelPage extends StatelessWidget {
textAlign: textAlign,
),
subtitle: Text(
message.user!.extraData['name'] as String,
message.user!.name,
textAlign: textAlign,
),
),
@@ -4,22 +4,29 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// Sixth step of the [tutorial](https://getstream.io/chat/flutter/tutorial/)
///
/// The Flutter SDK comes with a fully designed set of widgets which you can customize to fit with your application style and typography.
/// Changing the theme of Chat widgets works in a very similar way that [MaterialApp] and [Theme] do.
/// The Flutter SDK comes with a fully designed set of widgets which you can
/// customize to fit with your application style and typography.
/// Changing the theme of Chat widgets works in a very similar way that
/// [MaterialApp] and [Theme] do.
///
/// Out of the box all chat widgets use their own default styling, there are two ways to change the styling:
/// All chat widgets use their own default styling out of the box. There are
/// two ways to change the styling:
///
/// 1. Initialize the [StreamChatTheme] from your existing [MaterialApp] style
/// 2. Construct a custom theme and provide all the customizations needed
///
/// First we create a new Material [Theme] and pick [Colors.green] as swatch color. The theme is then passed to [MaterialApp] as usual.
/// First, we create a new Material [Theme] and pick [Colors.green] as the
/// swatch color. The theme is then passed to [MaterialApp] as usual.
///
/// Then we create a new [StreamChatTheme] from the green theme we just created.
/// After saving the app you will see the UI will update several widgets to match with the new color.
/// Then, we create a new [StreamChatTheme] from the green theme we just
/// created. After saving the app you will see that several widgets have
/// been updated with the new color.
///
/// We also change the message color posted by the current user.
/// You can perform these more granular style changes using [StreamChatTheme.copyWith].
void main() async {
///
/// You can perform these more granular style changes using
/// [StreamChatTheme.copyWith].
Future<void> main() async {
final client = StreamChatClient(
's2dxdhpxd94g',
logLevel: Level.INFO,
@@ -27,16 +34,23 @@ void main() async {
await client.connectUser(
User(id: 'super-band-9'),
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A',
'''eyJ0eXAiO«iJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoic3VwZXItYmFuZC05In0.0L6lGoeLwkz0aZRUcpZKsvaXtNEDHBcezVTZ0oPq40A''',
);
runApp(MyApp(client));
runApp(
MyApp(
client: client,
),
);
}
class MyApp extends StatelessWidget {
final StreamChatClient client;
const MyApp({
Key? key,
required this.client,
}) : super(key: key);
MyApp(this.client);
final StreamChatClient client;
@override
Widget build(BuildContext context) {
@@ -62,20 +76,23 @@ class MyApp extends StatelessWidget {
return MaterialApp(
theme: themeData,
builder: (context, child) {
return StreamChat(
client: client,
streamChatThemeData: customTheme,
child: child,
);
},
home: ChannelListPage(),
builder: (context, child) => StreamChat(
client: client,
streamChatThemeData: customTheme,
child: child,
),
home: const ChannelListPage(),
);
}
}
class ChannelListPage extends StatelessWidget {
const ChannelListPage({
Key? key,
}) : super(key: key);
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return Scaffold(
body: ChannelsBloc(
@@ -84,11 +101,11 @@ class ChannelListPage extends StatelessWidget {
'members',
[StreamChat.of(context).user!.id],
),
sort: [SortOption('last_message_at')],
pagination: PaginationParams(
sort: const [SortOption('last_message_at')],
pagination: const PaginationParams(
limit: 20,
),
channelWidget: ChannelPage(),
channelWidget: const ChannelPage(),
),
),
);
@@ -101,21 +118,20 @@ class ChannelPage extends StatelessWidget {
}) : super(key: key);
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return Scaffold(
appBar: ChannelHeader(),
appBar: const ChannelHeader(),
body: Column(
children: <Widget>[
Expanded(
child: MessageListView(
threadBuilder: (_, parentMessage) {
return ThreadPage(
parent: parentMessage,
);
},
threadBuilder: (_, parentMessage) => ThreadPage(
parent: parentMessage,
),
),
),
MessageInput(),
const MessageInput(),
],
),
);
@@ -123,14 +139,15 @@ class ChannelPage extends StatelessWidget {
}
class ThreadPage extends StatelessWidget {
final Message? parent;
ThreadPage({
const ThreadPage({
Key? key,
this.parent,
}) : super(key: key);
final Message? parent;
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return Scaffold(
appBar: ThreadHeader(
@@ -21,21 +21,21 @@ environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
flutter:
sdk: flutter
# stream_chat:
# path: ../../stream_chat
# stream_chat_flutter_core:
# path: ../../stream_chat_flutter_core
stream_chat_flutter:
path: ../
stream_chat_persistence:
path: ../../stream_chat_persistence
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
collection: ^1.15.0
cupertino_icons: ^1.0.3
flutter:
sdk: flutter
# stream_chat:
# path: ../../stream_chat
# stream_chat_flutter_core:
# path: ../../stream_chat_flutter_core
stream_chat_flutter:
path: ../
stream_chat_persistence:
path: ../../stream_chat_persistence
dev_dependencies:
flutter_test:
@@ -70,6 +70,7 @@ void main() {
StreamChat(
streamChatThemeData: theme,
client: client,
connectivityStream: Stream.value(ConnectivityResult.mobile),
child: StreamChannel(
showLoading: false,
channel: channel,
@@ -79,7 +80,6 @@ void main() {
),
),
),
connectivityStream: Stream.value(ConnectivityResult.mobile),
),
),
surfaceSize: const Size.square(200),
@@ -123,6 +123,7 @@ void main() {
StreamChat(
streamChatThemeData: theme,
client: client,
connectivityStream: Stream.value(ConnectivityResult.mobile),
child: StreamChannel(
showLoading: false,
channel: channel,
@@ -132,7 +133,6 @@ void main() {
),
),
),
connectivityStream: Stream.value(ConnectivityResult.mobile),
),
),
surfaceSize: const Size.square(200),
@@ -176,6 +176,7 @@ void main() {
StreamChat(
streamChatThemeData: theme,
client: client,
connectivityStream: Stream.value(ConnectivityResult.mobile),
child: StreamChannel(
showLoading: false,
channel: channel,
@@ -189,7 +190,6 @@ void main() {
),
),
),
connectivityStream: Stream.value(ConnectivityResult.mobile),
),
),
surfaceSize: const Size.square(200),
@@ -54,12 +54,12 @@ void main() {
)
]));
when(() => channelState.typingEvents).thenAnswer((i) => {
User(id: 'other-user', extraData: {'name': 'demo'}):
User(id: 'other-user', extraData: const {'name': 'demo'}):
Event(type: EventType.typingStart),
});
when(() => channelState.typingEventsStream)
.thenAnswer((i) => Stream.value({
User(id: 'other-user', extraData: {'name': 'demo'}):
User(id: 'other-user', extraData: const {'name': 'demo'}):
Event(type: EventType.typingStart),
}));
@@ -14,7 +14,7 @@ void main() {
});
test(
'Light GalleryFooterThemeData lerps completely to dark GalleryFooterThemeData',
'''Light GalleryFooterThemeData lerps completely to dark GalleryFooterThemeData''',
() {
expect(
const GalleryFooterThemeData().lerp(_galleryFooterThemeDataControl,
@@ -23,7 +23,7 @@ void main() {
});
test(
'Light GalleryFooterThemeData lerps halfway to dark GalleryFooterThemeData',
'''Light GalleryFooterThemeData lerps halfway to dark GalleryFooterThemeData''',
() {
expect(
const GalleryFooterThemeData().lerp(_galleryFooterThemeDataControl,
@@ -32,7 +32,7 @@ void main() {
});
test(
'Dark GalleryFooterThemeData lerps completely to light GalleryFooterThemeData',
'''Dark GalleryFooterThemeData lerps completely to light GalleryFooterThemeData''',
() {
expect(
const GalleryFooterThemeData().lerp(_galleryFooterThemeDataControlDark,
@@ -14,7 +14,7 @@ void main() {
});
test(
'Light GalleryHeaderThemeData lerps completely to dark GalleryHeaderThemeData',
'''Light GalleryHeaderThemeData lerps completely to dark GalleryHeaderThemeData''',
() {
expect(
const GalleryHeaderThemeData().lerp(_galleryHeaderThemeDataControl,
@@ -23,7 +23,7 @@ void main() {
});
test(
'Light GalleryHeaderThemeData lerps halfway to dark GalleryHeaderThemeData',
'''Light GalleryHeaderThemeData lerps halfway to dark GalleryHeaderThemeData''',
() {
expect(
const GalleryHeaderThemeData().lerp(_galleryHeaderThemeDataControl,
@@ -32,7 +32,7 @@ void main() {
});
test(
'Dark GalleryHeaderThemeData lerps completely to light GalleryHeaderThemeData',
'''Dark GalleryHeaderThemeData lerps completely to light GalleryHeaderThemeData''',
() {
expect(
const GalleryHeaderThemeData().lerp(_galleryHeaderThemeDataDarkControl,
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

@@ -79,7 +79,8 @@ void main() {
'name': 'test',
});
final messageText = '''a message.
const messageText = '''
a message.
with multiple lines
and a list:
- a. okasd
@@ -47,6 +47,7 @@ void main() {
StreamChat(
client: client,
streamChatThemeData: theme,
connectivityStream: Stream.value(ConnectivityResult.mobile),
child: SizedBox(
child: ReactionBubble(
reactions: [
@@ -60,7 +61,6 @@ void main() {
maskColor: theme.ownMessageTheme.reactionsMaskColor!,
),
),
connectivityStream: Stream.value(ConnectivityResult.mobile),
),
surfaceSize: const Size(100, 100),
);
@@ -83,6 +83,7 @@ void main() {
StreamChat(
client: client,
streamChatThemeData: StreamChatThemeData.fromTheme(themeData),
connectivityStream: Stream.value(ConnectivityResult.mobile),
child: Container(
color: Colors.black,
child: ReactionBubble(
@@ -97,7 +98,6 @@ void main() {
maskColor: theme.ownMessageTheme.reactionsMaskColor!,
),
),
connectivityStream: Stream.value(ConnectivityResult.mobile),
),
surfaceSize: const Size(100, 100),
);
@@ -89,6 +89,7 @@ void main() {
)(
StreamChat(
client: client,
connectivityStream: Stream.value(ConnectivityResult.mobile),
child: StreamChannel(
showLoading: false,
channel: channel,
@@ -100,7 +101,6 @@ void main() {
),
),
),
connectivityStream: Stream.value(ConnectivityResult.mobile),
),
),
surfaceSize: const Size.square(200),
@@ -141,6 +141,7 @@ void main() {
)(
StreamChat(
client: client,
connectivityStream: Stream.value(ConnectivityResult.mobile),
child: StreamChannel(
showLoading: false,
channel: channel,
@@ -152,7 +153,6 @@ void main() {
),
),
),
connectivityStream: Stream.value(ConnectivityResult.mobile),
),
),
surfaceSize: const Size.square(200),
@@ -54,12 +54,12 @@ void main() {
]));
when(() => channelState.typingEvents).thenAnswer((i) => {
User(id: 'other-user', extraData: {'name': 'demo'}):
User(id: 'other-user', extraData: const {'name': 'demo'}):
Event(type: EventType.typingStart),
});
when(() => channelState.typingEventsStream)
.thenAnswer((i) => Stream.value({
User(id: 'other-user', extraData: {'name': 'demo'}):
User(id: 'other-user', extraData: const {'name': 'demo'}):
Event(type: EventType.typingStart),
}));
@@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
Future<void> main() async {
/// Create a new instance of [StreamChatClient] passing the apikey obtained from your
/// project dashboard.
/// Create a new instance of [StreamChatClient] passing the apikey obtained
/// from your project dashboard.
final client = StreamChatClient('b67pax5b2wdq');
/// Set the current user. In a production scenario, this should be done using
@@ -13,12 +13,13 @@ Future<void> main() async {
await client.connectUser(
User(
id: 'cool-shadow-7',
extraData: {
extraData: const {
'image':
'https://getstream.io/random_png/?id=cool-shadow-7&amp;name=Cool+shadow',
},
),
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo',
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9'
'.gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo',
);
runApp(
@@ -29,36 +30,36 @@ Future<void> main() async {
}
/// Example application using Stream Chat core widgets.
/// Stream Chat Core is a set of Flutter wrappers which provide basic functionality
/// for building Flutter applications using Stream.
/// Stream Chat Core is a set of Flutter wrappers which provide basic
/// functionality for building Flutter applications using Stream.
///
/// If you'd prefer using pre-made UI widgets for your app, please see our other
/// package, `stream_chat_flutter`.
class StreamExample extends StatelessWidget {
/// Minimal example using Stream's core Flutter package.
/// If you'd prefer using pre-made UI widgets for your app, please see our other
/// package, `stream_chat_flutter`.
///
/// If you'd prefer using pre-made UI widgets for your app, please see our
/// other package, `stream_chat_flutter`.
const StreamExample({
Key? key,
required this.client,
}) : super(key: key);
/// Instance of Stream Client.
/// Stream's [StreamChatClient] can be used to connect to our servers and set the default
/// user for the application. Performing these actions trigger a websocket connection
/// allowing for real-time updates.
/// Stream's [StreamChatClient] can be used to connect to our servers and
/// set the default user for the application. Performing these actions
/// trigger a websocket connection allowing for real-time updates.
final StreamChatClient client;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Stream Chat Core Example',
home: HomeScreen(),
builder: (context, child) => StreamChatCore(
client: client,
child: child!,
),
);
}
Widget build(BuildContext context) => MaterialApp(
title: 'Stream Chat Core Example',
home: HomeScreen(),
builder: (context, child) => StreamChatCore(
client: client,
child: child!,
),
);
}
/// Basic layout displaying a list of [Channel]s the user is a part of.
@@ -67,104 +68,108 @@ class StreamExample extends StatelessWidget {
/// [ChannelListCore] is a `builder` with callbacks for constructing UIs based
/// on different scenarios.
class HomeScreen extends StatelessWidget {
/// Builds a basic layout displaying a list of [Channel]s the user is a
/// part of.
HomeScreen({Key? key}) : super(key: key);
/// Controller used for loading more data and controlling pagination in
/// [ChannelListCore].
final channelListController = ChannelListController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Channels'),
),
body: ChannelsBloc(
child: ChannelListCore(
channelListController: channelListController,
filter: Filter.and([
Filter.equal('type', 'messaging'),
Filter.in_('members', [
StreamChatCore.of(context).user!.id,
])
]),
emptyBuilder: (BuildContext context) {
return Center(
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text('Channels'),
),
body: ChannelsBloc(
child: ChannelListCore(
channelListController: channelListController,
filter: Filter.and([
Filter.equal('type', 'messaging'),
Filter.in_('members', [
StreamChatCore.of(context).user!.id,
])
]),
emptyBuilder: (BuildContext context) => const Center(
child: Text('Looks like you are not in any channels'),
);
},
loadingBuilder: (BuildContext context) {
return Center(
),
loadingBuilder: (BuildContext context) => const Center(
child: SizedBox(
height: 100.0,
width: 100.0,
height: 100,
width: 100,
child: CircularProgressIndicator(),
),
);
},
errorBuilder: (
BuildContext context,
dynamic error,
) {
return Center(
),
errorBuilder: (
BuildContext context,
dynamic error,
) =>
Center(
child: Text(
'Oh no, something went wrong. Please check your config. ${error}'),
);
},
listBuilder: (
BuildContext context,
List<Channel> channels,
) =>
LazyLoadScrollView(
onEndOfPage: () async {
channelListController.paginateData!();
},
child: ListView.builder(
itemCount: channels.length,
itemBuilder: (BuildContext context, int index) {
final _item = channels[index];
return ListTile(
title: Text(_item.name!),
subtitle: StreamBuilder<Message?>(
stream: _item.state!.lastMessageStream,
initialData: _item.state!.lastMessage,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.text!);
}
return SizedBox();
},
),
onTap: () {
/// Display a list of messages when the user taps on an item.
/// We can use [StreamChannel] to wrap our [MessageScreen] screen
/// with the selected channel.
///
/// This allows us to use a built-in inherited widget for accessing
/// our `channel` later on.
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => StreamChannel(
channel: _item,
child: MessageScreen(),
),
),
);
},
);
'Oh no, something went wrong. '
'Please check your config. $error',
),
),
listBuilder: (
BuildContext context,
List<Channel> channels,
) =>
LazyLoadScrollView(
onEndOfPage: () async {
channelListController.paginateData!();
},
child: ListView.builder(
itemCount: channels.length,
itemBuilder: (BuildContext context, int index) {
final _item = channels[index];
return ListTile(
title: Text(_item.name!),
subtitle: StreamBuilder<Message?>(
stream: _item.state!.lastMessageStream,
initialData: _item.state!.lastMessage,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.text!);
}
return const SizedBox();
},
),
onTap: () {
/// Display a list of messages when the user taps on
/// an item. We can use [StreamChannel] to wrap our
/// [MessageScreen] screen with the selected channel.
///
/// This allows us to use a built-in inherited widget
/// for accessing our `channel` later on.
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => StreamChannel(
channel: _item,
child: const MessageScreen(),
),
),
);
},
);
},
),
),
),
),
),
);
}
);
}
/// A list of messages sent in the current channel.
/// When a user taps on a channel in [HomeScreen], a navigator push [MessageScreen]
/// to display the list of messages in the selected channel.
/// When a user taps on a channel in [HomeScreen], a navigator push
/// [MessageScreen] to display the list of messages in the selected channel.
///
/// This is implemented using [MessageListCore], a convenience builder with
/// callbacks for building UIs based on different api results.
class MessageScreen extends StatefulWidget {
/// Build a MessageScreen
const MessageScreen({Key? key}) : super(key: key);
@override
_MessageScreenState createState() => _MessageScreenState();
}
@@ -198,8 +203,8 @@ class _MessageScreenState extends State<MessageScreen> {
@override
Widget build(BuildContext context) {
/// To access the current channel, we can use the `.of()` method on [StreamChannel]
/// to fetch the closest instance.
/// To access the current channel, we can use the `.of()` method on
/// [StreamChannel] to fetch the closest instance.
final channel = StreamChannel.of(context).channel;
return Scaffold(
appBar: AppBar(
@@ -210,7 +215,7 @@ class _MessageScreenState extends State<MessageScreen> {
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
return Text('${snapshot.data!.first.name} is typing...');
}
return SizedBox();
return const SizedBox();
},
),
),
@@ -224,57 +229,52 @@ class _MessageScreenState extends State<MessageScreen> {
},
child: MessageListCore(
messageListController: messageListController,
emptyBuilder: (BuildContext context) {
return Center(
child: Text('Nothing here yet'),
);
},
loadingBuilder: (BuildContext context) {
return Center(
child: SizedBox(
height: 100.0,
width: 100.0,
child: CircularProgressIndicator(),
),
);
},
emptyBuilder: (BuildContext context) => const Center(
child: Text('Nothing here yet'),
),
loadingBuilder: (BuildContext context) => const Center(
child: SizedBox(
height: 100,
width: 100,
child: CircularProgressIndicator(),
),
),
messageListBuilder: (
BuildContext context,
List<Message> messages,
) {
return ListView.builder(
controller: _scrollController,
itemCount: messages.length,
reverse: true,
itemBuilder: (BuildContext context, int index) {
final item = messages[index];
final client = StreamChatCore.of(context).client;
if (item.user!.id == client.uid) {
return Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(item.text!),
),
);
} else {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(item.text!),
),
);
}
},
);
},
) =>
ListView.builder(
controller: _scrollController,
itemCount: messages.length,
reverse: true,
itemBuilder: (BuildContext context, int index) {
final item = messages[index];
final client = StreamChatCore.of(context).client;
if (item.user!.id == client.uid) {
return Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.all(8),
child: Text(item.text!),
),
);
} else {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8),
child: Text(item.text!),
),
);
}
},
),
errorBuilder: (BuildContext context, error) {
print(error.toString());
return Center(
return const Center(
child: SizedBox(
height: 100.0,
width: 100.0,
height: 100,
width: 100,
child:
Text('Oh no, an error occured. Please see logs.'),
),
@@ -21,14 +21,14 @@ environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.3
flutter:
sdk: flutter
stream_chat_flutter_core:
path: ../
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.0
dev_dependencies:
flutter_test:
@@ -9,26 +9,25 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
import 'mocks.dart';
void main() {
const pagination = PaginationParams(offset: 0, limit: 3);
const pagination = PaginationParams(limit: 3);
List<Channel> _generateChannels(
StreamChatClient client, {
int count = 3,
int offset = 0,
}) {
return List.generate(
count,
(index) {
index = index + offset;
return Channel(
client,
'testType$index',
'testId$index',
extraData: {'extra_data_key': 'extra_data_value_$index'},
);
},
);
}
}) =>
List.generate(
count,
(index) {
index = index + offset;
return Channel(
client,
'testType$index',
'testId$index',
extraData: {'extra_data_key': 'extra_data_value_$index'},
);
},
);
testWidgets(
'should throw if ChannelListCore is used where ChannelsBloc is not present '
@@ -37,10 +36,10 @@ void main() {
const channelListCoreKey = Key('channelListCore');
final channelListCore = ChannelListCore(
key: channelListCoreKey,
listBuilder: (_, __) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
listBuilder: (_, __) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
);
await tester.pumpWidget(channelListCore);
@@ -56,16 +55,16 @@ void main() {
const channelListCoreKey = Key('channelListCore');
final channelListCore = ChannelListCore(
key: channelListCoreKey,
listBuilder: (_, __) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
listBuilder: (_, __) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
);
final mockClient = MockClient();
when(() => mockClient.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.empty());
.thenAnswer((_) => const Stream.empty());
await tester.pumpWidget(
StreamChatCore(
@@ -88,10 +87,10 @@ void main() {
final controller = ChannelListController();
final channelListCore = ChannelListCore(
key: channelListCoreKey,
listBuilder: (_, __) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
listBuilder: (_, __) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
channelListController: controller,
);
@@ -101,7 +100,7 @@ void main() {
final mockClient = MockClient();
when(() => mockClient.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.empty());
.thenAnswer((_) => const Stream.empty());
await tester.pumpWidget(
StreamChatCore(
@@ -125,9 +124,9 @@ void main() {
const errorWidgetKey = Key('errorWidget');
final channelListCore = ChannelListCore(
key: channelListCoreKey,
listBuilder: (_, __) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
listBuilder: (_, __) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) =>
Container(key: errorWidgetKey),
pagination: pagination,
@@ -136,7 +135,7 @@ void main() {
final mockClient = MockClient();
when(() => mockClient.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.empty());
.thenAnswer((_) => const Stream.empty());
const error = 'Error! Error! Error!';
when(() => mockClient.queryChannels(
@@ -177,23 +176,23 @@ void main() {
);
testWidgets(
'should build empty widget if channelsBlocState.channelsStream emits empty data',
'''should build empty widget if channelsBlocState.channelsStream emits empty data''',
(tester) async {
const channelListCoreKey = Key('channelListCore');
const emptyWidgetKey = Key('emptyWidget');
final channelListCore = ChannelListCore(
key: channelListCoreKey,
listBuilder: (_, __) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
listBuilder: (_, __) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => Container(key: emptyWidgetKey),
errorBuilder: (BuildContext context, Object error) => Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
pagination: pagination,
);
final mockClient = MockClient();
when(() => mockClient.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.empty());
.thenAnswer((_) => const Stream.empty());
const channels = <Channel>[];
when(() => mockClient.queryChannels(
@@ -234,23 +233,23 @@ void main() {
);
testWidgets(
'should build list widget if channelsBlocState.channelsStream emits some data',
'''should build list widget if channelsBlocState.channelsStream emits some data''',
(tester) async {
const channelListCoreKey = Key('channelListCore');
const listWidgetKey = Key('listWidget');
final channelListCore = ChannelListCore(
key: channelListCoreKey,
listBuilder: (_, __) => Container(key: listWidgetKey),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
pagination: pagination,
);
final mockClient = MockClient();
when(() => mockClient.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.empty());
.thenAnswer((_) => const Stream.empty());
final channels = _generateChannels(mockClient);
when(() => mockClient.queryChannels(
@@ -298,24 +297,22 @@ void main() {
const listWidgetKey = Key('listWidget');
final channelListCore = ChannelListCore(
key: channelListCoreKey,
listBuilder: (_, channels) {
return Container(
key: listWidgetKey,
child: Text(
channels.map((e) => e.cid).join(','),
),
);
},
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
listBuilder: (_, channels) => Container(
key: listWidgetKey,
child: Text(
channels.map((e) => e.cid).join(','),
),
),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
pagination: pagination,
);
final mockClient = MockClient();
when(() => mockClient.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.empty());
.thenAnswer((_) => const Stream.empty());
final channels = _generateChannels(mockClient);
when(() => mockClient.queryChannels(
@@ -413,24 +410,23 @@ void main() {
ChannelListCore channelListCoreBuilder(int limit) => ChannelListCore(
key: channelListCoreKey,
listBuilder: (_, channels) {
return Container(
key: listWidgetKey,
child: Text(
channels.map((e) => e.cid).join(','),
),
);
},
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
listBuilder: (_, channels) => Container(
key: listWidgetKey,
child: Text(
channels.map((e) => e.cid).join(','),
),
),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) =>
const Offstage(),
pagination: pagination.copyWith(limit: limit),
);
final mockClient = MockClient();
when(() => mockClient.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.empty());
.thenAnswer((_) => const Stream.empty());
final channels = _generateChannels(mockClient);
when(() => mockClient.queryChannels(
@@ -17,27 +17,26 @@ void main() {
StreamChatClient client, {
int count = 3,
int offset = 0,
}) {
return List.generate(
count,
(index) {
index = index + offset;
return Channel(
client,
'testType$index',
'testId$index',
extraData: {'extra_data_key': 'extra_data_value_$index'},
);
},
);
}
}) =>
List.generate(
count,
(index) {
index = index + offset;
return Channel(
client,
'testType$index',
'testId$index',
extraData: {'extra_data_key': 'extra_data_value_$index'},
);
},
);
testWidgets(
'should throw if ChannelsBloc is used where StreamChat is not present in the widget tree',
'''should throw if ChannelsBloc is used where StreamChat is not present in the widget tree''',
(tester) async {
const channelsBlocKey = Key('channelsBloc');
const childKey = Key('child');
final channelsBloc = ChannelsBloc(
const channelsBloc = ChannelsBloc(
key: channelsBlocKey,
child: Offstage(key: childKey),
);
@@ -55,7 +54,7 @@ void main() {
(tester) async {
const channelsBlocKey = Key('channelsBloc');
const childKey = Key('child');
final channelsBloc = ChannelsBloc(
const channelsBloc = ChannelsBloc(
key: channelsBlocKey,
child: Offstage(key: childKey),
);
@@ -63,7 +62,7 @@ void main() {
final mockClient = MockClient();
when(() => mockClient.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.empty());
.thenAnswer((_) => const Stream.empty());
await tester.pumpWidget(
StreamChatCore(
@@ -86,16 +85,14 @@ void main() {
key: channelsBlocKey,
child: Builder(
key: childKey,
builder: (context) {
return Offstage();
},
builder: (context) => const Offstage(),
),
);
final mockClient = MockClient();
when(() => mockClient.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.empty());
.thenAnswer((_) => const Stream.empty());
await tester.pumpWidget(
StreamChatCore(
@@ -157,16 +154,14 @@ void main() {
key: channelsBlocKey,
child: Builder(
key: childKey,
builder: (context) {
return Offstage();
},
builder: (context) => const Offstage(),
),
);
final mockClient = MockClient();
when(() => mockClient.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.empty());
.thenAnswer((_) => const Stream.empty());
await tester.pumpWidget(
StreamChatCore(
@@ -218,7 +213,7 @@ void main() {
'through queryChannelsLoading',
(tester) async {
const channelsBlocKey = Key('channelsBloc');
final channelsBloc = ChannelsBloc(
const channelsBloc = ChannelsBloc(
key: channelsBlocKey,
child: Offstage(),
);
@@ -226,7 +221,7 @@ void main() {
final mockClient = MockClient();
when(() => mockClient.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.empty());
.thenAnswer((_) => const Stream.empty());
await tester.pumpWidget(
StreamChatCore(
@@ -323,7 +318,7 @@ void main() {
'client.queryChannels() throws',
(tester) async {
const channelsBlocKey = Key('channelsBloc');
final channelsBloc = ChannelsBloc(
const channelsBloc = ChannelsBloc(
key: channelsBlocKey,
child: Offstage(),
);
@@ -331,7 +326,7 @@ void main() {
final mockClient = MockClient();
when(() => mockClient.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.empty());
.thenAnswer((_) => const Stream.empty());
await tester.pumpWidget(
StreamChatCore(
@@ -345,7 +340,7 @@ void main() {
);
final channels = _generateChannels(mockClient);
final paginationParams = const PaginationParams(
const paginationParams = PaginationParams(
limit: 3,
);
@@ -380,7 +375,7 @@ void main() {
paginationParams: paginationParams,
)).called(1);
final error = 'Error! Error! Error!';
const error = 'Error! Error! Error!';
when(() => mockClient.queryChannels(
filter: any(named: 'filter'),
@@ -424,13 +419,13 @@ void main() {
(tester) async {
final mockClient = MockClient();
const channelsBlocKey = Key('channelsBloc');
final channelsBloc = ChannelsBloc(
const channelsBloc = ChannelsBloc(
key: channelsBlocKey,
child: Offstage(),
);
when(() => mockClient.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.empty());
.thenAnswer((_) => const Stream.empty());
when(() => mockClient.on(
EventType.channelHidden,
@@ -503,13 +498,13 @@ void main() {
(tester) async {
final mockClient = MockClient();
const channelsBlocKey = Key('channelsBloc');
final channelsBloc = ChannelsBloc(
const channelsBloc = ChannelsBloc(
key: channelsBlocKey,
child: Offstage(),
);
when(() => mockClient.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.empty());
.thenAnswer((_) => const Stream.empty());
when(() => mockClient.on(
EventType.channelDeleted,
@@ -589,18 +584,18 @@ void main() {
);
testWidgets(
'event channel should be moved to top of the list if present when'
'event channel should be moved to top of the list if present when '
'EventType.messageNew event is received',
(tester) async {
final mockClient = MockClient();
const channelsBlocKey = Key('channelsBloc');
final channelsBloc = ChannelsBloc(
const channelsBloc = ChannelsBloc(
key: channelsBlocKey,
child: Offstage(),
);
when(() => mockClient.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.empty());
.thenAnswer((_) => const Stream.empty());
when(() => mockClient.on(
EventType.messageNew,
@@ -678,21 +673,19 @@ void main() {
(tester) async {
final hiddenChannelEventController = StreamController<Event>();
addTearDown(() {
hiddenChannelEventController.close();
});
addTearDown(hiddenChannelEventController.close);
final mockClient = MockClient();
final channels = _generateChannels(mockClient);
const channelsBlocKey = Key('channelsBloc');
final channelsBloc = ChannelsBloc(
key: channelsBlocKey,
child: Offstage(),
shouldAddChannel: (e) => channels.map((it) => it.cid).contains(e.cid),
child: const Offstage(),
);
when(() => mockClient.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.empty());
.thenAnswer((_) => const Stream.empty());
when(() => mockClient.on(
EventType.channelHidden,
@@ -789,14 +782,14 @@ void main() {
const channelsBlocKey = Key('channelsBloc');
final channelsBloc = ChannelsBloc(
key: channelsBlocKey,
child: Offstage(),
shouldAddChannel: (_) => true,
child: const Offstage(),
);
when(() => mockClient.state.channels).thenReturn(stateChannels);
when(() => mockClient.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.empty());
.thenAnswer((_) => const Stream.empty());
when(() => mockClient.on(
EventType.messageNew,
@@ -868,21 +861,21 @@ void main() {
final mockClient = MockClient();
final channels = _generateChannels(mockClient);
int channelComparator(Channel a, Channel b) {
final aData = a.extraData['extra_data_key'] as String;
final bData = b.extraData['extra_data_key'] as String;
final aData = a.extraData['extra_data_key'].toString();
final bData = b.extraData['extra_data_key'].toString();
return bData.compareTo(aData);
}
const channelsBlocKey = Key('channelsBloc');
final channelsBloc = ChannelsBloc(
key: channelsBlocKey,
child: Offstage(),
shouldAddChannel: (_) => true,
channelsComparator: channelComparator,
child: const Offstage(),
);
when(() => mockClient.on(any(), any(), any(), any()))
.thenAnswer((_) => Stream.empty());
.thenAnswer((_) => const Stream.empty());
when(() => mockClient.on(
EventType.messageNew,
@@ -7,7 +7,7 @@ void main() {
'should render LazyLoadScrollView if child is provided',
(tester) async {
const lazyLoadScrollViewKey = Key('lazyLoadScrollView');
final lazyLoadScrollView = LazyLoadScrollView(
const lazyLoadScrollView = LazyLoadScrollView(
key: lazyLoadScrollViewKey,
child: Offstage(),
);
@@ -23,7 +23,7 @@ void main() {
(tester) async {
const lazyLoadScrollViewKey = Key('lazyLoadScrollView');
const childKey = Key('childKey');
final lazyLoadScrollView = LazyLoadScrollView(
const lazyLoadScrollView = LazyLoadScrollView(
key: lazyLoadScrollViewKey,
child: Offstage(key: childKey),
);
@@ -41,7 +41,7 @@ void main() {
const lazyLoadScrollViewKey = Key('lazyLoadScrollView');
const childListViewKey = Key('childListView');
bool onPageScrollStartCalled = false;
var onPageScrollStartCalled = false;
final lazyLoadScrollView = LazyLoadScrollView(
key: lazyLoadScrollViewKey,
@@ -52,7 +52,7 @@ void main() {
key: childListViewKey,
children: List.generate(
12,
(index) => Container(
(index) => SizedBox(
height: 100,
child: Text('Item #$index'),
),
@@ -71,7 +71,7 @@ void main() {
expect(find.byKey(childListViewKey), findsOneWidget);
expect(onPageScrollStartCalled, isFalse);
await tester.startGesture(const Offset(100.0, 100.0));
await tester.startGesture(const Offset(100, 100));
await tester.pump(const Duration(seconds: 1));
expect(onPageScrollStartCalled, isTrue);
@@ -85,8 +85,8 @@ void main() {
const lazyLoadScrollViewKey = Key('lazyLoadScrollView');
const childListViewKey = Key('childListView');
bool onPageScrollStartCalled = false;
bool onPageScrollEndCalled = false;
var onPageScrollStartCalled = false;
var onPageScrollEndCalled = false;
final lazyLoadScrollView = LazyLoadScrollView(
key: lazyLoadScrollViewKey,
@@ -100,7 +100,7 @@ void main() {
key: childListViewKey,
children: List.generate(
12,
(index) => Container(
(index) => SizedBox(
height: 100,
child: Text('Item #$index'),
),
@@ -122,7 +122,7 @@ void main() {
final gesture = await tester.createGesture();
await gesture.down(const Offset(100.0, 100.0));
await gesture.down(const Offset(100, 100));
await tester.pump(const Duration(seconds: 1));
expect(onPageScrollStartCalled, isTrue);
@@ -141,7 +141,7 @@ void main() {
const lazyLoadScrollViewKey = Key('lazyLoadScrollView');
const childListViewKey = Key('childListView');
bool onInBetweenOfPageCalled = false;
var onInBetweenOfPageCalled = false;
final lazyLoadScrollView = LazyLoadScrollView(
key: lazyLoadScrollViewKey,
@@ -152,7 +152,7 @@ void main() {
key: childListViewKey,
children: List.generate(
12,
(index) => Container(
(index) => SizedBox(
height: 100,
child: Text('Item #$index'),
),
@@ -173,9 +173,9 @@ void main() {
final gesture = await tester.createGesture();
await gesture.down(const Offset(100.0, 100.0));
await gesture.down(const Offset(100, 100));
await tester.pump(const Duration(seconds: 1));
await gesture.moveBy(const Offset(-200.0, -200.0));
await gesture.moveBy(const Offset(-200, -200));
await tester.pump(const Duration(seconds: 1));
expect(onInBetweenOfPageCalled, isTrue);
@@ -189,7 +189,7 @@ void main() {
const lazyLoadScrollViewKey = Key('lazyLoadScrollView');
const childListViewKey = Key('childListView');
bool onStartOfPageCalled = false;
var onStartOfPageCalled = false;
final lazyLoadScrollView = LazyLoadScrollView(
key: lazyLoadScrollViewKey,
@@ -200,7 +200,7 @@ void main() {
key: childListViewKey,
children: List.generate(
12,
(index) => Container(
(index) => SizedBox(
height: 100,
child: Text('Item #$index'),
),
@@ -221,11 +221,11 @@ void main() {
final gesture = await tester.createGesture();
await gesture.down(const Offset(100.0, 100.0));
await gesture.down(const Offset(100, 100));
await tester.pump(const Duration(seconds: 1));
await gesture.moveBy(const Offset(-200.0, -200.0));
await gesture.moveBy(const Offset(-200, -200));
await tester.pump(const Duration(seconds: 1));
await gesture.moveBy(const Offset(201.0, 201.0));
await gesture.moveBy(const Offset(201, 201));
await tester.pump(const Duration(seconds: 1));
expect(onStartOfPageCalled, isTrue);
@@ -239,7 +239,7 @@ void main() {
const lazyLoadScrollViewKey = Key('lazyLoadScrollView');
const childListViewKey = Key('childListView');
bool onEndOfPageCalled = false;
var onEndOfPageCalled = false;
final lazyLoadScrollView = LazyLoadScrollView(
key: lazyLoadScrollViewKey,
@@ -250,7 +250,7 @@ void main() {
key: childListViewKey,
children: List.generate(
12,
(index) => Container(
(index) => SizedBox(
height: 100,
child: Text('Item #$index'),
),
@@ -271,9 +271,9 @@ void main() {
final gesture = await tester.createGesture();
await gesture.down(const Offset(100.0, 100.0));
await gesture.down(const Offset(100, 100));
await tester.pump(const Duration(seconds: 1));
await gesture.moveBy(const Offset(-601.0, -601.0));
await gesture.moveBy(const Offset(-601, -601));
await tester.pump(const Duration(seconds: 1));
expect(onEndOfPageCalled, isTrue);
@@ -1,4 +1,3 @@
import 'package:meta/meta.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
@@ -8,7 +7,7 @@ Matcher isSameChannelAs(Channel targetChannel) =>
class _IsSameChannelAs extends Matcher {
const _IsSameChannelAs({
required this.targetChannel,
}) : assert(targetChannel != null, '');
});
final Channel targetChannel;
@@ -27,13 +26,13 @@ Matcher isSameChannelListAs(List<Channel> targetChannelList) =>
class _IsSameChannelListAs extends Matcher {
const _IsSameChannelListAs({
required this.targetChannelList,
}) : assert(targetChannelList != null, '');
});
final List<Channel> targetChannelList;
@override
bool matches(covariant List<Channel> channelList, Map matchState) {
bool matches = true;
var matches = true;
for (var i = 0; i < channelList.length; i++) {
final channel = channelList[i];
final targetChannel = targetChannelList[i];
@@ -1,4 +1,3 @@
import 'package:meta/meta.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
@@ -8,7 +7,7 @@ Matcher isSameMessageAs(Message targetMessage) =>
class _IsSameMessageAs extends Matcher {
const _IsSameMessageAs({
required this.targetMessage,
}) : assert(targetMessage != null, '');
});
final Message targetMessage;
@@ -27,13 +26,13 @@ Matcher isSameMessageListAs(List<Message> targetMessageList) =>
class _IsSameMessageListAs extends Matcher {
const _IsSameMessageListAs({
required this.targetMessageList,
}) : assert(targetMessageList != null, '');
});
final List<Message> targetMessageList;
@override
bool matches(covariant List<Message> messageList, Map matchState) {
bool matches = true;
var matches = true;
for (var i = 0; i < messageList.length; i++) {
final message = messageList[i];
final targetMessage = targetMessageList[i];
@@ -1,4 +1,3 @@
import 'package:meta/meta.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
@@ -7,7 +6,7 @@ Matcher isSameUserAs(User targetUser) => _IsSameUserAs(targetUser: targetUser);
class _IsSameUserAs extends Matcher {
const _IsSameUserAs({
required this.targetUser,
}) : assert(targetUser != null, '');
});
final User targetUser;
@@ -25,13 +24,13 @@ Matcher isSameUserListAs(List<User> targetUserList) =>
class _IsSameUserListAs extends Matcher {
const _IsSameUserListAs({
required this.targetUserList,
}) : assert(targetUserList != null, '');
});
final List<User> targetUserList;
@override
bool matches(covariant List<User> userList, Map matchState) {
bool matches = true;
var matches = true;
for (var i = 0; i < userList.length; i++) {
final user = userList[i];
final targetUser = targetUserList[i];
@@ -27,10 +27,9 @@ void main() {
type: 'testType',
user: users[index],
createdAt: DateTime.now(),
shadowed: false,
replyCount: index,
updatedAt: DateTime.now(),
extraData: {'extra_test_field': 'extraTestData'},
extraData: const {'extra_test_field': 'extraTestData'},
text: 'Dummy text #$index',
pinned: true,
pinnedAt: DateTime.now(),
@@ -48,10 +47,9 @@ void main() {
user: users[index],
parentId: messages[0].id,
createdAt: DateTime.now(),
shadowed: false,
replyCount: index,
updatedAt: DateTime.now(),
extraData: {'extra_test_field': 'extraTestData'},
extraData: const {'extra_test_field': 'extraTestData'},
text: 'Dummy text #$index',
pinned: true,
pinnedAt: DateTime.now(),
@@ -63,16 +61,15 @@ void main() {
}
testWidgets(
'should throw if MessageListCore is used where StreamChannel is not present '
'in the widget tree',
'''should throw if MessageListCore is used where StreamChannel is not present in the widget tree''',
(tester) async {
const messageListCoreKey = Key('messageListCore');
final messageListCore = MessageListCore(
key: messageListCoreKey,
messageListBuilder: (_, __) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
messageListBuilder: (_, __) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
);
await tester.pumpWidget(messageListCore);
@@ -88,10 +85,10 @@ void main() {
const messageListCoreKey = Key('messageListCore');
final messageListCore = MessageListCore(
key: messageListCoreKey,
messageListBuilder: (_, __) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
messageListBuilder: (_, __) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
);
final mockChannel = MockChannel();
@@ -120,10 +117,10 @@ void main() {
final controller = MessageListController();
final messageListCore = MessageListCore(
key: messageListCoreKey,
messageListBuilder: (_, __) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
messageListBuilder: (_, __) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
messageListController: controller,
);
@@ -150,16 +147,16 @@ void main() {
);
testWidgets(
'should assign paginateData callback and paginate data correctly if a MessageListController is passed',
'''should assign paginateData callback and paginate data correctly if a MessageListController is passed''',
(tester) async {
const messageListCoreKey = Key('messageListCore');
final controller = MessageListController();
final messageListCore = MessageListCore(
key: messageListCoreKey,
messageListBuilder: (_, __) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
messageListBuilder: (_, __) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
messageListController: controller,
);
@@ -207,10 +204,10 @@ void main() {
const errorWidgetKey = Key('errorWidget');
final messageListCore = MessageListCore(
key: messageListCoreKey,
messageListBuilder: (_, __) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(
messageListBuilder: (_, __) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(
key: errorWidgetKey,
),
);
@@ -249,10 +246,11 @@ void main() {
const emptyWidgetKey = Key('emptyWidget');
final messageListCore = MessageListCore(
key: messageListCoreKey,
messageListBuilder: (_, __) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(key: emptyWidgetKey),
errorBuilder: (BuildContext context, Object error) => Offstage(),
messageListBuilder: (_, __) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) =>
const Offstage(key: emptyWidgetKey),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
);
final mockChannel = MockChannel();
@@ -289,10 +287,10 @@ void main() {
const listWidgetKey = Key('listWidget');
final messageListCore = MessageListCore(
key: messageListCoreKey,
messageListBuilder: (_, __) => Offstage(key: listWidgetKey),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
messageListBuilder: (_, __) => const Offstage(key: listWidgetKey),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
);
final mockChannel = MockChannel();
@@ -344,9 +342,9 @@ void main() {
messages.reversed.map((it) => it.id).join(','),
),
),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
);
final mockChannel = MockChannel();
@@ -391,9 +389,9 @@ void main() {
messages.reversed.map((it) => '${it.parentId}-${it.id}').join(','),
),
),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
parentMessage: parentMessage,
);
@@ -8,34 +8,33 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
import 'matchers/get_message_response_matcher.dart';
import 'mocks.dart';
final testFilter = Filter.custom(operator: '\$test', value: 'testValue');
const testFilter = Filter.custom(operator: '\$test', value: 'testValue');
void main() {
List<GetMessageResponse> _generateMessages({
int count = 3,
int offset = 0,
}) {
return List.generate(
count,
(index) {
index = index + offset;
return GetMessageResponse()
..message = Message(
id: 'testId$index',
text: 'testTextData$index',
)
..channel = ChannelModel(
cid: 'testCid:id',
);
},
);
}
}) =>
List.generate(
count,
(index) {
index = index + offset;
return GetMessageResponse()
..message = Message(
id: 'testId$index',
text: 'testTextData$index',
)
..channel = ChannelModel(
cid: 'testCid:id',
);
},
);
testWidgets(
'messageSearchBlocState.search() should throw if used where '
'StreamChat is not present in the widget tree',
(tester) async {
final messageSearchBloc = MessageSearchBloc(
const messageSearchBloc = MessageSearchBloc(
child: Offstage(),
);
@@ -49,7 +48,7 @@ void main() {
(tester) async {
const messageSearchBlocKey = Key('messageSearchBloc');
const childKey = Key('child');
final messageSearchBloc = MessageSearchBloc(
const messageSearchBloc = MessageSearchBloc(
key: messageSearchBlocKey,
child: Offstage(key: childKey),
);
@@ -101,7 +100,7 @@ void main() {
(tester) async {
const messageSearchBlocKey = Key('messageSearchBloc');
const childKey = Key('child');
final messageSearchBloc = MessageSearchBloc(
const messageSearchBloc = MessageSearchBloc(
key: messageSearchBlocKey,
child: Offstage(key: childKey),
);
@@ -151,7 +150,7 @@ void main() {
(tester) async {
const messageSearchBlocKey = Key('messageSearchBloc');
const childKey = Key('child');
final messageSearchBloc = MessageSearchBloc(
const messageSearchBloc = MessageSearchBloc(
key: messageSearchBlocKey,
child: Offstage(key: childKey),
);
@@ -243,7 +242,7 @@ void main() {
(tester) async {
const messageSearchBlocKey = Key('messageSearchBloc');
const childKey = Key('child');
final messageSearchBloc = MessageSearchBloc(
const messageSearchBloc = MessageSearchBloc(
key: messageSearchBlocKey,
child: Offstage(key: childKey),
);
@@ -6,7 +6,7 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
import 'mocks.dart';
final testFilter = Filter.custom(operator: '\$test', value: 'testValue');
const testFilter = Filter.custom(operator: '\$test', value: 'testValue');
void main() {
List<GetMessageResponse> _generateMessages({
@@ -56,10 +56,10 @@ void main() {
const messageSearchListCoreKey = Key('messageSearchListCore');
final messageSearchListCore = MessageSearchListCore(
key: messageSearchListCoreKey,
childBuilder: (List<GetMessageResponse> messages) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object? error) => Offstage(),
childBuilder: (List<GetMessageResponse> messages) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object? error) => const Offstage(),
filters: testFilter,
);
@@ -86,10 +86,10 @@ void main() {
final controller = MessageSearchListController();
final messageSearchListCore = MessageSearchListCore(
key: messageSearchListCoreKey,
childBuilder: (List<GetMessageResponse> messages) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
childBuilder: (List<GetMessageResponse> messages) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
messageSearchListController: controller,
filters: testFilter,
);
@@ -122,10 +122,10 @@ void main() {
const errorWidgetKey = Key('errorWidget');
final messageSearchListCore = MessageSearchListCore(
key: messageSearchListCoreKey,
childBuilder: (List<GetMessageResponse> messages) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(
childBuilder: (List<GetMessageResponse> messages) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(
key: errorWidgetKey,
),
filters: testFilter,
@@ -173,10 +173,11 @@ void main() {
const emptyWidgetKey = Key('emptyWidget');
final messageSearchListCore = MessageSearchListCore(
key: messageSearchListCoreKey,
childBuilder: (List<GetMessageResponse> messages) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(key: emptyWidgetKey),
errorBuilder: (BuildContext context, Object error) => Offstage(),
childBuilder: (List<GetMessageResponse> messages) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) =>
const Offstage(key: emptyWidgetKey),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
filters: testFilter,
);
@@ -223,12 +224,12 @@ void main() {
const childWidgetKey = Key('childWidget');
final messageSearchListCore = MessageSearchListCore(
key: messageSearchListCoreKey,
childBuilder: (List<GetMessageResponse> messages) => Offstage(
childBuilder: (List<GetMessageResponse> messages) => const Offstage(
key: childWidgetKey,
),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
filters: testFilter,
);
@@ -283,9 +284,9 @@ void main() {
messages.map((e) => '${e.channel?.cid}-${e.message.id}').join(','),
),
),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
paginationParams: pagination,
filters: testFilter,
);
@@ -399,9 +400,10 @@ void main() {
.join(','),
),
),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) =>
const Offstage(),
paginationParams: pagination.copyWith(limit: limit),
filters: testFilter,
);
@@ -27,10 +27,9 @@ void main() {
type: 'testType',
user: users[index],
createdAt: DateTime.now(),
shadowed: false,
replyCount: index,
updatedAt: DateTime.now(),
extraData: {'extra_test_field': 'extraTestData'},
extraData: const {'extra_test_field': 'extraTestData'},
text: 'Dummy text #$index',
pinned: true,
pinnedAt: DateTime.now(),
@@ -48,10 +47,9 @@ void main() {
user: users[index],
parentId: messages[0].id,
createdAt: DateTime.now(),
shadowed: false,
replyCount: index,
updatedAt: DateTime.now(),
extraData: {'extra_test_field': 'extraTestData'},
extraData: const {'extra_test_field': 'extraTestData'},
text: 'Dummy text #$index',
pinned: true,
pinnedAt: DateTime.now(),
@@ -72,7 +70,7 @@ void main() {
final streamChannel = StreamChannel(
key: streamChannelKey,
channel: mockChannel,
child: Offstage(key: childKey),
child: const Offstage(key: childKey),
);
await tester.pumpWidget(streamChannel);
@@ -91,10 +89,10 @@ void main() {
final streamChannel = StreamChannel(
key: streamChannelKey,
channel: mockChannel,
child: Offstage(key: childKey),
child: const Offstage(key: childKey),
);
final errorMessage = 'Error! Error! Error!';
const errorMessage = 'Error! Error! Error!';
final error = DioError(
type: DioErrorType.response,
error: errorMessage,
@@ -128,8 +126,7 @@ void main() {
final streamChannel = StreamChannel(
key: streamChannelKey,
channel: mockChannel,
child: Offstage(key: childKey),
showLoading: true,
child: const Offstage(key: childKey),
);
when(() => mockChannel.initialized).thenAnswer((_) async => false);
@@ -158,8 +155,8 @@ void main() {
final streamChannel = StreamChannel(
key: streamChannelKey,
channel: mockChannel,
child: Offstage(key: childKey),
initialMessageId: 'testInitialMessageId',
child: const Offstage(key: childKey),
);
when(() => mockChannel.initialized).thenAnswer((_) async => true);
@@ -213,8 +210,8 @@ void main() {
StreamChannel(
key: streamChannelKey,
channel: mockChannel,
child: Offstage(key: childKey),
initialMessageId: initialMessageId,
child: const Offstage(key: childKey),
);
final beforePagination = PaginationParams(
@@ -23,7 +23,7 @@ void main() {
final streamChatCore = StreamChatCore(
key: streamChatCoreKey,
client: mockClient,
child: Offstage(key: childKey),
child: const Offstage(key: childKey),
);
await tester.pumpWidget(streamChatCore);
@@ -42,7 +42,7 @@ void main() {
final streamChatCore = StreamChatCore(
key: streamChatCoreKey,
client: mockClient,
child: Offstage(key: childKey),
child: const Offstage(key: childKey),
);
await tester.pumpWidget(streamChatCore);
@@ -63,7 +63,7 @@ void main() {
final streamChatCore = StreamChatCore(
key: streamChatCoreKey,
client: mockClient,
child: Offstage(key: childKey),
child: const Offstage(key: childKey),
);
await tester.pumpWidget(streamChatCore);
@@ -71,7 +71,8 @@ void main() {
expect(find.byKey(streamChatCoreKey), findsOneWidget);
expect(find.byKey(childKey), findsOneWidget);
when(() => mockClient.closeConnection()).thenAnswer((_) async {
// ignore: prefer_expression_function_bodies
when(mockClient.closeConnection).thenAnswer((_) async {
return;
});
@@ -79,9 +80,10 @@ void main() {
find.byKey(streamChatCoreKey),
);
// ignore: cascade_invocations
streamChatCoreState.didChangeAppLifecycleState(AppLifecycleState.paused);
verify(() => mockClient.closeConnection()).called(1);
verify(mockClient.closeConnection).called(1);
},
);
@@ -93,16 +95,16 @@ void main() {
await tester.runAsync(() async {
final mockClient = MockClient();
final mockOnBackgroundEventReceived = MockOnBackgroundEventReceived();
const backgroundKeepAlive = const Duration(seconds: 3);
const backgroundKeepAlive = Duration(seconds: 3);
const streamChatCoreKey = Key('streamChatCore');
const childKey = Key('child');
final streamChatCore = StreamChatCore(
key: streamChatCoreKey,
client: mockClient,
child: Offstage(key: childKey),
onBackgroundEventReceived: mockOnBackgroundEventReceived,
backgroundKeepAlive: backgroundKeepAlive,
connectivityStream: Stream.value(ConnectivityResult.mobile),
child: const Offstage(key: childKey),
);
await tester.pumpWidget(streamChatCore);
@@ -111,8 +113,9 @@ void main() {
expect(find.byKey(childKey), findsOneWidget);
final event = Event(type: EventType.any);
when(() => mockClient.on()).thenAnswer((_) => Stream.value(event));
when(() => mockClient.closeConnection()).thenAnswer((_) async {
when(mockClient.on).thenAnswer((_) => Stream.value(event));
// ignore: prefer_expression_function_bodies
when(mockClient.closeConnection).thenAnswer((_) async {
return;
});
@@ -120,6 +123,7 @@ void main() {
find.byKey(streamChatCoreKey),
);
// ignore: cascade_invocations
streamChatCoreState
.didChangeAppLifecycleState(AppLifecycleState.paused);
@@ -129,7 +133,7 @@ void main() {
await Future.delayed(backgroundKeepAlive);
verify(() => mockClient.closeConnection()).called(1);
verify(mockClient.closeConnection).called(1);
verifyNever(() => mockOnBackgroundEventReceived.call(event));
});
},
@@ -143,15 +147,15 @@ void main() {
await tester.runAsync(() async {
final mockClient = MockClient();
final mockOnBackgroundEventReceived = MockOnBackgroundEventReceived();
const backgroundKeepAlive = const Duration(seconds: 3);
const backgroundKeepAlive = Duration(seconds: 3);
const streamChatCoreKey = Key('streamChatCore');
const childKey = Key('child');
final streamChatCore = StreamChatCore(
key: streamChatCoreKey,
client: mockClient,
child: Offstage(key: childKey),
onBackgroundEventReceived: mockOnBackgroundEventReceived,
backgroundKeepAlive: backgroundKeepAlive,
child: const Offstage(key: childKey),
);
await tester.pumpWidget(streamChatCore);
@@ -160,12 +164,13 @@ void main() {
expect(find.byKey(childKey), findsOneWidget);
final event = Event(type: EventType.any);
when(() => mockClient.on()).thenAnswer((_) => Stream.value(event));
when(mockClient.on).thenAnswer((_) => Stream.value(event));
final streamChatCoreState = tester.state<StreamChatCoreState>(
find.byKey(streamChatCoreKey),
);
// ignore: cascade_invocations
streamChatCoreState
.didChangeAppLifecycleState(AppLifecycleState.paused);
@@ -193,8 +198,8 @@ void main() {
final streamChatCore = StreamChatCore(
key: streamChatCoreKey,
client: mockClient,
child: Offstage(key: childKey),
connectivityStream: Stream.value(ConnectivityResult.mobile),
child: const Offstage(key: childKey),
);
await tester.pumpWidget(streamChatCore);
@@ -203,10 +208,14 @@ void main() {
expect(find.byKey(childKey), findsOneWidget);
final event = Event(type: EventType.any);
when(() => mockClient.on()).thenAnswer((_) => Stream.value(event));
when(() => mockClient.openConnection())
when(mockClient.on).thenAnswer((_) => Stream.value(event));
when(mockClient.openConnection)
.thenAnswer((_) async => OwnUser(id: 'test'));
when(() => mockClient.closeConnection()).thenAnswer((_) async => null);
// ignore: prefer_expression_function_bodies
when(mockClient.closeConnection).thenAnswer((_) async {
return;
});
when(() => mockClient.wsConnectionStatus)
.thenReturn(ConnectionStatus.disconnected);
@@ -214,6 +223,7 @@ void main() {
find.byKey(streamChatCoreKey),
);
// ignore: cascade_invocations
streamChatCoreState
.didChangeAppLifecycleState(AppLifecycleState.paused);
@@ -222,7 +232,7 @@ void main() {
streamChatCoreState
.didChangeAppLifecycleState(AppLifecycleState.resumed);
verify(() => mockClient.openConnection()).called(1);
verify(mockClient.openConnection).called(1);
});
},
);
@@ -238,18 +248,22 @@ void main() {
const childKey = Key('child');
final event = Event();
when(() => mockClient.on()).thenAnswer((_) => Stream.value(event));
when(() => mockClient.openConnection())
when(mockClient.on).thenAnswer((_) => Stream.value(event));
when(mockClient.openConnection)
.thenAnswer((_) async => OwnUser(id: 'test'));
when(() => mockClient.closeConnection()).thenAnswer((_) async => null);
// ignore: prefer_expression_function_bodies
when(mockClient.closeConnection).thenAnswer((_) async {
return;
});
when(() => mockClient.wsConnectionStatus)
.thenReturn(ConnectionStatus.disconnected);
final streamChatCore = StreamChatCore(
key: streamChatCoreKey,
client: mockClient,
child: Offstage(key: childKey),
connectivityStream: Stream.value(ConnectivityResult.none),
child: const Offstage(key: childKey),
);
await tester.pumpWidget(streamChatCore);
@@ -261,6 +275,7 @@ void main() {
find.byKey(streamChatCoreKey),
);
// ignore: cascade_invocations
streamChatCoreState
.didChangeAppLifecycleState(AppLifecycleState.paused);
@@ -269,7 +284,7 @@ void main() {
streamChatCoreState
.didChangeAppLifecycleState(AppLifecycleState.resumed);
verifyNever(() => mockClient.openConnection());
verifyNever(mockClient.openConnection);
});
},
);
@@ -287,7 +302,7 @@ void main() {
final streamChatCore = StreamChatCore(
key: streamChatCoreKey,
client: mockClient,
child: Offstage(key: childKey),
child: const Offstage(key: childKey),
);
await tester.pumpWidget(streamChatCore);
@@ -310,9 +325,7 @@ void main() {
emits(ownUser),
);
addTearDown(() {
userController.close();
});
addTearDown(userController.close);
});
},
);
@@ -328,18 +341,22 @@ void main() {
BehaviorSubject.seeded(ConnectivityResult.none);
final event = Event();
when(() => mockClient.on()).thenAnswer((_) => Stream.value(event));
when(() => mockClient.openConnection())
when(mockClient.on).thenAnswer((_) => Stream.value(event));
when(mockClient.openConnection)
.thenAnswer((_) async => OwnUser(id: 'test'));
when(() => mockClient.closeConnection()).thenAnswer((_) async => null);
// ignore: prefer_expression_function_bodies
when(mockClient.closeConnection).thenAnswer((_) async {
return;
});
when(() => mockClient.wsConnectionStatus)
.thenReturn(ConnectionStatus.disconnected);
final streamChatCore = StreamChatCore(
key: streamChatCoreKey,
client: mockClient,
child: Offstage(key: childKey),
connectivityStream: _connectivityController.stream,
child: const Offstage(key: childKey),
);
await tester.pumpWidget(streamChatCore);
@@ -351,7 +368,9 @@ void main() {
await Future.delayed(const Duration(seconds: 1));
verify(() => mockClient.openConnection()).called(1);
verify(mockClient.openConnection).called(1);
addTearDown(_connectivityController.close);
});
},
);
@@ -368,8 +387,8 @@ void main() {
final streamChatCore = StreamChatCore(
key: streamChatCoreKey,
client: mockClient,
child: Offstage(key: childKey),
connectivityStream: _connectivityController.stream,
child: const Offstage(key: childKey),
);
await tester.pumpWidget(streamChatCore);
@@ -378,10 +397,14 @@ void main() {
expect(find.byKey(childKey), findsOneWidget);
final event = Event();
when(() => mockClient.on()).thenAnswer((_) => Stream.value(event));
when(() => mockClient.openConnection())
when(mockClient.on).thenAnswer((_) => Stream.value(event));
when(mockClient.openConnection)
.thenAnswer((_) async => OwnUser(id: 'test'));
when(() => mockClient.closeConnection()).thenAnswer((_) async => null);
// ignore: prefer_expression_function_bodies
when(mockClient.closeConnection).thenAnswer((_) async {
return;
});
when(() => mockClient.wsConnectionStatus)
.thenReturn(ConnectionStatus.connected);
@@ -389,7 +412,9 @@ void main() {
await Future.delayed(const Duration(seconds: 1));
verify(() => mockClient.closeConnection()).called(1);
verify(mockClient.closeConnection).called(1);
addTearDown(_connectivityController.close);
});
},
);
@@ -405,18 +430,22 @@ void main() {
BehaviorSubject.seeded(ConnectivityResult.none);
final event = Event();
when(() => mockClient.on()).thenAnswer((_) => Stream.value(event));
when(() => mockClient.openConnection())
when(mockClient.on).thenAnswer((_) => Stream.value(event));
when(mockClient.openConnection)
.thenAnswer((_) async => OwnUser(id: 'test'));
when(() => mockClient.closeConnection()).thenAnswer((_) async => null);
// ignore: prefer_expression_function_bodies
when(mockClient.closeConnection).thenAnswer((_) async {
return;
});
when(() => mockClient.wsConnectionStatus)
.thenReturn(ConnectionStatus.disconnected);
final streamChatCore = StreamChatCore(
key: streamChatCoreKey,
client: mockClient,
child: Offstage(key: childKey),
connectivityStream: _connectivityController.stream,
child: const Offstage(key: childKey),
);
await tester.pumpWidget(streamChatCore);
@@ -428,6 +457,7 @@ void main() {
find.byKey(streamChatCoreKey),
);
// ignore: cascade_invocations
streamChatCoreState
.didChangeAppLifecycleState(AppLifecycleState.paused);
@@ -437,7 +467,9 @@ void main() {
await Future.delayed(const Duration(seconds: 1));
verifyNever(() => mockClient.closeConnection());
verifyNever(mockClient.closeConnection);
addTearDown(_connectivityController.close);
});
},
);
@@ -12,26 +12,24 @@ void main() {
List<User> _generateUsers({
int count = 3,
int offset = 0,
}) {
return List.generate(
count,
(index) {
index = index + offset;
return User(
id: 'testId$index',
role: 'testRole$index',
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
lastActive: DateTime.now(),
online: true,
banned: false,
extraData: {
'name': '${alphabets[index]}-testName',
},
);
},
);
}
}) =>
List.generate(
count,
(index) {
index = index + offset;
return User(
id: 'testId$index',
role: 'testRole$index',
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
lastActive: DateTime.now(),
online: true,
extraData: {
'name': '${alphabets[index]}-testName',
},
);
},
);
testWidgets(
'should throw if UserListCore is used where UsersBloc is not present '
@@ -40,10 +38,10 @@ void main() {
const userListCoreKey = Key('userListCore');
final userListCore = UserListCore(
key: userListCoreKey,
listBuilder: (_, __) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
listBuilder: (_, __) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
);
await tester.pumpWidget(userListCore);
@@ -59,10 +57,10 @@ void main() {
const userListCoreKey = Key('userListCore');
final userListCore = UserListCore(
key: userListCoreKey,
listBuilder: (_, __) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
listBuilder: (_, __) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
);
final mockClient = MockClient();
@@ -88,10 +86,10 @@ void main() {
final controller = UserListController();
final userListCore = UserListCore(
key: userListCoreKey,
listBuilder: (_, __) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
listBuilder: (_, __) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
userListController: controller,
);
@@ -122,9 +120,9 @@ void main() {
const errorWidgetKey = Key('errorWidget');
final userListCore = UserListCore(
key: userListCoreKey,
listBuilder: (_, __) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
listBuilder: (_, __) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) =>
Container(key: errorWidgetKey),
);
@@ -168,10 +166,10 @@ void main() {
const emptyWidgetKey = Key('emptyWidget');
final userListCore = UserListCore(
key: userListCoreKey,
listBuilder: (_, __) => Offstage(),
loadingBuilder: (BuildContext context) => Offstage(),
listBuilder: (_, __) => const Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => Container(key: emptyWidgetKey),
errorBuilder: (BuildContext context, Object error) => Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
);
final mockClient = MockClient();
@@ -214,9 +212,9 @@ void main() {
final userListCore = UserListCore(
key: userListCoreKey,
listBuilder: (_, __) => Container(key: listWidgetKey),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
);
final mockClient = MockClient();
@@ -261,20 +259,20 @@ void main() {
listBuilder: (_, items) => Container(
key: listWidgetKey,
child: ListView(
children: items.map((e) {
return Container(
key: Key(e.key ?? ''),
child: e.when(
headerItem: (heading) => Text(heading),
userItem: (user) => Text(user.id),
),
);
}).toList(growable: false),
children: items
.map((e) => Container(
key: Key(e.key ?? ''),
child: e.when(
headerItem: (heading) => Text(heading),
userItem: (user) => Text(user.id),
),
))
.toList(growable: false),
),
),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
groupAlphabetically: true,
);
@@ -329,20 +327,20 @@ void main() {
listBuilder: (_, items) => Container(
key: listWidgetKey,
child: ListView(
children: items.map((e) {
return Container(
key: Key(e.key ?? ''),
child: e.when(
headerItem: (heading) => Text(heading),
userItem: (user) => Text(user.id),
),
);
}).toList(growable: false),
children: items
.map((e) => Container(
key: Key(e.key ?? ''),
child: e.when(
headerItem: (heading) => Text(heading),
userItem: (user) => Text(user.id),
),
))
.toList(growable: false),
),
),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) => const Offstage(),
pagination: pagination,
groupAlphabetically: true,
);
@@ -425,7 +423,7 @@ void main() {
const pagination = PaginationParams();
StateSetter? _stateSetter;
int limit = pagination.limit;
var limit = pagination.limit;
const userListCoreKey = Key('userListCore');
const listWidgetKey = Key('listWidget');
@@ -434,20 +432,21 @@ void main() {
listBuilder: (_, items) => Container(
key: listWidgetKey,
child: ListView(
children: items.map((e) {
return Container(
key: Key(e.key ?? ''),
child: e.when(
headerItem: (heading) => Text(heading),
userItem: (user) => Text(user.id),
),
);
}).toList(growable: false),
children: items
.map((e) => Container(
key: Key(e.key ?? ''),
child: e.when(
headerItem: (heading) => Text(heading),
userItem: (user) => Text(user.id),
),
))
.toList(growable: false),
),
),
loadingBuilder: (BuildContext context) => Offstage(),
emptyBuilder: (BuildContext context) => Offstage(),
errorBuilder: (BuildContext context, Object error) => Offstage(),
loadingBuilder: (BuildContext context) => const Offstage(),
emptyBuilder: (BuildContext context) => const Offstage(),
errorBuilder: (BuildContext context, Object error) =>
const Offstage(),
pagination: pagination.copyWith(limit: limit),
groupAlphabetically: true,
);
@@ -12,30 +12,28 @@ void main() {
List<User> _generateUsers({
int count = 3,
int offset = 0,
}) {
return List.generate(
count,
(index) {
index = index + offset;
return User(
id: 'testId$index',
role: 'testRole$index',
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
lastActive: DateTime.now(),
online: true,
banned: false,
extraData: {'extra_data_key': 'extraDataValue'},
);
},
);
}
}) =>
List.generate(
count,
(index) {
index = index + offset;
return User(
id: 'testId$index',
role: 'testRole$index',
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
lastActive: DateTime.now(),
online: true,
extraData: const {'extra_data_key': 'extraDataValue'},
);
},
);
testWidgets(
'usersBlocState.queryUsers() should throw if used where '
'StreamChat is not present in the widget tree',
(tester) async {
final usersBloc = UsersBloc(
const usersBloc = UsersBloc(
child: Offstage(),
);
@@ -49,7 +47,7 @@ void main() {
(tester) async {
const usersBlocKey = Key('usersBloc');
const childKey = Key('child');
final usersBloc = UsersBloc(
const usersBloc = UsersBloc(
key: usersBlocKey,
child: Offstage(key: childKey),
);
@@ -98,7 +96,7 @@ void main() {
(tester) async {
const usersBlocKey = Key('usersBloc');
const childKey = Key('child');
final usersBloc = UsersBloc(
const usersBloc = UsersBloc(
key: usersBlocKey,
child: Offstage(key: childKey),
);
@@ -116,7 +114,7 @@ void main() {
find.byKey(usersBlocKey),
);
final error = 'Error! Error! Error!';
const error = 'Error! Error! Error!';
when(() => mockClient.queryUsers(
filter: any(named: 'filter'),
@@ -148,7 +146,7 @@ void main() {
(tester) async {
const usersBlocKey = Key('usersBloc');
const childKey = Key('child');
final usersBloc = UsersBloc(
const usersBloc = UsersBloc(
key: usersBlocKey,
child: Offstage(key: childKey),
);
@@ -231,7 +229,7 @@ void main() {
(tester) async {
const usersBlocKey = Key('usersBloc');
const childKey = Key('child');
final usersBloc = UsersBloc(
const usersBloc = UsersBloc(
key: usersBlocKey,
child: Offstage(key: childKey),
);
@@ -275,7 +273,7 @@ void main() {
final offset = users.length;
final pagination = PaginationParams(offset: offset);
final error = 'Error! Error! Error!';
const error = 'Error! Error! Error!';
when(() => mockClient.queryUsers(
filter: any(named: 'filter'),
@@ -3,8 +3,8 @@ import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_persistence/stream_chat_persistence.dart';
Future<void> main() async {
/// Create a new instance of [StreamChatClient] passing the apikey obtained from your
/// project dashboard.
/// Create a new instance of [StreamChatClient] passing the apikey obtained
/// from your project dashboard.
final client = StreamChatClient('b67pax5b2wdq');
WidgetsFlutterBinding.ensureInitialized();
@@ -22,12 +22,13 @@ Future<void> main() async {
await client.connectUser(
User(
id: 'cool-shadow-7',
extraData: {
extraData: const {
'image':
'https://getstream.io/random_png/?id=cool-shadow-7&amp;name=Cool+shadow',
},
),
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo',
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.'
'gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo',
);
/// Creates a channel using the type `messaging` and `godevs`.
@@ -66,12 +67,10 @@ class StreamExample extends StatelessWidget {
final Channel channel;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Stream Chat Dart Example',
home: HomeScreen(channel: channel),
);
}
Widget build(BuildContext context) => MaterialApp(
title: 'Stream Chat Dart Example',
home: HomeScreen(channel: channel),
);
}
/// Main screen of our application. The layout is comprised of an [AppBar]
@@ -176,83 +175,81 @@ class _MessageViewState extends State<MessageView> {
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: ListView.builder(
controller: _scrollController,
itemCount: _messages.length,
reverse: true,
itemBuilder: (BuildContext context, int index) {
final item = _messages[index];
if (item.user?.id == widget.channel.client.uid) {
return Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.all(8),
child: Text(item.text ?? ''),
),
);
} else {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8),
child: Text(item.text ?? ''),
),
);
}
},
Widget build(BuildContext context) => Column(
children: [
Expanded(
child: ListView.builder(
controller: _scrollController,
itemCount: _messages.length,
reverse: true,
itemBuilder: (BuildContext context, int index) {
final item = _messages[index];
if (item.user?.id == widget.channel.client.uid) {
return Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.all(8),
child: Text(item.text ?? ''),
),
);
} else {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8),
child: Text(item.text ?? ''),
),
);
}
},
),
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: const InputDecoration(
hintText: 'Enter your message',
),
),
),
Material(
type: MaterialType.circle,
color: Colors.blue,
clipBehavior: Clip.hardEdge,
child: InkWell(
onTap: () async {
// We can send a new message by calling `sendMessage` on
// the current channel. After sending a message, the
// TextField is cleared and the list view is scrolled
// to show the new item.
if (_controller.value.text.isNotEmpty) {
await widget.channel.sendMessage(
Message(text: _controller.value.text),
);
_controller.clear();
_updateList();
}
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Center(
child: Icon(
Icons.send,
color: Colors.white,
),
Padding(
padding: const EdgeInsets.all(8),
child: Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: const InputDecoration(
hintText: 'Enter your message',
),
),
),
)
],
),
)
],
);
}
Material(
type: MaterialType.circle,
color: Colors.blue,
clipBehavior: Clip.hardEdge,
child: InkWell(
onTap: () async {
// We can send a new message by calling `sendMessage` on
// the current channel. After sending a message, the
// TextField is cleared and the list view is scrolled
// to show the new item.
if (_controller.value.text.isNotEmpty) {
await widget.channel.sendMessage(
Message(text: _controller.value.text),
);
_controller.clear();
_updateList();
}
},
child: const Padding(
padding: EdgeInsets.all(8),
child: Center(
child: Icon(
Icons.send,
color: Colors.white,
),
),
),
),
)
],
),
)
],
);
}
/// Helper extension for quickly retrieving
@@ -8,9 +8,9 @@ environment:
sdk: ">=2.12.0 <3.0.0"
dependencies:
cupertino_icons: ^1.0.3
flutter:
sdk: flutter
cupertino_icons: ^1.0.0
stream_chat_persistence:
path: ../
@@ -213,8 +213,8 @@ void main() {
test('should return sorted channels using custom field', () async {
int sortComparator(ChannelModel a, ChannelModel b) {
final aData = a.extraData['test_custom_field'] as int;
final bData = b.extraData['test_custom_field'] as int;
final aData = int.parse(a.extraData['test_custom_field'].toString());
final bData = int.parse(b.extraData['test_custom_field'].toString());
return bData.compareTo(aData);
}
@@ -173,8 +173,8 @@ void main() {
expect(entity.shadowed, message.shadowed);
expect(entity.showInChannel, message.showInChannel);
expect(entity.replyCount, message.replyCount);
expect(entity.mentionedUsers,
message.mentionedUsers.map((e) => jsonEncode(e)).toList());
expect(
entity.mentionedUsers, message.mentionedUsers.map(jsonEncode).toList());
expect(entity.reactionScores, message.reactionScores);
expect(entity.reactionCounts, message.reactionCounts);
expect(entity.status, message.status);
@@ -39,7 +39,7 @@ void main() {
lastActive: DateTime.now(),
online: math.Random().nextBool(),
banned: math.Random().nextBool(),
extraData: {'test_extra_data': 'extraData'},
extraData: const {'test_extra_data': 'extraData'},
);
final entity = user.toEntity();
expect(entity, isA<UserEntity>());