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:
@@ -1,10 +1,8 @@
|
||||
analyzer:
|
||||
exclude:
|
||||
- packages/*/lib/**/*.g.dart
|
||||
- packages/*/example/**
|
||||
- packages/*/lib/src/emoji
|
||||
- packages/*/lib/**/*.freezed.dart
|
||||
- packages/*/test/**
|
||||
|
||||
linter:
|
||||
rules:
|
||||
|
||||
@@ -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&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(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
+32
-14
@@ -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(),
|
||||
),
|
||||
+42
-19
@@ -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.
|
||||
///
|
||||
/// Let’s find out how we can change our application chat screen to display the list of conversations and navigate between them.
|
||||
/// Let’s find out how we can change our application chat screen to display
|
||||
/// the list of conversations and navigate between them.
|
||||
///
|
||||
/// > Note: the SDK uses Flutter’s [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 Flutter’s [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(),
|
||||
),
|
||||
+41
-19
@@ -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 you’ve 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.
|
||||
///
|
||||
/// Let’s see how we can make some changes to the SDK’s 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(),
|
||||
),
|
||||
+40
-25
@@ -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 thread’s 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 thread’s 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(
|
||||
+44
-23
@@ -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,
|
||||
),
|
||||
),
|
||||
+50
-33
@@ -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&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&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>());
|
||||
|
||||
Reference in New Issue
Block a user