diff --git a/.github/workflows/build_nightly.yml b/.github/workflows/build_nightly.yml
index 44716c9..011d2fa 100644
--- a/.github/workflows/build_nightly.yml
+++ b/.github/workflows/build_nightly.yml
@@ -69,3 +69,24 @@ jobs:
with:
name: android-stream-chat-v1
path: packages/stream_chat_v1/build/app/outputs/apk/release/app-release.apk
+ build_and_deploy_web:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: config git
+ run: |
+ git config --global user.email "$(git log --format='%ae' HEAD^!)"
+ git config --global user.name "$(git log --format='%an' HEAD^!)"
+ git fetch origin gh-pages:gh-pages
+ - uses: subosito/flutter-action@v1.4.0
+ with:
+ channel: 'stable'
+ - run: flutter pub get
+ - name: Copy production config
+ run: echo "${{ secrets.PRODUCTION_CONFIG }}" > lib/app_config.dart
+ - uses: erickzanardo/flutter-gh-pages@v3
+ with:
+ webRenderer: canvaskit
+ workingDir: packages/stream_chat_v1
+
+
diff --git a/packages/chatty/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/chatty/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
index 1d526a1..919434a 100644
--- a/packages/chatty/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ b/packages/chatty/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -2,6 +2,6 @@
+ location = "self:">
diff --git a/packages/chatty/lib/data/auth_repository.dart b/packages/chatty/lib/data/auth_repository.dart
index 2ac77e8..db5849e 100644
--- a/packages/chatty/lib/data/auth_repository.dart
+++ b/packages/chatty/lib/data/auth_repository.dart
@@ -1,7 +1,7 @@
import 'package:stream_chatter/domain/models/auth_user.dart';
abstract class AuthRepository {
- Future getAuthUser();
+ Future getAuthUser();
Future signIn();
Future logout();
}
diff --git a/packages/chatty/lib/data/image_picker_repository.dart b/packages/chatty/lib/data/image_picker_repository.dart
index f5632f0..08d7ab8 100644
--- a/packages/chatty/lib/data/image_picker_repository.dart
+++ b/packages/chatty/lib/data/image_picker_repository.dart
@@ -1,5 +1,5 @@
import 'dart:io';
abstract class ImagePickerRepository {
- Future pickImage();
+ Future pickImage();
}
diff --git a/packages/chatty/lib/data/local/image_picker_impl.dart b/packages/chatty/lib/data/local/image_picker_impl.dart
index 21a1b15..2bd82dd 100644
--- a/packages/chatty/lib/data/local/image_picker_impl.dart
+++ b/packages/chatty/lib/data/local/image_picker_impl.dart
@@ -4,9 +4,17 @@ import 'package:stream_chatter/data/image_picker_repository.dart';
class ImagePickerImpl extends ImagePickerRepository {
@override
- Future pickImage() async {
+ Future pickImage() async {
final picker = ImagePicker();
- final pickedFile = await picker.getImage(source: ImageSource.gallery, maxWidth: 400);
+ final pickedFile = await picker.getImage(
+ source: ImageSource.gallery,
+ maxWidth: 400,
+ );
+
+ if (pickedFile == null) {
+ return null;
+ }
+
return File(pickedFile.path);
}
}
diff --git a/packages/chatty/lib/data/local/stream_api_local_impl.dart b/packages/chatty/lib/data/local/stream_api_local_impl.dart
index f772a26..4a84e35 100644
--- a/packages/chatty/lib/data/local/stream_api_local_impl.dart
+++ b/packages/chatty/lib/data/local/stream_api_local_impl.dart
@@ -8,7 +8,7 @@ class StreamApiLocalImpl extends StreamApiRepository {
final StreamChatClient _client;
@override
- Future connectUser(ChatUser user, String token) async {
+ Future connectUser(ChatUser user, String? token) async {
Map extraData = {};
if (user.image != null) {
extraData['image'] = user.image;
@@ -18,7 +18,7 @@ class StreamApiLocalImpl extends StreamApiRepository {
}
await _client.disconnect();
await _client.connectUser(
- User(id: user.id, extraData: extraData),
+ User(id: user.id!, extraData: extraData as Map),
token,
);
return user;
@@ -28,12 +28,12 @@ class StreamApiLocalImpl extends StreamApiRepository {
Future> getChatUsers() async {
final result = await _client.queryUsers();
final chatUsers = result.users
- .where((element) => element.id != _client.state.user.id)
+ .where((element) => element.id != _client.state.user!.id)
.map(
(e) => ChatUser(
id: e.id,
name: e.name,
- image: e.extraData['image'],
+ image: e.extraData['image'] as String?,
),
)
.toList();
@@ -46,25 +46,28 @@ class StreamApiLocalImpl extends StreamApiRepository {
}
@override
- Future createGroupChat(String channelId, String name, List members, {String image}) async {
+ Future createGroupChat(
+ String channelId, String? name, List? members,
+ {String? image}) async {
final channel = _client.channel('messaging', id: channelId, extraData: {
- 'name': name,
- 'image': image,
- 'members': [_client.state.user.id, ...members],
+ 'name': name!,
+ 'image': image!,
+ 'members': [_client.state.user!.id, ...members!],
});
await channel.watch();
return channel;
}
@override
- Future createSimpleChat(String friendId) async {
- final channel =
- _client.channel('messaging', id: '${_client.state.user.id.hashCode}${friendId.hashCode}', extraData: {
- 'members': [
- friendId,
- _client.state.user.id,
- ],
- });
+ Future createSimpleChat(String? friendId) async {
+ final channel = _client.channel('messaging',
+ id: '${_client.state.user!.id.hashCode}${friendId.hashCode}',
+ extraData: {
+ 'members': [
+ friendId,
+ _client.state.user!.id,
+ ],
+ });
await channel.watch();
return channel;
}
@@ -81,6 +84,6 @@ class StreamApiLocalImpl extends StreamApiRepository {
User(id: userId),
token,
);
- return _client.state.user.name != null && _client.state.user.name != userId;
+ return _client.state.user!.name != userId;
}
}
diff --git a/packages/chatty/lib/data/local/upload_storage_local_impl.dart b/packages/chatty/lib/data/local/upload_storage_local_impl.dart
index 8a3b804..a1b0f63 100644
--- a/packages/chatty/lib/data/local/upload_storage_local_impl.dart
+++ b/packages/chatty/lib/data/local/upload_storage_local_impl.dart
@@ -4,7 +4,7 @@ import 'package:stream_chatter/data/upload_storage_repository.dart';
class UploadStorageLocalImpl extends UploadStorageRepository {
@override
- Future uploadPhoto(File file, String path) async {
+ Future uploadPhoto(File? file, String path) async {
return 'https://lh3.googleusercontent.com/a-/AOh14GjhqGZ-V7tNXS1pOIp9vbBij4OS9JbzxXgxgy1t=s600-k-no-rp-mo';
}
}
diff --git a/packages/chatty/lib/data/prod/auth_impl.dart b/packages/chatty/lib/data/prod/auth_impl.dart
index 90547bb..cc8e139 100644
--- a/packages/chatty/lib/data/prod/auth_impl.dart
+++ b/packages/chatty/lib/data/prod/auth_impl.dart
@@ -7,7 +7,7 @@ class AuthImpl extends AuthRepository {
FirebaseAuth _auth = FirebaseAuth.instance;
@override
- Future getAuthUser() async {
+ Future getAuthUser() async {
final user = _auth.currentUser;
if (user != null) {
return AuthUser(user.uid);
@@ -19,14 +19,21 @@ class AuthImpl extends AuthRepository {
Future signIn() async {
try {
UserCredential userCredential;
- final GoogleSignInAccount googleUser = await GoogleSignIn().signIn();
- final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
- final GoogleAuthCredential googleAuthCredential = GoogleAuthProvider.credential(
+ final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
+
+ if (googleUser == null) {
+ throw Exception('login error');
+ }
+
+ final GoogleSignInAuthentication googleAuth =
+ await googleUser.authentication;
+ final GoogleAuthCredential googleAuthCredential =
+ GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
- );
+ ) as GoogleAuthCredential;
userCredential = await _auth.signInWithCredential(googleAuthCredential);
- final user = userCredential.user;
+ final user = userCredential.user!;
return AuthUser(user.uid);
} catch (e) {
print(e);
diff --git a/packages/chatty/lib/data/prod/persistent_storage_impl.dart b/packages/chatty/lib/data/prod/persistent_storage_impl.dart
index 5d247d5..d701846 100644
--- a/packages/chatty/lib/data/prod/persistent_storage_impl.dart
+++ b/packages/chatty/lib/data/prod/persistent_storage_impl.dart
@@ -13,6 +13,6 @@ class PersistentStorageImpl extends PersistentStorageRepository {
@override
Future updateDarkMode(bool isDarkMode) async {
final preference = await SharedPreferences.getInstance();
- return await preference.setBool(_isDarkMode, isDarkMode);
+ await preference.setBool(_isDarkMode, isDarkMode);
}
}
diff --git a/packages/chatty/lib/data/prod/stream_api_impl.dart b/packages/chatty/lib/data/prod/stream_api_impl.dart
index 1bcab14..fa65e76 100644
--- a/packages/chatty/lib/data/prod/stream_api_impl.dart
+++ b/packages/chatty/lib/data/prod/stream_api_impl.dart
@@ -11,7 +11,7 @@ class StreamApiImpl extends StreamApiRepository {
final StreamChatClient _client;
@override
- Future connectUser(ChatUser user, String token) async {
+ Future connectUser(ChatUser user, String? token) async {
Map extraData = {};
if (user.image != null) {
extraData['image'] = user.image;
@@ -21,7 +21,7 @@ class StreamApiImpl extends StreamApiRepository {
}
await _client.disconnect();
await _client.connectUser(
- User(id: user.id, extraData: extraData),
+ User(id: user.id!, extraData: extraData as Map),
token,
);
return user;
@@ -31,12 +31,12 @@ class StreamApiImpl extends StreamApiRepository {
Future> getChatUsers() async {
final result = await _client.queryUsers();
final chatUsers = result.users
- .where((element) => element.id != _client.state.user.id)
+ .where((element) => element.id != _client.state.user!.id)
.map(
(e) => ChatUser(
id: e.id,
name: e.name,
- image: e.extraData['image'],
+ image: e.extraData['image'] as String?,
),
)
.toList();
@@ -44,10 +44,10 @@ class StreamApiImpl extends StreamApiRepository {
}
@override
- Future getToken(String userId) async {
+ Future getToken(String userId) async {
//TODO: use your own implementation in Production
final response = await http.post(
- 'your_backend_url',
+ Uri.parse('your_backend_url'),
body: jsonEncode({'id': userId}),
headers: {
'Content-Type': 'application/json; charset=UTF-8',
@@ -62,25 +62,27 @@ class StreamApiImpl extends StreamApiRepository {
}
@override
- Future createGroupChat(String id, String name, List members, {String image}) async {
+ Future createGroupChat(String id, String? name, List? members,
+ {String? image}) async {
final channel = _client.channel('messaging', id: id, extraData: {
- 'name': name,
- 'image': image,
- 'members': [_client.state.user.id, ...members],
+ 'name': name!,
+ 'image': image!,
+ 'members': [_client.state.user!.id, ...members!],
});
await channel.watch();
return channel;
}
@override
- Future createSimpleChat(String friendId) async {
- final channel =
- _client.channel('messaging', id: '${_client.state.user.id.hashCode}${friendId.hashCode}', extraData: {
- 'members': [
- friendId,
- _client.state.user.id,
- ],
- });
+ Future createSimpleChat(String? friendId) async {
+ final channel = _client.channel('messaging',
+ id: '${_client.state.user!.id.hashCode}${friendId.hashCode}',
+ extraData: {
+ 'members': [
+ friendId,
+ _client.state.user!.id,
+ ],
+ });
await channel.watch();
return channel;
}
@@ -97,6 +99,6 @@ class StreamApiImpl extends StreamApiRepository {
User(id: userId),
token,
);
- return _client.state.user.name != null && _client.state.user.name != userId;
+ return _client.state.user!.name != userId;
}
}
diff --git a/packages/chatty/lib/data/prod/upload_storage_impl.dart b/packages/chatty/lib/data/prod/upload_storage_impl.dart
index 37f6830..a476f43 100644
--- a/packages/chatty/lib/data/prod/upload_storage_impl.dart
+++ b/packages/chatty/lib/data/prod/upload_storage_impl.dart
@@ -4,9 +4,9 @@ import 'package:stream_chatter/data/upload_storage_repository.dart';
class UploadStorageImpl extends UploadStorageRepository {
@override
- Future uploadPhoto(File file, String path) async {
+ Future uploadPhoto(File? file, String path) async {
final ref = firebase_storage.FirebaseStorage.instance.ref(path);
- final uploadTask = ref.putFile(file);
+ final uploadTask = ref.putFile(file!);
await uploadTask;
return await ref.getDownloadURL();
}
diff --git a/packages/chatty/lib/data/stream_api_repository.dart b/packages/chatty/lib/data/stream_api_repository.dart
index 4ae31cb..b722932 100644
--- a/packages/chatty/lib/data/stream_api_repository.dart
+++ b/packages/chatty/lib/data/stream_api_repository.dart
@@ -3,10 +3,12 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
abstract class StreamApiRepository {
Future> getChatUsers();
- Future getToken(String userId);
+ Future getToken(String userId);
Future connectIfExist(String userId);
- Future connectUser(ChatUser user, String token);
- Future createGroupChat(String channelId, String name, List members, {String image});
- Future createSimpleChat(String friendId);
+ Future connectUser(ChatUser user, String? token);
+ Future createGroupChat(
+ String channelId, String? name, List? members,
+ {String? image});
+ Future createSimpleChat(String? friendId);
Future logout();
}
diff --git a/packages/chatty/lib/data/upload_storage_repository.dart b/packages/chatty/lib/data/upload_storage_repository.dart
index 6876138..88590ee 100644
--- a/packages/chatty/lib/data/upload_storage_repository.dart
+++ b/packages/chatty/lib/data/upload_storage_repository.dart
@@ -1,5 +1,5 @@
import 'dart:io';
abstract class UploadStorageRepository {
- Future uploadPhoto(File file, String path);
+ Future uploadPhoto(File? file, String path);
}
diff --git a/packages/chatty/lib/dependencies.dart b/packages/chatty/lib/dependencies.dart
index f6d6414..d60194c 100644
--- a/packages/chatty/lib/dependencies.dart
+++ b/packages/chatty/lib/dependencies.dart
@@ -18,10 +18,13 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
List buildRepositories(StreamChatClient client) {
//TODO: Here you can use your local implementations of your repositories
return [
- RepositoryProvider(create: (_) => StreamApiImpl(client)),
- RepositoryProvider(create: (_) => PersistentStorageImpl()),
+ RepositoryProvider(
+ create: (_) => StreamApiImpl(client)),
+ RepositoryProvider(
+ create: (_) => PersistentStorageImpl()),
RepositoryProvider(create: (_) => AuthImpl()),
- RepositoryProvider(create: (_) => UploadStorageImpl()),
+ RepositoryProvider(
+ create: (_) => UploadStorageImpl()),
RepositoryProvider(create: (_) => ImagePickerImpl()),
RepositoryProvider(
create: (context) => ProfileSignInUseCase(
diff --git a/packages/chatty/lib/domain/models/chat_user.dart b/packages/chatty/lib/domain/models/chat_user.dart
index 05a0a61..9ad376b 100644
--- a/packages/chatty/lib/domain/models/chat_user.dart
+++ b/packages/chatty/lib/domain/models/chat_user.dart
@@ -1,6 +1,6 @@
class ChatUser {
const ChatUser({this.name, this.image, this.id});
- final String name;
- final String image;
- final String id;
+ final String? name;
+ final String? image;
+ final String? id;
}
diff --git a/packages/chatty/lib/domain/usecases/create_group_usecase.dart b/packages/chatty/lib/domain/usecases/create_group_usecase.dart
index 82122a2..4b0dc78 100644
--- a/packages/chatty/lib/domain/usecases/create_group_usecase.dart
+++ b/packages/chatty/lib/domain/usecases/create_group_usecase.dart
@@ -7,9 +7,9 @@ import 'package:uuid/uuid.dart';
class CreateGroupInput {
CreateGroupInput({this.imageFile, this.name, this.members});
- final File imageFile;
- final String name;
- final List members;
+ final File? imageFile;
+ final String? name;
+ final List? members;
}
class CreateGroupUseCase {
@@ -23,9 +23,10 @@ class CreateGroupUseCase {
Future createGroup(CreateGroupInput input) async {
final channelId = Uuid().v4();
- String image;
+ String? image;
if (input.imageFile != null) {
- image = await _uploadStorageRepository.uploadPhoto(input.imageFile, 'channels/$channelId');
+ image = await _uploadStorageRepository.uploadPhoto(
+ input.imageFile, 'channels/$channelId');
}
final channel = await _streamApiRepository.createGroupChat(
channelId,
diff --git a/packages/chatty/lib/domain/usecases/profile_sign_in_usecase.dart b/packages/chatty/lib/domain/usecases/profile_sign_in_usecase.dart
index 8450581..19969d6 100644
--- a/packages/chatty/lib/domain/usecases/profile_sign_in_usecase.dart
+++ b/packages/chatty/lib/domain/usecases/profile_sign_in_usecase.dart
@@ -5,10 +5,12 @@ import 'package:stream_chatter/data/stream_api_repository.dart';
import 'package:stream_chatter/data/upload_storage_repository.dart';
import 'package:stream_chatter/domain/models/chat_user.dart';
+import '../exceptions/auth_exception.dart';
+
class ProfileInput {
ProfileInput({this.imageFile, this.name});
- final File imageFile;
- final String name;
+ final File? imageFile;
+ final String? name;
}
class ProfileSignInUseCase {
@@ -24,11 +26,21 @@ class ProfileSignInUseCase {
Future verify(ProfileInput input) async {
final auth = await _authRepository.getAuthUser();
- final token = await _streamApiRepository.getToken(auth.id);
- String image;
- if (input.imageFile != null) {
- image = await _uploadStorageRepository.uploadPhoto(input.imageFile, 'users/${auth.id}');
+ if (auth == null) {
+ throw AuthException(AuthErrorCode.not_auth);
}
- await _streamApiRepository.connectUser(ChatUser(name: input.name, id: auth.id, image: image), token);
+ final token = await _streamApiRepository.getToken(auth.id);
+ String? image;
+ if (input.imageFile != null) {
+ image = await _uploadStorageRepository.uploadPhoto(
+ input.imageFile, 'users/${auth.id}');
+ }
+ await _streamApiRepository.connectUser(
+ ChatUser(
+ name: input.name,
+ id: auth.id,
+ image: image,
+ ),
+ token);
}
}
diff --git a/packages/chatty/lib/main.dart b/packages/chatty/lib/main.dart
index 479c1c8..817010d 100644
--- a/packages/chatty/lib/main.dart
+++ b/packages/chatty/lib/main.dart
@@ -34,7 +34,8 @@ class MyApp extends StatelessWidget {
return StreamChat(
child: child,
client: _streamChatClient,
- streamChatThemeData: StreamChatThemeData.fromTheme(Theme.of(context)).copyWith(
+ streamChatThemeData:
+ StreamChatThemeData.fromTheme(Theme.of(context)).copyWith(
ownMessageTheme: MessageTheme(
messageBackgroundColor: Theme.of(context).accentColor,
messageText: TextStyle(color: Colors.white),
diff --git a/packages/chatty/lib/navigator_utils.dart b/packages/chatty/lib/navigator_utils.dart
index 2d5c27f..731c4c1 100644
--- a/packages/chatty/lib/navigator_utils.dart
+++ b/packages/chatty/lib/navigator_utils.dart
@@ -18,5 +18,7 @@ Future pushAndReplaceToPage(BuildContext context, Widget widget) async {
Future popAllAndPush(BuildContext context, Widget widget) async {
await Navigator.pushAndRemoveUntil(
- context, MaterialPageRoute(builder: (BuildContext context) => widget), ModalRoute.withName('/'));
+ context,
+ MaterialPageRoute(builder: (BuildContext context) => widget),
+ ModalRoute.withName('/'));
}
diff --git a/packages/chatty/lib/ui/common/avatar_image_view.dart b/packages/chatty/lib/ui/common/avatar_image_view.dart
index 76ee22a..08a2fbf 100644
--- a/packages/chatty/lib/ui/common/avatar_image_view.dart
+++ b/packages/chatty/lib/ui/common/avatar_image_view.dart
@@ -1,9 +1,9 @@
import 'package:flutter/material.dart';
class AvatarImageView extends StatelessWidget {
- const AvatarImageView({Key key, this.onTap, this.child}) : super(key: key);
- final Widget child;
- final VoidCallback onTap;
+ const AvatarImageView({Key? key, this.onTap, this.child}) : super(key: key);
+ final Widget? child;
+ final VoidCallback? onTap;
@override
Widget build(BuildContext context) {
diff --git a/packages/chatty/lib/ui/common/loading_view.dart b/packages/chatty/lib/ui/common/loading_view.dart
index 9e1cded..6ff3f12 100644
--- a/packages/chatty/lib/ui/common/loading_view.dart
+++ b/packages/chatty/lib/ui/common/loading_view.dart
@@ -5,8 +5,8 @@ class LoadingView extends StatelessWidget {
final Widget child;
const LoadingView({
- Key key,
- @required this.child,
+ Key? key,
+ required this.child,
this.isLoading = false,
}) : super(key: key);
diff --git a/packages/chatty/lib/ui/common/my_channel_preview.dart b/packages/chatty/lib/ui/common/my_channel_preview.dart
index b561e45..2e168b5 100644
--- a/packages/chatty/lib/ui/common/my_channel_preview.dart
+++ b/packages/chatty/lib/ui/common/my_channel_preview.dart
@@ -1,3 +1,4 @@
+import 'package:collection/collection.dart' show IterableExtension;
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:jiffy/jiffy.dart';
@@ -30,22 +31,22 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
/// Modify it to change the widget appearance.
class MyChannelPreview extends StatelessWidget {
/// Function called when tapping this widget
- final void Function(Channel) onTap;
+ final void Function(Channel)? onTap;
/// Function called when long pressing this widget
- final void Function(Channel) onLongPress;
+ final void Function(Channel)? onLongPress;
/// Channel displayed
final Channel channel;
/// The function called when the image is tapped
- final VoidCallback onImageTap;
+ final VoidCallback? onImageTap;
- final String heroTag;
+ final String? heroTag;
MyChannelPreview({
- @required this.channel,
- Key key,
+ required this.channel,
+ Key? key,
this.onTap,
this.onLongPress,
this.onImageTap,
@@ -59,24 +60,24 @@ class MyChannelPreview extends StatelessWidget {
initialData: channel.isMuted,
builder: (context, snapshot) {
return Opacity(
- opacity: snapshot.data ? 0.5 : 1,
+ opacity: snapshot.data! ? 0.5 : 1,
child: ListTile(
contentPadding: const EdgeInsets.symmetric(
horizontal: 8,
),
onTap: () {
if (onTap != null) {
- onTap(channel);
+ onTap!(channel);
}
},
onLongPress: () {
if (onLongPress != null) {
- onLongPress(channel);
+ onLongPress!(channel);
}
},
leading: Material(
child: Hero(
- tag: heroTag,
+ tag: heroTag!,
child: StreamChannel(
channel: channel,
child: ChannelImage(
@@ -90,16 +91,18 @@ class MyChannelPreview extends StatelessWidget {
children: [
Flexible(
child: ChannelName(
- textStyle: StreamChatTheme.of(context).channelPreviewTheme.title,
+ textStyle:
+ StreamChatTheme.of(context).channelPreviewTheme.title,
),
),
StreamBuilder>(
- stream: channel.state.membersStream,
- initialData: channel.state.members,
+ stream: channel.state!.membersStream,
+ initialData: channel.state!.members,
builder: (context, snapshot) {
if (!snapshot.hasData ||
- snapshot.data.isEmpty ||
- !snapshot.data.any((Member e) => e.user.id == channel.client.state.user.id)) {
+ snapshot.data!.isEmpty ||
+ !snapshot.data!.any((Member e) =>
+ e.user!.id == channel.client.state.user!.id)) {
return SizedBox();
}
return ChannelUnreadIndicator(
@@ -114,20 +117,26 @@ class MyChannelPreview extends StatelessWidget {
Flexible(child: _buildSubtitle(context)),
Builder(
builder: (context) {
- final lastMessage = channel.state.messages.lastWhere(
+ final lastMessage =
+ channel.state!.messages.lastWhereOrNull(
(m) => !m.isDeleted && m.shadowed != true,
- orElse: () => null,
);
- if (lastMessage?.user?.id == StreamChat.of(context).user.id) {
+ if (lastMessage?.user?.id ==
+ StreamChat.of(context).user!.id) {
return Padding(
padding: const EdgeInsets.only(right: 4.0),
child: SendingIndicator(
- message: lastMessage,
- size: StreamChatTheme.of(context).channelPreviewTheme.indicatorIconSize,
- isMessageRead: channel.state.read
- ?.where((element) => element.user.id != channel.client.state.user.id)
- ?.where((element) => element.lastRead.isAfter(lastMessage.createdAt))
- ?.isNotEmpty ==
+ message: lastMessage!,
+ size: StreamChatTheme.of(context)
+ .channelPreviewTheme
+ .indicatorIconSize,
+ isMessageRead: channel.state!.read
+ ?.where((element) =>
+ element.user.id !=
+ channel.client.state.user!.id)
+ .where((element) => element.lastRead
+ .isAfter(lastMessage.createdAt))
+ .isNotEmpty ==
true,
),
);
@@ -144,21 +153,22 @@ class MyChannelPreview extends StatelessWidget {
}
Widget _buildDate(BuildContext context) {
- return StreamBuilder(
+ return StreamBuilder(
stream: channel.lastMessageAtStream,
initialData: channel.lastMessageAt,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return SizedBox();
}
- final lastMessageAt = snapshot.data.toLocal();
+ final lastMessageAt = snapshot.data!.toLocal();
String stringDate;
final now = DateTime.now();
var startOfDay = DateTime(now.year, now.month, now.day);
- if (lastMessageAt.millisecondsSinceEpoch >= startOfDay.millisecondsSinceEpoch) {
+ if (lastMessageAt.millisecondsSinceEpoch >=
+ startOfDay.millisecondsSinceEpoch) {
stringDate = Jiffy(lastMessageAt.toLocal()).format('HH:mm');
} else if (lastMessageAt.millisecondsSinceEpoch >=
startOfDay.subtract(Duration(days: 1)).millisecondsSinceEpoch) {
@@ -187,8 +197,14 @@ class MyChannelPreview extends StatelessWidget {
),
Text(
' Channel is muted',
- style: StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith(
- color: StreamChatTheme.of(context).channelPreviewTheme.subtitle.color,
+ style: StreamChatTheme.of(context)
+ .channelPreviewTheme
+ .subtitle!
+ .copyWith(
+ color: StreamChatTheme.of(context)
+ .channelPreviewTheme
+ .subtitle!
+ .color,
),
),
],
@@ -197,52 +213,64 @@ class MyChannelPreview extends StatelessWidget {
return TypingIndicator(
channel: channel,
alternativeWidget: _buildLastMessage(context),
- style: StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith(
- color: StreamChatTheme.of(context).channelPreviewTheme.subtitle.color,
+ style: StreamChatTheme.of(context).channelPreviewTheme.subtitle!.copyWith(
+ color:
+ StreamChatTheme.of(context).channelPreviewTheme.subtitle!.color,
),
);
}
Widget _buildLastMessage(BuildContext context) {
- return StreamBuilder>(
- stream: channel.state.messagesStream,
- initialData: channel.state.messages,
+ return StreamBuilder?>(
+ stream: channel.state!.messagesStream,
+ initialData: channel.state!.messages,
builder: (context, snapshot) {
- final lastMessage = snapshot.data?.lastWhere((m) => m.shadowed != true && !m.isDeleted, orElse: () => null);
+ final lastMessage = snapshot.data
+ ?.lastWhereOrNull((m) => m.shadowed != true && !m.isDeleted);
if (lastMessage == null) {
return SizedBox();
}
var text = lastMessage.text;
- if (lastMessage.attachments != null) {
- final parts = [
- ...lastMessage.attachments.map((e) {
- if (e.type == 'image') {
- return '📷';
- } else if (e.type == 'video') {
- return '🎬';
- } else if (e.type == 'giphy') {
- return '[GIF]';
- }
- return e == lastMessage.attachments.last ? (e.title ?? 'File') : '${e.title ?? 'File'} , ';
- }).where((e) => e != null),
- lastMessage.text ?? '',
- ];
+ final parts = [
+ ...lastMessage.attachments.map((e) {
+ if (e.type == 'image') {
+ return '📷';
+ } else if (e.type == 'video') {
+ return '🎬';
+ } else if (e.type == 'giphy') {
+ return '[GIF]';
+ }
+ return e == lastMessage.attachments.last
+ ? (e.title ?? 'File')
+ : '${e.title ?? 'File'} , ';
+ }),
+ lastMessage.text ?? '',
+ ];
- text = parts.join(' ');
- }
+ text = parts.join(' ');
return Text.rich(
_getDisplayText(
text,
lastMessage.mentionedUsers,
lastMessage.attachments,
- StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith(
- color: StreamChatTheme.of(context).channelPreviewTheme.subtitle.color,
- fontStyle: (lastMessage.isSystem || lastMessage.isDeleted) ? FontStyle.italic : FontStyle.normal),
- StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith(
- color: StreamChatTheme.of(context).channelPreviewTheme.subtitle.color,
- fontStyle: (lastMessage.isSystem || lastMessage.isDeleted) ? FontStyle.italic : FontStyle.normal,
+ StreamChatTheme.of(context).channelPreviewTheme.subtitle!.copyWith(
+ color: StreamChatTheme.of(context)
+ .channelPreviewTheme
+ .subtitle!
+ .color,
+ fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
+ ? FontStyle.italic
+ : FontStyle.normal),
+ StreamChatTheme.of(context).channelPreviewTheme.subtitle!.copyWith(
+ color: StreamChatTheme.of(context)
+ .channelPreviewTheme
+ .subtitle!
+ .color,
+ fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
+ ? FontStyle.italic
+ : FontStyle.normal,
fontWeight: FontWeight.bold),
),
maxLines: 1,
@@ -252,19 +280,25 @@ class MyChannelPreview extends StatelessWidget {
);
}
- TextSpan _getDisplayText(String text, List mentions, List attachments, TextStyle normalTextStyle,
+ TextSpan _getDisplayText(
+ String text,
+ List mentions,
+ List attachments,
+ TextStyle normalTextStyle,
TextStyle mentionsTextStyle) {
var textList = text.split(' ');
var resList = [];
for (var e in textList) {
- if (mentions != null && mentions.isNotEmpty && mentions.any((element) => '@${element.name}' == e)) {
+ if (mentions.isNotEmpty &&
+ mentions.any((element) => '@${element.name}' == e)) {
resList.add(TextSpan(
text: '$e ',
style: mentionsTextStyle,
));
- } else if (attachments != null &&
- attachments.isNotEmpty &&
- attachments.where((e) => e.title != null).any((element) => element.title == e)) {
+ } else if (attachments.isNotEmpty &&
+ attachments
+ .where((e) => e.title != null)
+ .any((element) => element.title == e)) {
resList.add(TextSpan(
text: '$e ',
style: normalTextStyle.copyWith(fontStyle: FontStyle.italic),
@@ -283,8 +317,8 @@ class MyChannelPreview extends StatelessWidget {
class ChannelUnreadIndicator extends StatelessWidget {
const ChannelUnreadIndicator({
- Key key,
- @required this.channel,
+ Key? key,
+ required this.channel,
}) : super(key: key);
final Channel channel;
@@ -292,8 +326,8 @@ class ChannelUnreadIndicator extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder(
- stream: channel.state.unreadCountStream,
- initialData: channel.state.unreadCount,
+ stream: channel.state!.unreadCountStream,
+ initialData: channel.state!.unreadCount,
builder: (context, snapshot) {
if (!snapshot.hasData || snapshot.data == 0) {
return SizedBox();
@@ -301,7 +335,9 @@ class ChannelUnreadIndicator extends StatelessWidget {
return Material(
borderRadius: BorderRadius.circular(8),
- color: StreamChatTheme.of(context).channelPreviewTheme.unreadCounterColor,
+ color: StreamChatTheme.of(context)
+ .channelPreviewTheme
+ .unreadCounterColor,
child: Padding(
padding: const EdgeInsets.only(
left: 5.0,
@@ -311,7 +347,7 @@ class ChannelUnreadIndicator extends StatelessWidget {
),
child: Center(
child: Text(
- '${snapshot.data > 99 ? '99+' : snapshot.data}',
+ '${snapshot.data! > 99 ? '99+' : snapshot.data}',
style: TextStyle(
fontSize: 11,
color: Colors.white,
diff --git a/packages/chatty/lib/ui/home/chat/chat_view.dart b/packages/chatty/lib/ui/home/chat/chat_view.dart
index b9f4ea7..0bed83a 100644
--- a/packages/chatty/lib/ui/home/chat/chat_view.dart
+++ b/packages/chatty/lib/ui/home/chat/chat_view.dart
@@ -23,11 +23,10 @@ class ChatView extends StatelessWidget {
),
body: ChannelsBloc(
child: ChannelListView(
- filter: {
- 'members': {
- '\$in': [StreamChat.of(context).user?.id],
- }
- },
+ filter: Filter.in_(
+ 'members',
+ [StreamChat.of(context).user!.id],
+ ),
sort: [SortOption('last_message_at')],
channelPreviewBuilder: (context, channel) {
return Container(
@@ -36,20 +35,22 @@ class ChatView extends StatelessWidget {
channel: channel,
heroTag: channel.id,
onImageTap: () {
- String name;
- String image;
+ String? name;
+ String? image;
final currentUser = StreamChat.of(context).client.state.user;
if (channel.isGroup) {
name = channel.extraData['name'];
image = channel.extraData['image'];
} else {
- final friend =
- channel.state.members.where((element) => element.userId != currentUser.id).first.user;
+ final friend = channel.state!.members
+ .where((element) => element.userId != currentUser!.id)
+ .first
+ .user!;
name = friend.name;
- image = friend.extraData['image'];
+ image = friend.extraData['image'] as String?;
}
- return Navigator.of(context).push(
+ Navigator.of(context).push(
PageRouteBuilder(
barrierColor: Colors.black45,
barrierDismissible: true,
@@ -108,15 +109,15 @@ class ChannelPage extends StatelessWidget {
class ChatDetailView extends StatelessWidget {
const ChatDetailView({
- Key key,
+ Key? key,
this.image,
this.name,
this.channelId,
}) : super(key: key);
- final String image;
- final String name;
- final String channelId;
+ final String? image;
+ final String? name;
+ final String? channelId;
@override
Widget build(BuildContext context) {
@@ -133,10 +134,10 @@ class ChatDetailView extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
Hero(
- tag: channelId,
+ tag: channelId!,
child: ClipOval(
child: Image.network(
- image,
+ image!,
height: 180,
width: 180,
fit: BoxFit.cover,
@@ -144,7 +145,7 @@ class ChatDetailView extends StatelessWidget {
),
),
Text(
- name,
+ name!,
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 22,
diff --git a/packages/chatty/lib/ui/home/chat/selection/friends_selection_cubit.dart b/packages/chatty/lib/ui/home/chat/selection/friends_selection_cubit.dart
index 3f35080..d43c222 100644
--- a/packages/chatty/lib/ui/home/chat/selection/friends_selection_cubit.dart
+++ b/packages/chatty/lib/ui/home/chat/selection/friends_selection_cubit.dart
@@ -13,21 +13,27 @@ class FriendsSelectionCubit extends Cubit> {
FriendsSelectionCubit(this._streamApiRepository) : super([]);
final StreamApiRepository _streamApiRepository;
- List get selectedUsers => state.where((element) => element.selected).toList();
+ List get selectedUsers =>
+ state.where((element) => element.selected).toList();
Future init() async {
- final chatUsers = (await _streamApiRepository.getChatUsers()).map((e) => ChatUserState(e)).toList();
+ final chatUsers = (await _streamApiRepository.getChatUsers())
+ .map((e) => ChatUserState(e))
+ .toList();
emit(chatUsers);
}
void selectUser(ChatUserState chatUser) {
- final index = state.indexWhere((element) => element.chatUser.id == chatUser.chatUser.id);
- state[index] = ChatUserState(state[index].chatUser, selected: !chatUser.selected);
+ final index = state
+ .indexWhere((element) => element.chatUser.id == chatUser.chatUser.id);
+ state[index] =
+ ChatUserState(state[index].chatUser, selected: !chatUser.selected);
emit(List.from(state));
}
Future createFriendChannel(ChatUserState chatUserState) async {
- return await _streamApiRepository.createSimpleChat(chatUserState.chatUser.id);
+ return await _streamApiRepository
+ .createSimpleChat(chatUserState.chatUser.id);
}
}
diff --git a/packages/chatty/lib/ui/home/chat/selection/friends_selection_view.dart b/packages/chatty/lib/ui/home/chat/selection/friends_selection_view.dart
index 4cafcef..3fdb50b 100644
--- a/packages/chatty/lib/ui/home/chat/selection/friends_selection_view.dart
+++ b/packages/chatty/lib/ui/home/chat/selection/friends_selection_view.dart
@@ -7,8 +7,11 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
class FriendsSelectionView extends StatelessWidget {
- void _createFriendChannel(BuildContext context, ChatUserState chatUserState) async {
- final channel = await context.read().createFriendChannel(chatUserState);
+ void _createFriendChannel(
+ BuildContext context, ChatUserState chatUserState) async {
+ final channel = await context
+ .read()
+ .createFriendChannel(chatUserState);
pushAndReplaceToPage(
context,
Scaffold(
@@ -26,19 +29,23 @@ class FriendsSelectionView extends StatelessWidget {
final accentColor = Theme.of(context).accentColor;
return MultiBlocProvider(
providers: [
- BlocProvider(create: (context) => FriendsSelectionCubit(context.read())..init()),
+ BlocProvider(
+ create: (context) => FriendsSelectionCubit(context.read())..init()),
BlocProvider(create: (_) => FriendsGroupCubit()),
],
child: BlocBuilder(builder: (context, isGroup) {
- return BlocBuilder>(builder: (context, snapshot) {
- final selectedUsers = context.read().selectedUsers;
+ return BlocBuilder>(
+ builder: (context, snapshot) {
+ final selectedUsers =
+ context.read().selectedUsers;
return Scaffold(
floatingActionButton: isGroup && selectedUsers.isNotEmpty
? FloatingActionButton(
child: Icon(Icons.arrow_right_alt_rounded),
onPressed: () {
- pushAndReplaceToPage(context, GroupSelectionView(selectedUsers));
+ pushAndReplaceToPage(
+ context, GroupSelectionView(selectedUsers));
})
: null,
backgroundColor: Theme.of(context).canvasColor,
@@ -91,12 +98,14 @@ class FriendsSelectionView extends StatelessWidget {
backgroundColor: accentColor,
child: Icon(Icons.group_outlined),
),
- title: Text('Create group', style: TextStyle(fontWeight: FontWeight.w700)),
+ title: Text('Create group',
+ style: TextStyle(fontWeight: FontWeight.w700)),
subtitle: Text('Talk with 2 or more contacts'),
)
else if (isGroup && selectedUsers.isEmpty)
Padding(
- padding: const EdgeInsets.only(top: 15.0, left: 20.0, bottom: 20),
+ padding: const EdgeInsets.only(
+ top: 15.0, left: 20.0, bottom: 20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
@@ -123,7 +132,8 @@ class FriendsSelectionView extends StatelessWidget {
itemBuilder: (context, index) {
final chatUserState = selectedUsers[index];
return Padding(
- padding: const EdgeInsets.symmetric(horizontal: 13.0),
+ padding: const EdgeInsets.symmetric(
+ horizontal: 13.0),
child: Stack(
clipBehavior: Clip.none,
children: [
@@ -132,20 +142,24 @@ class FriendsSelectionView extends StatelessWidget {
children: [
CircleAvatar(
radius: 30,
- backgroundImage: NetworkImage(chatUserState.chatUser.image),
+ backgroundImage: NetworkImage(
+ chatUserState.chatUser.image!),
),
- Text(chatUserState.chatUser.name),
+ Text(chatUserState.chatUser.name!),
],
),
Positioned(
bottom: 40,
right: -4,
child: InkWell(
- onTap: () => context.read().selectUser(chatUserState),
+ onTap: () => context
+ .read()
+ .selectUser(chatUserState),
child: CircleAvatar(
radius: 9,
backgroundColor: accentColor,
- child: Icon(Icons.close_rounded, size: 12),
+ child: Icon(Icons.close_rounded,
+ size: 12),
),
),
),
@@ -163,15 +177,18 @@ class FriendsSelectionView extends StatelessWidget {
_createFriendChannel(context, chatUserState);
},
leading: CircleAvatar(
- backgroundImage: NetworkImage(chatUserState.chatUser.image),
+ backgroundImage:
+ NetworkImage(chatUserState.chatUser.image!),
),
- title: Text(chatUserState.chatUser.name),
+ title: Text(chatUserState.chatUser.name!),
trailing: isGroup
? Checkbox(
value: chatUserState.selected,
onChanged: (val) {
print('select user for group');
- context.read().selectUser(chatUserState);
+ context
+ .read()
+ .selectUser(chatUserState);
},
)
: null,
diff --git a/packages/chatty/lib/ui/home/chat/selection/group_selection_cubit.dart b/packages/chatty/lib/ui/home/chat/selection/group_selection_cubit.dart
index 79ac5eb..db72b1a 100644
--- a/packages/chatty/lib/ui/home/chat/selection/group_selection_cubit.dart
+++ b/packages/chatty/lib/ui/home/chat/selection/group_selection_cubit.dart
@@ -13,8 +13,8 @@ class GroupSelectionState {
this.channel,
this.isLoading = false,
});
- final File file;
- final Channel channel;
+ final File? file;
+ final Channel? channel;
final bool isLoading;
}
diff --git a/packages/chatty/lib/ui/home/chat/selection/group_selection_view.dart b/packages/chatty/lib/ui/home/chat/selection/group_selection_view.dart
index 7f336f9..42f65e5 100644
--- a/packages/chatty/lib/ui/home/chat/selection/group_selection_view.dart
+++ b/packages/chatty/lib/ui/home/chat/selection/group_selection_view.dart
@@ -21,13 +21,14 @@ class GroupSelectionView extends StatelessWidget {
context.read(),
context.read(),
),
- child: BlocConsumer(listener: (context, snapshot) {
+ child: BlocConsumer(
+ listener: (context, snapshot) {
if (snapshot.channel != null) {
pushAndReplaceToPage(
context,
Scaffold(
body: StreamChannel(
- channel: snapshot.channel,
+ channel: snapshot.channel!,
child: ChannelPage(),
),
),
@@ -59,9 +60,9 @@ class GroupSelectionView extends StatelessWidget {
children: [
AvatarImageView(
onTap: context.read().pickImage,
- child: snapshot?.file != null
+ child: snapshot.file != null
? Image.file(
- snapshot?.file,
+ snapshot.file!,
fit: BoxFit.cover,
)
: Icon(
@@ -76,9 +77,12 @@ class GroupSelectionView extends StatelessWidget {
vertical: 20,
),
child: TextField(
- controller: context.read().nameTextController,
+ controller:
+ context.read().nameTextController,
decoration: InputDecoration(
- fillColor: Theme.of(context).bottomNavigationBarTheme.backgroundColor,
+ fillColor: Theme.of(context)
+ .bottomNavigationBarTheme
+ .backgroundColor,
hintText: 'Name of the group',
hintStyle: TextStyle(
fontSize: 13,
@@ -99,9 +103,10 @@ class GroupSelectionView extends StatelessWidget {
children: [
CircleAvatar(
radius: 30,
- backgroundImage: NetworkImage(chatUserState.chatUser.image),
+ backgroundImage:
+ NetworkImage(chatUserState.chatUser.image!),
),
- Text(chatUserState.chatUser.name),
+ Text(chatUserState.chatUser.name!),
],
),
);
diff --git a/packages/chatty/lib/ui/home/home_view.dart b/packages/chatty/lib/ui/home/home_view.dart
index 5199415..0492889 100644
--- a/packages/chatty/lib/ui/home/home_view.dart
+++ b/packages/chatty/lib/ui/home/home_view.dart
@@ -36,7 +36,7 @@ class HomeView extends StatelessWidget {
class HomeNavigationBar extends StatelessWidget {
const HomeNavigationBar({
- Key key,
+ Key? key,
}) : super(key: key);
@override
@@ -61,7 +61,9 @@ class HomeNavigationBar extends StatelessWidget {
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
- color: Theme.of(context).bottomNavigationBarTheme.backgroundColor,
+ color: Theme.of(context)
+ .bottomNavigationBarTheme
+ .backgroundColor,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
@@ -108,22 +110,24 @@ class HomeNavigationBar extends StatelessWidget {
class _HomeNavItem extends StatelessWidget {
const _HomeNavItem({
- Key key,
+ Key? key,
this.iconData,
this.text,
this.onTap,
this.selected = false,
}) : super(key: key);
- final IconData iconData;
- final String text;
- final VoidCallback onTap;
+ final IconData? iconData;
+ final String? text;
+ final VoidCallback? onTap;
final bool selected;
@override
Widget build(BuildContext context) {
- final selectedColor = Theme.of(context).bottomNavigationBarTheme.selectedItemColor;
- final unselectedColor = Theme.of(context).bottomNavigationBarTheme.unselectedItemColor;
+ final selectedColor =
+ Theme.of(context).bottomNavigationBarTheme.selectedItemColor;
+ final unselectedColor =
+ Theme.of(context).bottomNavigationBarTheme.unselectedItemColor;
final color = selected ? selectedColor : unselectedColor;
return GestureDetector(
onTap: onTap,
@@ -131,7 +135,7 @@ class _HomeNavItem extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
Icon(iconData, color: color),
- Text(text, style: TextStyle(color: color)),
+ Text(text!, style: TextStyle(color: color)),
],
),
);
diff --git a/packages/chatty/lib/ui/home/settings/settings_view.dart b/packages/chatty/lib/ui/home/settings/settings_view.dart
index 77ce96a..4347749 100644
--- a/packages/chatty/lib/ui/home/settings/settings_view.dart
+++ b/packages/chatty/lib/ui/home/settings/settings_view.dart
@@ -10,13 +10,14 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
class SettingsView extends StatelessWidget {
@override
Widget build(BuildContext context) {
- final user = StreamChat.of(context).client.state.user;
- final image = user?.extraData['image'];
+ final user = StreamChat.of(context).client.state.user!;
+ final image = user.extraData['image'];
final textColor = Theme.of(context).appBarTheme.color;
return MultiBlocProvider(
providers: [
BlocProvider(
- create: (_) => SettingsSwitchCubit(context.read().isDark),
+ create: (_) =>
+ SettingsSwitchCubit(context.read().isDark),
),
BlocProvider(
create: (_) => SettingsLogoutCubit(context.read()),
@@ -47,7 +48,7 @@ class SettingsView extends StatelessWidget {
onTap: () => null,
child: image != null
? Image.network(
- image,
+ image as String,
fit: BoxFit.cover,
)
: Icon(
@@ -75,11 +76,14 @@ class SettingsView extends StatelessWidget {
),
),
Spacer(),
- BlocBuilder(builder: (context, snapshot) {
+ BlocBuilder(
+ builder: (context, snapshot) {
return Switch(
value: snapshot,
onChanged: (val) {
- context.read().onChangeDarkMode(val);
+ context
+ .read()
+ .onChangeDarkMode(val);
context.read().updateTheme(val);
},
);
diff --git a/packages/chatty/lib/ui/profile_verify/profile_verify_cubit.dart b/packages/chatty/lib/ui/profile_verify/profile_verify_cubit.dart
index cc82668..2f32aa7 100644
--- a/packages/chatty/lib/ui/profile_verify/profile_verify_cubit.dart
+++ b/packages/chatty/lib/ui/profile_verify/profile_verify_cubit.dart
@@ -11,7 +11,7 @@ class ProfileState {
this.success = false,
this.loading = false,
});
- final File file;
+ final File? file;
final bool success;
final bool loading;
}
diff --git a/packages/chatty/lib/ui/profile_verify/profile_verify_view.dart b/packages/chatty/lib/ui/profile_verify/profile_verify_view.dart
index 3a475c3..6b6c2bd 100644
--- a/packages/chatty/lib/ui/profile_verify/profile_verify_view.dart
+++ b/packages/chatty/lib/ui/profile_verify/profile_verify_view.dart
@@ -13,7 +13,8 @@ class ProfileVerifyView extends StatelessWidget {
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => ProfileVerifyCubit(context.read(), context.read()),
- child: BlocConsumer(listener: (context, snapshot) {
+ child: BlocConsumer(
+ listener: (context, snapshot) {
if (snapshot.success) {
pushAndReplaceToPage(context, HomeView());
}
@@ -38,7 +39,7 @@ class ProfileVerifyView extends StatelessWidget {
onTap: context.read().pickImage,
child: snapshot.file != null
? Image.file(
- snapshot.file,
+ snapshot.file!,
fit: BoxFit.cover,
)
: Icon(
@@ -59,9 +60,12 @@ class ProfileVerifyView extends StatelessWidget {
vertical: 20,
),
child: TextField(
- controller: context.read().nameController,
+ controller:
+ context.read().nameController,
decoration: InputDecoration(
- fillColor: Theme.of(context).bottomNavigationBarTheme.backgroundColor,
+ fillColor: Theme.of(context)
+ .bottomNavigationBarTheme
+ .backgroundColor,
hintText: 'Or just how people now you',
hintStyle: TextStyle(
fontSize: 13,
diff --git a/packages/chatty/lib/ui/sign_in/sign_in_cubit.dart b/packages/chatty/lib/ui/sign_in/sign_in_cubit.dart
index 10d23b2..ee341f2 100644
--- a/packages/chatty/lib/ui/sign_in/sign_in_cubit.dart
+++ b/packages/chatty/lib/ui/sign_in/sign_in_cubit.dart
@@ -20,10 +20,8 @@ class SignInCubit extends Cubit {
emit(SignInState.existing_user);
}
} catch (ex) {
- final result = await _loginUseCase.signIn();
- if (result != null) {
- emit(SignInState.none);
- }
+ _loginUseCase.signIn();
+ emit(SignInState.none);
}
}
}
diff --git a/packages/chatty/lib/ui/sign_in/sign_in_view.dart b/packages/chatty/lib/ui/sign_in/sign_in_view.dart
index 41ff317..d8f2a9e 100644
--- a/packages/chatty/lib/ui/sign_in/sign_in_view.dart
+++ b/packages/chatty/lib/ui/sign_in/sign_in_view.dart
@@ -11,7 +11,8 @@ class SignInView extends StatelessWidget {
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => SignInCubit(context.read()),
- child: BlocConsumer(listener: (context, snapshot) {
+ child:
+ BlocConsumer(listener: (context, snapshot) {
if (snapshot == SignInState.none) {
pushAndReplaceToPage(context, ProfileVerifyView());
} else {
@@ -59,7 +60,9 @@ class SignInView extends StatelessWidget {
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
- color: Theme.of(context).bottomNavigationBarTheme.backgroundColor,
+ color: Theme.of(context)
+ .bottomNavigationBarTheme
+ .backgroundColor,
child: InkWell(
onTap: () {
context.read().signIn();
diff --git a/packages/chatty/lib/ui/themes.dart b/packages/chatty/lib/ui/themes.dart
index c99082d..ef9412b 100644
--- a/packages/chatty/lib/ui/themes.dart
+++ b/packages/chatty/lib/ui/themes.dart
@@ -44,7 +44,7 @@ class Themes {
selectedItemColor: primaryColor,
unselectedItemColor: Colors.grey[300],
),
- textSelectionColor: Colors.white,
+ textSelectionTheme: TextSelectionThemeData(selectionColor: Colors.white),
// switch active color
toggleableActiveColor: primaryColor,
canvasColor: backgroundDarkColor,
diff --git a/packages/chatty/pubspec.yaml b/packages/chatty/pubspec.yaml
index 6327bfd..c0f4552 100644
--- a/packages/chatty/pubspec.yaml
+++ b/packages/chatty/pubspec.yaml
@@ -4,22 +4,23 @@ publish_to: 'none'
version: 1.0.0+1
environment:
- sdk: ">=2.7.0 <3.0.0"
+ sdk: '>=2.12.0 <3.0.0'
dependencies:
flutter:
sdk: flutter
- flutter_bloc: 6.1.2
- stream_chat_flutter: 1.3.0-beta
- uuid: 2.2.2
+ flutter_bloc: ^7.0.0
+ stream_chat_flutter: ^2.0.0-nullsafety.3
+ uuid: ^3.0.4
- firebase_core: 0.7.0
- google_sign_in: 4.5.9
- firebase_auth: 0.20.0+1
- firebase_storage: 7.0.0
- shared_preferences: 0.5.12+4
+ firebase_core: ^1.2.0
+ google_sign_in: ^5.0.3
+ firebase_auth: ^1.2.0
+ firebase_storage: ^8.1.0
+ shared_preferences: ^2.0.5
http: any
+ collection: ^1.15.0-nullsafety.4
dev_dependencies:
flutter_test:
diff --git a/packages/imessage/lib/channel_image.dart b/packages/imessage/lib/channel_image.dart
index 65b23ff..d7b6343 100644
--- a/packages/imessage/lib/channel_image.dart
+++ b/packages/imessage/lib/channel_image.dart
@@ -4,7 +4,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart' show Channel;
import 'package:imessage/utils.dart';
class ChannelImage extends StatelessWidget {
- const ChannelImage({Key key, @required this.channel, @required this.size})
+ const ChannelImage({Key? key, required this.channel, required this.size})
: super(key: key);
final Channel channel;
@@ -14,7 +14,7 @@ class ChannelImage extends StatelessWidget {
Widget build(BuildContext context) {
final avatarUrl = channel.extraData.containsKey('image') &&
(channel.extraData['image'] as String).isNotEmpty
- ? channel.extraData['image'] as String
+ ? channel.extraData['image'] as String?
: 'https://4.bp.blogspot.com/-Jx21kNqFSTU/UXemtqPhZCI/AAAAAAAAh74/BMGSzpU6F48/s1600/funny-cat-pictures-047-001.jpg';
return CupertinoCircleAvatar(
diff --git a/packages/imessage/lib/channel_list_view.dart b/packages/imessage/lib/channel_list_view.dart
index 89c03b5..9421b6d 100644
--- a/packages/imessage/lib/channel_list_view.dart
+++ b/packages/imessage/lib/channel_list_view.dart
@@ -6,7 +6,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'
show Channel, StreamChannel;
class ChannelListView extends StatelessWidget {
- const ChannelListView({Key key, @required this.channels}) : super(key: key);
+ const ChannelListView({Key? key, required this.channels}) : super(key: key);
final List channels;
@override
Widget build(BuildContext context) {
@@ -36,10 +36,10 @@ class ChannelListView extends StatelessWidget {
child,
) =>
SharedAxisTransition(
- child: child,
animation: animation,
secondaryAnimation: secondaryAnimation,
transitionType: SharedAxisTransitionType.horizontal,
+ child: child,
),
),
);
diff --git a/packages/imessage/lib/channel_name_text.dart b/packages/imessage/lib/channel_name_text.dart
index e2e8d2b..169bb9d 100644
--- a/packages/imessage/lib/channel_name_text.dart
+++ b/packages/imessage/lib/channel_name_text.dart
@@ -3,8 +3,8 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart' show Channel;
class ChannelNameText extends StatelessWidget {
const ChannelNameText({
- Key key,
- @required this.channel,
+ Key? key,
+ required this.channel,
this.size = 17,
}) : super(key: key);
@@ -14,7 +14,7 @@ class ChannelNameText extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
- channel.extraData['name'] as String ?? 'No name',
+ channel.extraData['name'] as String? ?? 'No name',
style: TextStyle(
fontSize: size,
fontWeight: FontWeight.bold,
diff --git a/packages/imessage/lib/channel_page_appbar.dart b/packages/imessage/lib/channel_page_appbar.dart
index f991829..5f44995 100644
--- a/packages/imessage/lib/channel_page_appbar.dart
+++ b/packages/imessage/lib/channel_page_appbar.dart
@@ -2,7 +2,7 @@ import 'package:flutter/cupertino.dart';
class ChannelPageAppBar extends StatelessWidget {
const ChannelPageAppBar({
- Key key,
+ Key? key,
}) : super(key: key);
@override
diff --git a/packages/imessage/lib/channel_preview.dart b/packages/imessage/lib/channel_preview.dart
index 8b8c039..98a16c4 100644
--- a/packages/imessage/lib/channel_preview.dart
+++ b/packages/imessage/lib/channel_preview.dart
@@ -11,19 +11,20 @@ class ChannelPreview extends StatelessWidget {
final Channel channel;
const ChannelPreview({
- Key key,
- @required this.onTap,
- @required this.channel,
+ Key? key,
+ required this.onTap,
+ required this.channel,
}) : super(key: key);
@override
Widget build(BuildContext context) {
- final lastMessage =
- channel.state.messages.isNotEmpty ? channel.state.messages.last : null;
+ final lastMessage = channel.state!.messages.isNotEmpty
+ ? channel.state!.messages.last
+ : null;
final prefix = lastMessage?.attachments != null
? lastMessage?.attachments //TODO: ugly
- ?.map((e) {
+ .map((e) {
if (e.type == 'image') {
return '📷 ';
} else if (e.type == 'video') {
@@ -31,8 +32,8 @@ class ChannelPreview extends StatelessWidget {
}
return null;
})
- ?.where((e) => e != null)
- ?.join(' ')
+ .where((e) => e != null)
+ .join(' ')
: '';
return GestureDetector(
onTap: onTap,
@@ -76,9 +77,9 @@ class ChannelPreview extends StatelessWidget {
child: Row(
children: [
Text(
- isSameWeek(channel.lastMessageAt)
- ? formatDateSameWeek(channel.lastMessageAt)
- : formatDate(channel.lastMessageAt),
+ isSameWeek(channel.lastMessageAt!)
+ ? formatDateSameWeek(channel.lastMessageAt!)
+ : formatDate(channel.lastMessageAt!),
style: TextStyle(
fontSize: 15,
color: CupertinoColors.systemGrey,
diff --git a/packages/imessage/lib/cutom_painter.dart b/packages/imessage/lib/cutom_painter.dart
index 4437fc3..0ed6213 100644
--- a/packages/imessage/lib/cutom_painter.dart
+++ b/packages/imessage/lib/cutom_painter.dart
@@ -2,10 +2,10 @@ import 'package:flutter/cupertino.dart';
class ChatBubble extends CustomPainter {
final Color color;
- final Alignment alignment;
+ final Alignment? alignment;
ChatBubble({
- @required this.color,
+ required this.color,
this.alignment,
});
diff --git a/packages/imessage/lib/main.dart b/packages/imessage/lib/main.dart
index f235f41..fc7283d 100644
--- a/packages/imessage/lib/main.dart
+++ b/packages/imessage/lib/main.dart
@@ -1,18 +1,7 @@
import 'package:flutter/cupertino.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'
- show
- Channel,
- ChannelListController,
- ChannelListCore,
- ChannelsBloc,
- LazyLoadScrollView,
- Level,
- PaginationParams,
- SortOption,
- StreamChatClient,
- StreamChatCore,
- User;
+ hide ChannelListView;
import 'package:imessage/channel_list_view.dart';
@@ -37,7 +26,7 @@ Future main() async {
class IMessage extends StatelessWidget {
final StreamChatClient client;
- IMessage({@required this.client});
+ IMessage({required this.client});
@override
Widget build(BuildContext context) {
initializeDateFormatting('en_US', null);
@@ -52,70 +41,69 @@ class IMessage extends StatelessWidget {
class ChatLoader extends StatelessWidget {
ChatLoader({
- Key key,
+ Key? key,
}) : super(key: key);
final channelListController = ChannelListController();
@override
Widget build(BuildContext context) {
- final user = StreamChatCore.of(context).user;
+ final user = StreamChatCore.of(context).user!;
return CupertinoPageScaffold(
- child: ChannelsBloc(
- child: ChannelListCore(
- channelListController: channelListController,
- filter: {
- 'members': {
- r'$in': [user.id],
- },
- 'type': {
- r'$eq': 'messaging',
- },
- },
- sort: [SortOption('last_message_at')],
- pagination: PaginationParams(
- limit: 20,
- ),
- emptyBuilder: (BuildContext context) {
- return Center(
- child: Text('Looks like you are not in any channels'),
- );
- },
- loadingBuilder: (BuildContext context) {
- return Center(
- child: SizedBox(
- height: 100.0,
- width: 100.0,
- child: CupertinoActivityIndicator(),
- ),
- );
- },
- errorBuilder: (BuildContext context, dynamic error) {
- return Center(
- child: Text(
- 'Oh no, something went wrong. Please check your config.'),
- );
- },
- listBuilder: (
- BuildContext context,
- List channels,
- ) =>
- LazyLoadScrollView(
- onEndOfPage: () async {
- channelListController.paginateData();
- },
- child: CustomScrollView(
- slivers: [
- CupertinoSliverRefreshControl(onRefresh: () async {
- channelListController.loadData();
- }),
- ChannelPageAppBar(),
- SliverPadding(
- sliver: ChannelListView(channels: channels),
- padding: const EdgeInsets.only(top: 16),
- )
- ],
- ),
- ))));
+ child: ChannelsBloc(
+ child: ChannelListCore(
+ channelListController: channelListController,
+ filter: Filter.and([
+ Filter.in_('members', [user.id]),
+ Filter.equal('type', 'messaging'),
+ ]),
+ sort: [SortOption('last_message_at')],
+ pagination: PaginationParams(
+ limit: 20,
+ ),
+ emptyBuilder: (BuildContext context) {
+ return Center(
+ child: Text('Looks like you are not in any channels'),
+ );
+ },
+ loadingBuilder: (BuildContext context) {
+ return Center(
+ child: SizedBox(
+ height: 100.0,
+ width: 100.0,
+ child: CupertinoActivityIndicator(),
+ ),
+ );
+ },
+ errorBuilder: (BuildContext context, dynamic error) {
+ return Center(
+ child: Text(
+ 'Oh no, something went wrong. Please check your config.'),
+ );
+ },
+ listBuilder: (
+ BuildContext context,
+ List channels,
+ ) =>
+ LazyLoadScrollView(
+ onEndOfPage: () async {
+ return channelListController.paginateData!();
+ },
+ child: CustomScrollView(
+ slivers: [
+ CupertinoSliverRefreshControl(onRefresh: () async {
+ return channelListController.loadData!();
+ }),
+ ChannelPageAppBar(),
+ SliverPadding(
+ sliver: ChannelListView(channels: channels),
+ padding: const EdgeInsets.only(top: 16),
+ )
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
}
}
diff --git a/packages/imessage/lib/message_header.dart b/packages/imessage/lib/message_header.dart
index 31ae34b..e8ee94b 100644
--- a/packages/imessage/lib/message_header.dart
+++ b/packages/imessage/lib/message_header.dart
@@ -4,7 +4,7 @@ import 'package:imessage/utils.dart';
class MessageHeader extends StatelessWidget {
final String rawTimeStamp;
- const MessageHeader({Key key, @required this.rawTimeStamp}) : super(key: key);
+ const MessageHeader({Key? key, required this.rawTimeStamp}) : super(key: key);
@override
Widget build(BuildContext context) {
diff --git a/packages/imessage/lib/message_input.dart b/packages/imessage/lib/message_input.dart
index 9d9d831..7836498 100644
--- a/packages/imessage/lib/message_input.dart
+++ b/packages/imessage/lib/message_input.dart
@@ -3,11 +3,11 @@ import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:image_picker/image_picker.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart'
- show Attachment, AttachmentFile, Message, MultipartFile, StreamChannel;
+ show Attachment, AttachmentFile, Message, StreamChannel;
class MessageInput extends StatefulWidget {
const MessageInput({
- Key key,
+ Key? key,
}) : super(key: key);
@override
@@ -16,7 +16,6 @@ class MessageInput extends StatefulWidget {
class _MessageInputState extends State {
final textController = TextEditingController();
- File _image;
final picker = ImagePicker();
@override
@@ -39,16 +38,25 @@ class _MessageInputState extends State {
GestureDetector(
onTap: () async {
final pickedFile =
- await picker.getImage(source: ImageSource.gallery);
+ await (picker.getImage(source: ImageSource.gallery));
+ if (pickedFile == null) {
+ return;
+ }
final bytes = await File(pickedFile.path).readAsBytes();
final channel = StreamChannel.of(context).channel;
- final message =
- Message(text: textController.value.text, attachments: [
- Attachment(
- type: 'image',
- file: AttachmentFile(bytes: bytes, path: pickedFile.path),
- ),
- ]);
+ final message = Message(
+ text: textController.value.text,
+ attachments: [
+ Attachment(
+ type: 'image',
+ file: AttachmentFile(
+ bytes: bytes,
+ path: pickedFile.path,
+ size: bytes.length,
+ ),
+ ),
+ ],
+ );
await channel.sendMessage(message);
},
child: Padding(
@@ -68,10 +76,11 @@ class _MessageInputState extends State {
},
placeholder: 'Text Message',
prefix: Padding(
- padding: const EdgeInsets.all(8.0),
- child: Text(
- "") //trick to add padding around placeholder iMessage text
- ),
+ padding: const EdgeInsets.all(8.0),
+ child: Text(
+ '',
+ ), //trick to add padding around placeholder iMessage text
+ ),
suffix: GestureDetector(
onTap: () async {
if (textController.value.text.isNotEmpty) {
diff --git a/packages/imessage/lib/message_list_view.dart b/packages/imessage/lib/message_list_view.dart
index e12572c..b76f506 100644
--- a/packages/imessage/lib/message_list_view.dart
+++ b/packages/imessage/lib/message_list_view.dart
@@ -7,12 +7,12 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'
show Message, StreamChatCore;
class MessageListView extends StatelessWidget {
- const MessageListView({Key key, this.messages}) : super(key: key);
- final List messages;
+ const MessageListView({Key? key, this.messages}) : super(key: key);
+ final List? messages;
@override
Widget build(BuildContext context) {
- final entries = groupBy(messages,
+ final entries = groupBy(messages!,
(Message message) => message.createdAt.toString().substring(0, 10))
.entries
.toList();
@@ -64,8 +64,8 @@ class MessageListView extends StatelessWidget {
}
bool isReceived(Message message, BuildContext context) {
- final currentUserId = StreamChatCore.of(context).user.id;
- return message.user.id == currentUserId;
+ final currentUserId = StreamChatCore.of(context).user!.id;
+ return message.user!.id == currentUserId;
}
bool isSameDay(Message message) =>
diff --git a/packages/imessage/lib/message_page.dart b/packages/imessage/lib/message_page.dart
index 01b555e..f64eb66 100644
--- a/packages/imessage/lib/message_page.dart
+++ b/packages/imessage/lib/message_page.dart
@@ -53,7 +53,7 @@ class MessagePage extends StatelessWidget {
},
messageListBuilder: (context, messages) => LazyLoadScrollView(
onStartOfPage: () async {
- messageListController.paginateData();
+ await messageListController.paginateData!();
},
child: MessageListView(
messages: messages,
diff --git a/packages/imessage/lib/message_widget.dart b/packages/imessage/lib/message_widget.dart
index 494e9d2..8fa6638 100644
--- a/packages/imessage/lib/message_widget.dart
+++ b/packages/imessage/lib/message_widget.dart
@@ -1,8 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/cupertino.dart';
import 'package:imessage/cutom_painter.dart';
-import 'package:stream_chat_flutter/stream_chat_flutter.dart'
- show Message, AttachmentUploadStateBuilder;
+import 'package:stream_chat_flutter/stream_chat_flutter.dart' show Message;
class MessageWidget extends StatelessWidget {
final Alignment alignment;
@@ -11,16 +10,16 @@ class MessageWidget extends StatelessWidget {
final Color messageColor;
const MessageWidget(
- {Key key,
- @required this.alignment,
- @required this.message,
- @required this.color,
- @required this.messageColor})
+ {Key? key,
+ required this.alignment,
+ required this.message,
+ required this.color,
+ required this.messageColor})
: super(key: key);
@override
Widget build(BuildContext context) {
- if (message.attachments?.isNotEmpty == true &&
+ if (message.attachments.isNotEmpty == true &&
message.attachments.first.type == 'image') {
return MessageImage(
color: color, message: message, messageColor: messageColor);
@@ -36,10 +35,10 @@ class MessageWidget extends StatelessWidget {
class MessageImage extends StatelessWidget {
const MessageImage({
- Key key,
- @required this.color,
- @required this.message,
- @required this.messageColor,
+ Key? key,
+ required this.color,
+ required this.message,
+ required this.messageColor,
}) : super(key: key);
final Color color;
@@ -61,23 +60,23 @@ class MessageImage extends StatelessWidget {
children: [
if (message.attachments.first.file != null)
Image.memory(
- message.attachments.first.file.bytes,
+ message.attachments.first.file!.bytes!,
fit: BoxFit.cover,
)
else
CachedNetworkImage(
imageUrl: message.attachments.first.thumbUrl ??
message.attachments.first.imageUrl ??
- message.attachments.first.assetUrl,
+ message.attachments.first.assetUrl!,
),
- if (message.attachments.first?.title != null)
+ if (message.attachments.first.title != null)
Padding(
padding: const EdgeInsets.all(8.0),
- child: Text(message.attachments.first.title,
+ child: Text(message.attachments.first.title!,
style: TextStyle(color: messageColor)),
),
message.attachments.first.pretext != null
- ? Text(message.attachments.first.pretext)
+ ? Text(message.attachments.first.pretext!)
: Container()
],
),
@@ -92,7 +91,7 @@ class MessageImage extends StatelessWidget {
child: Container(
color: color,
child: CachedNetworkImage(
- imageUrl: message.attachments.first.thumbUrl,
+ imageUrl: message.attachments.first.thumbUrl!,
)),
);
}
@@ -101,11 +100,11 @@ class MessageImage extends StatelessWidget {
class MessageText extends StatelessWidget {
const MessageText({
- Key key,
- @required this.alignment,
- @required this.color,
- @required this.message,
- @required this.messageColor,
+ Key? key,
+ required this.alignment,
+ required this.color,
+ required this.message,
+ required this.messageColor,
}) : super(key: key);
final Alignment alignment;
@@ -133,7 +132,7 @@ class MessageText extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Text(
- message.text,
+ message.text!,
style: TextStyle(color: messageColor),
),
),
diff --git a/packages/imessage/lib/utils.dart b/packages/imessage/lib/utils.dart
index f78e450..6487b35 100644
--- a/packages/imessage/lib/utils.dart
+++ b/packages/imessage/lib/utils.dart
@@ -26,44 +26,45 @@ bool isSameWeek(DateTime timestamp) =>
DateTime.now().difference(timestamp).inDays < 7;
class CupertinoCircleAvatar extends StatelessWidget {
- final String url;
- final double size;
- const CupertinoCircleAvatar({Key key, this.url, this.size}) : super(key: key);
+ final String? url;
+ final double? size;
+ const CupertinoCircleAvatar({Key? key, this.url, this.size})
+ : super(key: key);
@override
Widget build(BuildContext context) {
return ClipRRect(
- borderRadius: BorderRadius.circular(size / 2),
+ borderRadius: BorderRadius.circular(size! / 2),
child: CachedNetworkImage(
- imageUrl: url,
+ imageUrl: url!,
height: size,
width: size,
fit: BoxFit.cover,
errorWidget: (context, url, error) {
//TODO: this crash the app when getting 404 and in debug mode, see :https://github.com/Baseflow/flutter_cached_network_image/issues/504
return CachedNetworkImage(
- imageUrl:
- "https://4.bp.blogspot.com/-Jx21kNqFSTU/UXemtqPhZCI/AAAAAAAAh74/BMGSzpU6F48/s1600/funny-cat-pictures-047-001.jpg");
+ imageUrl:
+ 'https://4.bp.blogspot.com/-Jx21kNqFSTU/UXemtqPhZCI/AAAAAAAAh74/BMGSzpU6F48/s1600/funny-cat-pictures-047-001.jp',
+ );
}),
);
}
}
-
class Divider extends StatelessWidget {
const Divider({
- Key key,
+ Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Expanded(
child: Align(
+ alignment: Alignment.bottomCenter,
child: Container(
height: 1,
color: CupertinoColors.systemGrey5,
),
- alignment: Alignment.bottomCenter,
),
);
}
diff --git a/packages/imessage/pubspec.yaml b/packages/imessage/pubspec.yaml
index 62e7507..9d05f4c 100644
--- a/packages/imessage/pubspec.yaml
+++ b/packages/imessage/pubspec.yaml
@@ -18,16 +18,16 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
- sdk: ">=2.7.0 <3.0.0"
+ sdk: '>=2.12.0 <3.0.0'
dependencies:
flutter:
sdk: flutter
- intl: ^0.16.1
- stream_chat_flutter: ^1.3.0-beta
- animations: ^1.0.0+5
- collection: ^1.14.13
- cached_network_image: ^2.0.0-rc
+ intl: ^0.17.0
+ stream_chat_flutter: ^2.0.0-nullsafety.3
+ animations: ^2.0.0
+ collection: ^1.15.0
+ cached_network_image: ^3.0.0
# The following adds the Cupertino Icons font to your application.
diff --git a/packages/stream_chat_v1/android/gradle/wrapper/gradle-wrapper.properties b/packages/stream_chat_v1/android/gradle/wrapper/gradle-wrapper.properties
index 4a4c204..c0a81d1 100644
--- a/packages/stream_chat_v1/android/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/stream_chat_v1/android/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
diff --git a/packages/stream_chat_v1/ios/Gemfile.lock b/packages/stream_chat_v1/ios/Gemfile.lock
index d4fa45b..06d690f 100644
--- a/packages/stream_chat_v1/ios/Gemfile.lock
+++ b/packages/stream_chat_v1/ios/Gemfile.lock
@@ -2,21 +2,21 @@ GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.3)
- addressable (2.7.0)
+ addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.1.1)
- aws-partitions (1.437.0)
- aws-sdk-core (3.113.1)
+ aws-partitions (1.473.0)
+ aws-sdk-core (3.115.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.239.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
- aws-sdk-kms (1.43.0)
+ aws-sdk-kms (1.44.0)
aws-sdk-core (~> 3, >= 3.112.0)
aws-sigv4 (~> 1.1)
- aws-sdk-s3 (1.93.0)
+ aws-sdk-s3 (1.96.1)
aws-sdk-core (~> 3, >= 3.112.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.1)
@@ -26,29 +26,40 @@ GEM
claide (1.0.3)
colored (1.2)
colored2 (3.1.2)
- commander-fastlane (4.4.6)
- highline (~> 1.7.2)
+ commander (4.6.0)
+ highline (~> 2.0.0)
declarative (0.0.20)
- declarative-option (0.1.0)
digest-crc (0.6.3)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.6)
emoji_regex (3.2.2)
- excon (0.79.0)
- faraday (1.3.0)
+ excon (0.83.0)
+ faraday (1.5.0)
+ faraday-em_http (~> 1.0)
+ faraday-em_synchrony (~> 1.0)
+ faraday-excon (~> 1.1)
+ faraday-httpclient (~> 1.0.1)
faraday-net_http (~> 1.0)
+ faraday-net_http_persistent (~> 1.1)
+ faraday-patron (~> 1.0)
multipart-post (>= 1.2, < 3)
- ruby2_keywords
+ ruby2_keywords (>= 0.0.4)
faraday-cookie_jar (0.0.7)
faraday (>= 0.8.0)
http-cookie (~> 1.0.0)
+ faraday-em_http (1.0.0)
+ faraday-em_synchrony (1.0.0)
+ faraday-excon (1.1.0)
+ faraday-httpclient (1.0.1)
faraday-net_http (1.0.1)
+ faraday-net_http_persistent (1.1.0)
+ faraday-patron (1.0.0)
faraday_middleware (1.0.0)
faraday (~> 1.0)
- fastimage (2.2.3)
- fastlane (2.179.0)
+ fastimage (2.2.4)
+ fastlane (2.187.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
artifactory (~> 3.0)
@@ -56,7 +67,7 @@ GEM
babosa (>= 1.0.3, < 2.0.0)
bundler (>= 1.12.0, < 3.0.0)
colored
- commander-fastlane (>= 4.4.6, < 5.0.0)
+ commander (~> 4.6)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 4.0)
excon (>= 0.71.0, < 1.0.0)
@@ -65,9 +76,10 @@ GEM
faraday_middleware (~> 1.0)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
- google-api-client (>= 0.37.0, < 0.39.0)
- google-cloud-storage (>= 1.15.0, < 2.0.0)
- highline (>= 1.7.2, < 2.0.0)
+ google-apis-androidpublisher_v3 (~> 0.1)
+ google-apis-playcustomapp_v1 (~> 0.1)
+ google-cloud-storage (~> 1.31)
+ highline (~> 2.0)
json (< 3.0.0)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
@@ -77,7 +89,6 @@ GEM
rubyzip (>= 2.0.0, < 3.0.0)
security (= 0.1.3)
simctl (~> 1.6.3)
- slack-notifier (>= 2.0.0, < 3.0.0)
terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (>= 1.4.5, < 2.0.0)
tty-screen (>= 0.6.3, < 1.0.0)
@@ -86,61 +97,56 @@ GEM
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
- fastlane-plugin-firebase_app_distribution (0.2.3)
+ fastlane-plugin-firebase_app_distribution (0.2.9)
gh_inspector (1.1.3)
- google-api-client (0.38.0)
+ google-apis-androidpublisher_v3 (0.8.0)
+ google-apis-core (>= 0.4, < 2.a)
+ google-apis-core (0.4.0)
addressable (~> 2.5, >= 2.5.1)
- googleauth (~> 0.9)
- httpclient (>= 2.8.1, < 3.0)
+ googleauth (>= 0.16.2, < 2.a)
+ httpclient (>= 2.8.1, < 3.a)
mini_mime (~> 1.0)
representable (~> 3.0)
- retriable (>= 2.0, < 4.0)
- signet (~> 0.12)
- google-apis-core (0.3.0)
- addressable (~> 2.5, >= 2.5.1)
- googleauth (~> 0.14)
- httpclient (>= 2.8.1, < 3.0)
- mini_mime (~> 1.0)
- representable (~> 3.0)
- retriable (>= 2.0, < 4.0)
+ retriable (>= 2.0, < 4.a)
rexml
- signet (~> 0.14)
webrick
- google-apis-iamcredentials_v1 (0.2.0)
- google-apis-core (~> 0.1)
- google-apis-storage_v1 (0.3.0)
- google-apis-core (~> 0.1)
+ google-apis-iamcredentials_v1 (0.6.0)
+ google-apis-core (>= 0.4, < 2.a)
+ google-apis-playcustomapp_v1 (0.5.0)
+ google-apis-core (>= 0.4, < 2.a)
+ google-apis-storage_v1 (0.6.0)
+ google-apis-core (>= 0.4, < 2.a)
google-cloud-core (1.6.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.5.0)
faraday (>= 0.17.3, < 2.0)
google-cloud-errors (1.1.0)
- google-cloud-storage (1.31.0)
+ google-cloud-storage (1.34.0)
addressable (~> 2.5)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.1)
- google-cloud-core (~> 1.2)
- googleauth (~> 0.9)
+ google-cloud-core (~> 1.6)
+ googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
- googleauth (0.16.0)
+ googleauth (0.16.2)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (~> 0.14)
- highline (1.7.10)
- http-cookie (1.0.3)
+ highline (2.0.3)
+ http-cookie (1.0.4)
domain_name (~> 0.5)
httpclient (2.8.3)
jmespath (1.4.0)
json (2.5.1)
- jwt (2.2.2)
+ jwt (2.2.3)
memoist (0.16.2)
mini_magick (4.11.0)
- mini_mime (1.0.3)
+ mini_mime (1.1.0)
multi_json (1.15.0)
multipart-post (2.0.0)
nanaimo (0.3.0)
@@ -148,16 +154,16 @@ GEM
os (1.1.1)
plist (3.6.0)
public_suffix (4.0.6)
- rake (13.0.3)
- representable (3.0.4)
+ rake (13.0.4)
+ representable (3.1.1)
declarative (< 0.1.0)
- declarative-option (< 0.2.0)
+ trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
- rexml (3.2.4)
+ rexml (3.2.5)
rouge (2.0.7)
ruby2_keywords (0.0.4)
- rubyzip (2.3.0)
+ rubyzip (2.3.2)
security (0.1.3)
signet (0.15.0)
addressable (~> 2.3)
@@ -167,10 +173,10 @@ GEM
simctl (1.6.8)
CFPropertyList
naturally
- slack-notifier (2.3.2)
terminal-notifier (2.0.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
+ trailblazer-option (0.1.1)
tty-cursor (0.7.1)
tty-screen (0.8.1)
tty-spinner (0.9.3)
@@ -182,12 +188,13 @@ GEM
unicode-display_width (1.7.0)
webrick (1.7.0)
word_wrap (1.0.0)
- xcodeproj (1.19.0)
+ xcodeproj (1.20.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.3.0)
+ rexml (~> 3.2.4)
xcpretty (0.3.0)
rouge (~> 2.0.7)
xcpretty-travis-formatter (1.0.1)
diff --git a/packages/stream_chat_v1/ios/Runner.xcodeproj/project.pbxproj b/packages/stream_chat_v1/ios/Runner.xcodeproj/project.pbxproj
index 8cd3ed0..417fb0b 100644
--- a/packages/stream_chat_v1/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/stream_chat_v1/ios/Runner.xcodeproj/project.pbxproj
@@ -245,8 +245,10 @@
"${BUILT_PRODUCTS_DIR}/DKImagePickerController/DKImagePickerController.framework",
"${BUILT_PRODUCTS_DIR}/DKPhotoGallery/DKPhotoGallery.framework",
"${BUILT_PRODUCTS_DIR}/FMDB/FMDB.framework",
+ "${BUILT_PRODUCTS_DIR}/Reachability/Reachability.framework",
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
"${BUILT_PRODUCTS_DIR}/SwiftyGif/SwiftyGif.framework",
+ "${BUILT_PRODUCTS_DIR}/connectivity_plus/connectivity_plus.framework",
"${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework",
"${BUILT_PRODUCTS_DIR}/flutter_app_badger/flutter_app_badger.framework",
"${BUILT_PRODUCTS_DIR}/flutter_keyboard_visibility/flutter_keyboard_visibility.framework",
@@ -273,8 +275,10 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKImagePickerController.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DKPhotoGallery.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FMDB.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reachability.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyGif.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity_plus.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_app_badger.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_keyboard_visibility.framework",
@@ -447,7 +451,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = io.getstream.flutter;
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = "match AdHoc io.getstream.flutter";
+ PROVISIONING_PROFILE_SPECIFIER = "match AdHoc io.getstream.flutter 1620032657";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@@ -586,7 +590,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = io.getstream.flutter;
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = "match AdHoc io.getstream.flutter";
+ PROVISIONING_PROFILE_SPECIFIER = "match AdHoc io.getstream.flutter 1620032657";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@@ -620,7 +624,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = io.getstream.flutter;
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = "match AdHoc io.getstream.flutter";
+ PROVISIONING_PROFILE_SPECIFIER = "match AdHoc io.getstream.flutter 1620032657";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
diff --git a/packages/stream_chat_v1/ios/fastlane/Fastfile b/packages/stream_chat_v1/ios/fastlane/Fastfile
index 91aacbf..2d46198 100644
--- a/packages/stream_chat_v1/ios/fastlane/Fastfile
+++ b/packages/stream_chat_v1/ios/fastlane/Fastfile
@@ -1,4 +1,4 @@
-fastlane_version "2.179.0"
+fastlane_version "2.187.0"
default_platform :ios
before_all do
@@ -62,7 +62,7 @@ platform :ios do
settings_to_override = {
:BUNDLE_IDENTIFIER => "io.getstream.flutter",
- :PROVISIONING_PROFILE_SPECIFIER => "match AppStore io.getstream.flutter"
+ :PROVISIONING_PROFILE_SPECIFIER => "match AppStore io.getstream.flutter 1620032657"
}
gym(
diff --git a/packages/stream_chat_v1/ios/fastlane/beta_gym_export_options.plist b/packages/stream_chat_v1/ios/fastlane/beta_gym_export_options.plist
index e88559a..3721528 100644
--- a/packages/stream_chat_v1/ios/fastlane/beta_gym_export_options.plist
+++ b/packages/stream_chat_v1/ios/fastlane/beta_gym_export_options.plist
@@ -7,7 +7,7 @@
provisioningProfiles
io.getstream.flutter
- match AdHoc io.getstream.flutter
+ match AdHoc io.getstream.flutter 1620032657
\ No newline at end of file
diff --git a/packages/stream_chat_v1/ios/fastlane/report.xml b/packages/stream_chat_v1/ios/fastlane/report.xml
index 42d2518..55d4f11 100644
--- a/packages/stream_chat_v1/ios/fastlane/report.xml
+++ b/packages/stream_chat_v1/ios/fastlane/report.xml
@@ -5,12 +5,12 @@
-
+
-
+
@@ -20,24 +20,22 @@
-
+
-
+
-
+
-
-
-
+
diff --git a/packages/stream_chat_v1/lib/advanced_options_page.dart b/packages/stream_chat_v1/lib/advanced_options_page.dart
index a654f3d..3fd9a95 100644
--- a/packages/stream_chat_v1/lib/advanced_options_page.dart
+++ b/packages/stream_chat_v1/lib/advanced_options_page.dart
@@ -1,3 +1,4 @@
+import 'package:example/home_page.dart';
import 'package:example/routes/routes.dart';
import 'package:example/stream_version.dart';
import 'package:flutter/material.dart';
@@ -17,13 +18,13 @@ class _AdvancedOptionsPageState extends State {
final _formKey = GlobalKey();
final TextEditingController _apiKeyController = TextEditingController();
- String _apiKeyError;
+ String? _apiKeyError;
final TextEditingController _userIdController = TextEditingController();
- String _userIdError;
+ String? _userIdError;
final TextEditingController _userTokenController = TextEditingController();
- String _userTokenError;
+ String? _userTokenError;
final TextEditingController _usernameController = TextEditingController();
@@ -32,22 +33,20 @@ class _AdvancedOptionsPageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
- backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
+ backgroundColor: StreamChatTheme.of(context).colorTheme.appBg,
appBar: AppBar(
- backgroundColor: StreamChatTheme.of(context).colorTheme.white,
+ backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg,
elevation: 1,
centerTitle: true,
brightness: Theme.of(context).brightness,
title: Text(
'Advanced Options',
- style: StreamChatTheme.of(context)
- .textTheme
- .headlineBold
- .copyWith(color: StreamChatTheme.of(context).colorTheme.black),
+ style: StreamChatTheme.of(context).textTheme.headlineBold.copyWith(
+ color: StreamChatTheme.of(context).colorTheme.textHighEmphasis),
),
leading: IconButton(
icon: StreamSvgIcon.left(
- color: StreamChatTheme.of(context).colorTheme.black,
+ color: StreamChatTheme.of(context).colorTheme.textHighEmphasis,
),
onPressed: () {
Navigator.pop(context);
@@ -73,7 +72,7 @@ class _AdvancedOptionsPageState extends State {
}
},
validator: (value) {
- if (value.isEmpty) {
+ if (value!.isEmpty) {
setState(() {
_apiKeyError =
'Please enter the Chat API Key'.toUpperCase();
@@ -84,7 +83,9 @@ class _AdvancedOptionsPageState extends State {
},
style: TextStyle(
fontSize: 14,
- color: StreamChatTheme.of(context).colorTheme.black,
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textHighEmphasis,
),
decoration: InputDecoration(
errorStyle: TextStyle(height: 0, fontSize: 0),
@@ -92,15 +93,16 @@ class _AdvancedOptionsPageState extends State {
fontSize: 14,
fontWeight: FontWeight.bold,
color: _apiKeyError != null
- ? StreamChatTheme.of(context).colorTheme.accentRed
- : StreamChatTheme.of(context).colorTheme.grey,
+ ? StreamChatTheme.of(context).colorTheme.accentError
+ : StreamChatTheme.of(context)
+ .colorTheme
+ .textLowEmphasis,
),
border: UnderlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none,
),
- fillColor:
- StreamChatTheme.of(context).colorTheme.whiteSmoke,
+ fillColor: StreamChatTheme.of(context).colorTheme.inputBg,
filled: true,
labelText: _apiKeyError != null
? 'CHAT API KEY: $_apiKeyError'
@@ -119,7 +121,7 @@ class _AdvancedOptionsPageState extends State {
}
},
validator: (value) {
- if (value.isEmpty) {
+ if (value!.isEmpty) {
setState(() {
_userIdError =
'Please enter the User ID'.toUpperCase();
@@ -130,7 +132,9 @@ class _AdvancedOptionsPageState extends State {
},
style: TextStyle(
fontSize: 14,
- color: StreamChatTheme.of(context).colorTheme.black,
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textHighEmphasis,
),
textInputAction: TextInputAction.next,
decoration: InputDecoration(
@@ -139,15 +143,16 @@ class _AdvancedOptionsPageState extends State {
fontWeight: FontWeight.bold,
fontSize: 14,
color: _userIdError != null
- ? StreamChatTheme.of(context).colorTheme.accentRed
- : StreamChatTheme.of(context).colorTheme.grey,
+ ? StreamChatTheme.of(context).colorTheme.accentError
+ : StreamChatTheme.of(context)
+ .colorTheme
+ .textLowEmphasis,
),
border: UnderlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none,
),
- fillColor:
- StreamChatTheme.of(context).colorTheme.whiteSmoke,
+ fillColor: StreamChatTheme.of(context).colorTheme.inputBg,
filled: true,
labelText: _userIdError != null
? 'USER ID: $_userIdError'
@@ -165,7 +170,7 @@ class _AdvancedOptionsPageState extends State {
},
controller: _userTokenController,
validator: (value) {
- if (value.isEmpty) {
+ if (value!.isEmpty) {
setState(() {
_userTokenError =
'Please enter the user token'.toUpperCase();
@@ -176,7 +181,9 @@ class _AdvancedOptionsPageState extends State {
},
style: TextStyle(
fontSize: 14,
- color: StreamChatTheme.of(context).colorTheme.black,
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textHighEmphasis,
),
textInputAction: TextInputAction.next,
decoration: InputDecoration(
@@ -185,15 +192,16 @@ class _AdvancedOptionsPageState extends State {
fontWeight: FontWeight.bold,
fontSize: 14,
color: _userTokenError != null
- ? StreamChatTheme.of(context).colorTheme.accentRed
- : StreamChatTheme.of(context).colorTheme.grey,
+ ? StreamChatTheme.of(context).colorTheme.accentError
+ : StreamChatTheme.of(context)
+ .colorTheme
+ .textLowEmphasis,
),
border: UnderlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none,
),
- fillColor:
- StreamChatTheme.of(context).colorTheme.whiteSmoke,
+ fillColor: StreamChatTheme.of(context).colorTheme.inputBg,
filled: true,
labelText: _userTokenError != null
? 'USER TOKEN: $_userTokenError'
@@ -208,34 +216,45 @@ class _AdvancedOptionsPageState extends State {
labelStyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
- color: StreamChatTheme.of(context).colorTheme.grey,
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textLowEmphasis,
),
border: UnderlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none,
),
- fillColor:
- StreamChatTheme.of(context).colorTheme.whiteSmoke,
+ fillColor: StreamChatTheme.of(context).colorTheme.inputBg,
filled: true,
labelText: 'Username (optional)',
),
),
Spacer(),
- RaisedButton(
- color: Theme.of(context).brightness == Brightness.light
- ? StreamChatTheme.of(context).colorTheme.accentBlue
- : Colors.white,
- elevation: 0,
- padding: const EdgeInsets.symmetric(vertical: 16),
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(26),
+ ElevatedButton(
+ style: ButtonStyle(
+ backgroundColor: MaterialStateProperty.all(
+ Theme.of(context).brightness == Brightness.light
+ ? StreamChatTheme.of(context)
+ .colorTheme
+ .accentPrimary
+ : Colors.white),
+ elevation: MaterialStateProperty.all(0),
+ padding: MaterialStateProperty.all(
+ const EdgeInsets.symmetric(vertical: 16)),
+ shape: MaterialStateProperty.all(
+ RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(26),
+ ),
+ ),
),
child: Text(
'Login',
style: TextStyle(
fontSize: 16,
color: Theme.of(context).brightness != Brightness.light
- ? StreamChatTheme.of(context).colorTheme.accentBlue
+ ? StreamChatTheme.of(context)
+ .colorTheme
+ .accentPrimary
: Colors.white,
),
),
@@ -243,7 +262,7 @@ class _AdvancedOptionsPageState extends State {
if (loading) {
return;
}
- if (_formKey.currentState.validate()) {
+ if (_formKey.currentState!.validate()) {
final apiKey = _apiKeyController.text;
final userId = _userIdController.text;
final userToken = _userTokenController.text;
@@ -261,7 +280,7 @@ class _AdvancedOptionsPageState extends State {
borderRadius: BorderRadius.circular(16),
color: StreamChatTheme.of(context)
.colorTheme
- .white,
+ .barsBg,
),
height: 100,
width: 100,
@@ -298,7 +317,6 @@ class _AdvancedOptionsPageState extends State {
key: kStreamToken,
value: userToken,
);
- await client.disconnect();
} catch (e) {
var errorText = 'Error connecting, retry';
if (e is Map) {
@@ -309,15 +327,14 @@ class _AdvancedOptionsPageState extends State {
_apiKeyError = errorText.toUpperCase();
});
loading = false;
- await client.disconnect();
return;
}
loading = false;
await Navigator.pushNamedAndRemoveUntil(
context,
- Routes.APP,
- ModalRoute.withName(Routes.APP),
- arguments: client,
+ Routes.HOME,
+ ModalRoute.withName(Routes.HOME),
+ arguments: HomePageArgs(client),
);
}
},
diff --git a/packages/stream_chat_v1/lib/channel_file_display_screen.dart b/packages/stream_chat_v1/lib/channel_file_display_screen.dart
new file mode 100644
index 0000000..2cffbc8
--- /dev/null
+++ b/packages/stream_chat_v1/lib/channel_file_display_screen.dart
@@ -0,0 +1,177 @@
+import 'package:flutter/material.dart';
+import 'package:stream_chat_flutter/stream_chat_flutter.dart';
+
+class ChannelFileDisplayScreen extends StatefulWidget {
+ /// The sorting used for the channels matching the filters.
+ /// Sorting is based on field and direction, multiple sorting options can be provided.
+ /// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count.
+ /// Direction can be ascending or descending.
+ final List? sortOptions;
+
+ /// Pagination parameters
+ /// limit: the number of users to return (max is 30)
+ /// offset: the offset (max is 1000)
+ /// message_limit: how many messages should be included to each channel
+ final PaginationParams? paginationParams;
+
+ /// The builder used when the file list is empty.
+ final WidgetBuilder? emptyBuilder;
+
+ const ChannelFileDisplayScreen({
+ this.sortOptions,
+ this.paginationParams,
+ this.emptyBuilder,
+ });
+
+ @override
+ _ChannelFileDisplayScreenState createState() =>
+ _ChannelFileDisplayScreenState();
+}
+
+class _ChannelFileDisplayScreenState extends State {
+ @override
+ void initState() {
+ super.initState();
+ final messageSearchBloc = MessageSearchBloc.of(context);
+ messageSearchBloc.search(
+ filter: Filter.in_(
+ 'cid',
+ [StreamChannel.of(context).channel.cid!],
+ ),
+ messageFilter: Filter.in_(
+ 'attachments.type',
+ ['file'],
+ ),
+ sort: widget.sortOptions,
+ pagination: widget.paginationParams,
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg,
+ appBar: AppBar(
+ brightness: Theme.of(context).brightness,
+ elevation: 1,
+ centerTitle: true,
+ title: Text(
+ 'Files',
+ style: TextStyle(
+ color: StreamChatTheme.of(context).colorTheme.textHighEmphasis,
+ fontSize: 16.0),
+ ),
+ leading: Center(
+ child: InkWell(
+ onTap: () {
+ Navigator.of(context).pop();
+ },
+ child: Container(
+ width: 24.0,
+ height: 24.0,
+ child: StreamSvgIcon.left(
+ color: StreamChatTheme.of(context).colorTheme.textHighEmphasis,
+ size: 24.0,
+ ),
+ ),
+ ),
+ ),
+ backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg,
+ ),
+ body: _buildMediaGrid(),
+ );
+ }
+
+ Widget _buildMediaGrid() {
+ final messageSearchBloc = MessageSearchBloc.of(context);
+
+ return StreamBuilder>(
+ builder: (context, snapshot) {
+ if (snapshot.data == null) {
+ return Center(
+ child: const CircularProgressIndicator(),
+ );
+ }
+
+ if (snapshot.data!.isEmpty) {
+ if (widget.emptyBuilder != null) {
+ return widget.emptyBuilder!(context);
+ }
+ return Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ StreamSvgIcon.files(
+ size: 136.0,
+ color: StreamChatTheme.of(context).colorTheme.disabled,
+ ),
+ SizedBox(height: 16.0),
+ Text(
+ 'No Files',
+ style: TextStyle(
+ fontSize: 14.0,
+ color:
+ StreamChatTheme.of(context).colorTheme.textHighEmphasis,
+ ),
+ ),
+ SizedBox(height: 8.0),
+ Text(
+ 'Files sent in this chat will appear here',
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 14.0,
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textHighEmphasis
+ .withOpacity(0.5),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ final media = {};
+
+ for (var item in snapshot.data!) {
+ item.message.attachments.where((e) => e.type == 'file').forEach((e) {
+ media[e] = item.message;
+ });
+ }
+
+ return LazyLoadScrollView(
+ onEndOfPage: () => messageSearchBloc.search(
+ filter: Filter.in_(
+ 'cid',
+ [StreamChannel.of(context).channel.cid!],
+ ),
+ messageFilter: Filter.in_(
+ 'attachments.type',
+ ['file'],
+ ),
+ sort: widget.sortOptions,
+ pagination: widget.paginationParams!.copyWith(
+ offset: messageSearchBloc.messageResponses?.length ?? 0,
+ ),
+ ),
+ child: ListView.builder(
+ itemBuilder: (context, position) {
+ return Padding(
+ padding: const EdgeInsets.all(1.0),
+ child: Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: FileAttachment(
+ message: media.values.toList()[position],
+ attachment: media.keys.toList()[position],
+ ),
+ ),
+ );
+ },
+ itemCount: media.length,
+ ),
+ );
+ },
+ stream: messageSearchBloc.messagesStream,
+ );
+ }
+}
diff --git a/packages/stream_chat_v1/lib/channel_list.dart b/packages/stream_chat_v1/lib/channel_list.dart
new file mode 100644
index 0000000..c31fabb
--- /dev/null
+++ b/packages/stream_chat_v1/lib/channel_list.dart
@@ -0,0 +1,199 @@
+import 'dart:async';
+
+import 'package:example/routes/routes.dart';
+import 'package:example/search_text_field.dart';
+import 'package:flutter/material.dart';
+import 'package:stream_chat_flutter/stream_chat_flutter.dart';
+
+import 'channel_page.dart';
+import 'chat_info_screen.dart';
+import 'group_info_screen.dart';
+
+class ChannelList extends StatefulWidget {
+ @override
+ _ChannelList createState() => _ChannelList();
+}
+
+class _ChannelList extends State {
+ TextEditingController? _controller;
+
+ String _channelQuery = '';
+
+ bool _isSearchActive = false;
+
+ Timer? _debounce;
+
+ void _channelQueryListener() {
+ if (_debounce?.isActive ?? false) _debounce!.cancel();
+ _debounce = Timer(const Duration(milliseconds: 350), () {
+ if (mounted) {
+ setState(() {
+ _channelQuery = _controller!.text;
+ _isSearchActive = _channelQuery.isNotEmpty;
+ });
+ }
+ });
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ _controller = TextEditingController()..addListener(_channelQueryListener);
+ }
+
+ @override
+ void dispose() {
+ _controller?.removeListener(_channelQueryListener);
+ _controller?.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final user = StreamChat.of(context).user;
+ return WillPopScope(
+ onWillPop: () async {
+ if (_isSearchActive) {
+ _controller!.clear();
+ setState(() => _isSearchActive = false);
+ return false;
+ }
+ return true;
+ },
+ child: NestedScrollView(
+ floatHeaderSlivers: true,
+ headerSliverBuilder: (_, __) => [
+ SliverToBoxAdapter(
+ child: SearchTextField(
+ controller: _controller,
+ showCloseButton: _isSearchActive,
+ ),
+ ),
+ ],
+ body: AnimatedSwitcher(
+ duration: const Duration(milliseconds: 350),
+ child: GestureDetector(
+ behavior: HitTestBehavior.opaque,
+ onPanDown: (_) => FocusScope.of(context).unfocus(),
+ child: _isSearchActive
+ ? MessageSearchBloc(
+ child: MessageSearchListView(
+ showErrorTile: true,
+ messageQuery: _channelQuery,
+ filters: Filter.in_('members', [user!.id]),
+ sortOptions: [
+ SortOption(
+ 'created_at',
+ direction: SortOption.ASC,
+ ),
+ ],
+ pullToRefresh: false,
+ paginationParams: PaginationParams(limit: 20),
+ emptyBuilder: (_) {
+ return LayoutBuilder(
+ builder: (context, viewportConstraints) {
+ return SingleChildScrollView(
+ physics: AlwaysScrollableScrollPhysics(),
+ child: ConstrainedBox(
+ constraints: BoxConstraints(
+ minHeight: viewportConstraints.maxHeight,
+ ),
+ child: Center(
+ child: Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(24),
+ child: StreamSvgIcon.search(
+ size: 96,
+ color: Colors.grey,
+ ),
+ ),
+ Text(
+ 'No results...',
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ },
+ );
+ },
+ onItemTap: (messageResponse) async {
+ FocusScope.of(context).requestFocus(FocusNode());
+ final client = StreamChat.of(context).client;
+ final message = messageResponse.message;
+ final channel = client.channel(
+ messageResponse.channel!.type,
+ id: messageResponse.channel!.id,
+ );
+ if (channel.state == null) {
+ await channel.watch();
+ }
+ Navigator.pushNamed(
+ context,
+ Routes.CHANNEL_PAGE,
+ arguments: ChannelPageArgs(
+ channel: channel,
+ initialMessage: message,
+ ),
+ );
+ },
+ ),
+ )
+ : ChannelsBloc(
+ child: ChannelListView(
+ onStartChatPressed: () {
+ Navigator.pushNamed(context, Routes.NEW_CHAT);
+ },
+ swipeToAction: true,
+ filter: Filter.in_('members', [user!.id]),
+ presence: true,
+ pagination: PaginationParams(
+ limit: 20,
+ ),
+ channelWidget: ChannelPage(),
+ onViewInfoTap: (channel) {
+ Navigator.pop(context);
+ if (channel.memberCount == 2 && channel.isDistinct) {
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => StreamChannel(
+ channel: channel,
+ child: ChatInfoScreen(
+ messageTheme: StreamChatTheme.of(context)
+ .ownMessageTheme,
+ user: channel.state!.members
+ .where((m) =>
+ m.userId !=
+ channel.client.state.user!.id)
+ .first
+ .user,
+ ),
+ ),
+ ),
+ );
+ } else {
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => StreamChannel(
+ channel: channel,
+ child: GroupInfoScreen(
+ messageTheme: StreamChatTheme.of(context)
+ .ownMessageTheme,
+ ),
+ ),
+ ),
+ );
+ }
+ },
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/stream_chat_v1/lib/channel_list_page.dart b/packages/stream_chat_v1/lib/channel_list_page.dart
new file mode 100644
index 0000000..5379062
--- /dev/null
+++ b/packages/stream_chat_v1/lib/channel_list_page.dart
@@ -0,0 +1,284 @@
+import 'dart:async';
+
+import 'package:example/routes/routes.dart';
+import 'package:example/user_mentions_page.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_app_badger/flutter_app_badger.dart';
+import 'package:flutter_secure_storage/flutter_secure_storage.dart';
+import 'package:stream_chat_flutter/stream_chat_flutter.dart';
+import 'package:streaming_shared_preferences/streaming_shared_preferences.dart';
+
+import 'channel_list.dart';
+
+class ChannelListPage extends StatefulWidget {
+ const ChannelListPage({
+ Key? key,
+ }) : super(key: key);
+
+ @override
+ _ChannelListPageState createState() => _ChannelListPageState();
+}
+
+class _ChannelListPageState extends State {
+ int _currentIndex = 0;
+
+ bool _isSelected(int index) => _currentIndex == index;
+
+ List get _navBarItems {
+ return [
+ BottomNavigationBarItem(
+ icon: Stack(
+ clipBehavior: Clip.none,
+ children: [
+ StreamSvgIcon.message(
+ color: _isSelected(0)
+ ? StreamChatTheme.of(context).colorTheme.textHighEmphasis
+ : Colors.grey,
+ ),
+ Positioned(
+ top: -3,
+ right: -16,
+ child: UnreadIndicator(),
+ ),
+ ],
+ ),
+ label: 'Chats',
+ ),
+ BottomNavigationBarItem(
+ icon: Stack(
+ clipBehavior: Clip.none,
+ children: [
+ StreamSvgIcon.mentions(
+ color: _isSelected(1)
+ ? StreamChatTheme.of(context).colorTheme.textHighEmphasis
+ : Colors.grey,
+ ),
+ ],
+ ),
+ label: 'Mentions',
+ ),
+ ];
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final user = StreamChat.of(context).user;
+ if (user == null) {
+ return Offstage();
+ }
+ return Scaffold(
+ backgroundColor: StreamChatTheme.of(context).colorTheme.appBg,
+ appBar: ChannelListHeader(
+ onNewChatButtonTap: () {
+ Navigator.pushNamed(context, Routes.NEW_CHAT);
+ },
+ preNavigationCallback: () {
+ FocusScope.of(context).requestFocus(FocusNode());
+ },
+ ),
+ drawer: LeftDrawer(
+ user: user,
+ ),
+ drawerEdgeDragWidth: 50,
+ bottomNavigationBar: BottomNavigationBar(
+ backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg,
+ currentIndex: _currentIndex,
+ items: _navBarItems,
+ selectedLabelStyle: StreamChatTheme.of(context).textTheme.footnoteBold,
+ unselectedLabelStyle:
+ StreamChatTheme.of(context).textTheme.footnoteBold,
+ type: BottomNavigationBarType.fixed,
+ selectedItemColor:
+ StreamChatTheme.of(context).colorTheme.textHighEmphasis,
+ unselectedItemColor: Colors.grey,
+ onTap: (index) {
+ setState(() => _currentIndex = index);
+ },
+ ),
+ body: IndexedStack(
+ index: _currentIndex,
+ children: [
+ ChannelList(),
+ UserMentionsPage(),
+ ],
+ ),
+ );
+ }
+
+ StreamSubscription? badgeListener;
+
+ @override
+ void initState() {
+ if (!kIsWeb) {
+ badgeListener = StreamChat.of(context)
+ .client
+ .state
+ .totalUnreadCountStream
+ .listen((count) {
+ if (count > 0) {
+ FlutterAppBadger.updateBadgeCount(count);
+ } else {
+ FlutterAppBadger.removeBadge();
+ }
+ });
+ }
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ badgeListener?.cancel();
+ super.dispose();
+ }
+}
+
+class LeftDrawer extends StatelessWidget {
+ const LeftDrawer({
+ Key? key,
+ required this.user,
+ }) : super(key: key);
+
+ final User user;
+
+ @override
+ Widget build(BuildContext context) {
+ return Drawer(
+ child: Container(
+ color: StreamChatTheme.of(context).colorTheme.barsBg,
+ child: SafeArea(
+ child: Padding(
+ padding: EdgeInsets.only(
+ top: MediaQuery.of(context).viewPadding.top + 8,
+ ),
+ child: Column(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(
+ bottom: 20.0,
+ left: 8,
+ ),
+ child: Row(
+ children: [
+ UserAvatar(
+ user: user,
+ showOnlineStatus: false,
+ constraints: BoxConstraints.tight(Size.fromRadius(20)),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(left: 16.0),
+ child: Text(
+ user.name,
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ListTile(
+ leading: StreamSvgIcon.penWrite(
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textHighEmphasis
+ .withOpacity(.5),
+ ),
+ onTap: () {
+ Navigator.popAndPushNamed(
+ context,
+ Routes.NEW_CHAT,
+ );
+ },
+ title: Text(
+ 'New direct message',
+ style: TextStyle(
+ fontSize: 14.5,
+ ),
+ ),
+ ),
+ ListTile(
+ leading: StreamSvgIcon.contacts(
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textHighEmphasis
+ .withOpacity(.5),
+ ),
+ onTap: () {
+ Navigator.popAndPushNamed(
+ context,
+ Routes.NEW_GROUP_CHAT,
+ );
+ },
+ title: Text(
+ 'New group',
+ style: TextStyle(
+ fontSize: 14.5,
+ ),
+ ),
+ ),
+ Expanded(
+ child: Container(
+ alignment: Alignment.bottomCenter,
+ child: ListTile(
+ onTap: () async {
+ Navigator.pop(context);
+
+ if (!kIsWeb) {
+ final secureStorage = FlutterSecureStorage();
+ await secureStorage.deleteAll();
+ }
+
+ final client = StreamChat.of(context).client;
+ client.disconnectUser();
+ await client.dispose();
+
+ await Navigator.of(
+ context,
+ rootNavigator: true,
+ ).pushNamedAndRemoveUntil(
+ Routes.CHOOSE_USER,
+ ModalRoute.withName(Routes.CHOOSE_USER),
+ );
+ },
+ leading: StreamSvgIcon.user(
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textHighEmphasis
+ .withOpacity(.5),
+ ),
+ title: Text(
+ 'Sign out',
+ style: TextStyle(
+ fontSize: 14.5,
+ ),
+ ),
+ trailing: IconButton(
+ icon: StreamSvgIcon.iconMoon(
+ size: 24,
+ ),
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textLowEmphasis,
+ onPressed: () async {
+ final sp = await StreamingSharedPreferences.instance;
+ sp.setInt(
+ 'theme',
+ Theme.of(context).brightness == Brightness.dark
+ ? 1
+ : -1,
+ );
+ },
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/stream_chat_v1/lib/channel_media_display_screen.dart b/packages/stream_chat_v1/lib/channel_media_display_screen.dart
new file mode 100644
index 0000000..f2ad261
--- /dev/null
+++ b/packages/stream_chat_v1/lib/channel_media_display_screen.dart
@@ -0,0 +1,235 @@
+import 'package:flutter/material.dart';
+import 'package:stream_chat_flutter/stream_chat_flutter.dart';
+import 'package:video_player/video_player.dart';
+
+class ChannelMediaDisplayScreen extends StatefulWidget {
+ /// The sorting used for the channels matching the filters.
+ /// Sorting is based on field and direction, multiple sorting options can be provided.
+ /// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count.
+ /// Direction can be ascending or descending.
+ final List? sortOptions;
+
+ /// Pagination parameters
+ /// limit: the number of users to return (max is 30)
+ /// offset: the offset (max is 1000)
+ /// message_limit: how many messages should be included to each channel
+ final PaginationParams? paginationParams;
+
+ /// The builder used when the file list is empty.
+ final WidgetBuilder? emptyBuilder;
+
+ final ShowMessageCallback? onShowMessage;
+
+ final MessageTheme messageTheme;
+
+ const ChannelMediaDisplayScreen({
+ required this.messageTheme,
+ this.sortOptions,
+ this.paginationParams,
+ this.emptyBuilder,
+ this.onShowMessage,
+ });
+
+ @override
+ _ChannelMediaDisplayScreenState createState() =>
+ _ChannelMediaDisplayScreenState();
+}
+
+class _ChannelMediaDisplayScreenState extends State {
+ Map controllerCache = {};
+
+ @override
+ void initState() {
+ super.initState();
+ final messageSearchBloc = MessageSearchBloc.of(context);
+ messageSearchBloc.search(
+ filter: Filter.in_(
+ 'cid',
+ [StreamChannel.of(context).channel.cid!],
+ ),
+ messageFilter: Filter.in_(
+ 'attachments.type',
+ ['image', 'video'],
+ ),
+ sort: widget.sortOptions,
+ pagination: widget.paginationParams,
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg,
+ appBar: AppBar(
+ brightness: Theme.of(context).brightness,
+ elevation: 1,
+ centerTitle: true,
+ title: Text(
+ 'Photos & Videos',
+ style: TextStyle(
+ color: StreamChatTheme.of(context).colorTheme.textHighEmphasis,
+ fontSize: 16.0,
+ ),
+ ),
+ leading: StreamBackButton(),
+ backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg,
+ ),
+ body: _buildMediaGrid(),
+ );
+ }
+
+ Widget _buildMediaGrid() {
+ final messageSearchBloc = MessageSearchBloc.of(context);
+
+ return StreamBuilder>(
+ builder: (context, snapshot) {
+ if (snapshot.data == null) {
+ return Center(
+ child: const CircularProgressIndicator(),
+ );
+ }
+
+ if (snapshot.data!.isEmpty) {
+ if (widget.emptyBuilder != null) {
+ return widget.emptyBuilder!(context);
+ }
+ return Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ StreamSvgIcon.pictures(
+ size: 136.0,
+ color: StreamChatTheme.of(context).colorTheme.disabled,
+ ),
+ SizedBox(height: 16.0),
+ Text(
+ 'No Media',
+ style: TextStyle(
+ fontSize: 14.0,
+ color:
+ StreamChatTheme.of(context).colorTheme.textHighEmphasis,
+ ),
+ ),
+ SizedBox(height: 8.0),
+ Text(
+ 'Photos or video sent in this chat will \nappear here',
+ textAlign: TextAlign.center,
+ style: TextStyle(
+ fontSize: 14.0,
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textHighEmphasis
+ .withOpacity(0.5),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ final media = <_AssetPackage>[];
+
+ for (var item in snapshot.data!) {
+ item.message.attachments
+ .where((e) =>
+ (e.type == 'image' || e.type == 'video') &&
+ e.ogScrapeUrl == null)
+ .forEach((e) {
+ VideoPlayerController? controller;
+ if (e.type == 'video') {
+ var cachedController = controllerCache[e.assetUrl];
+
+ if (cachedController == null) {
+ controller = VideoPlayerController.network(e.assetUrl!);
+ controller.initialize();
+ controllerCache[e.assetUrl] = controller;
+ } else {
+ controller = cachedController;
+ }
+ }
+ media.add(_AssetPackage(e, item.message, controller));
+ });
+ }
+
+ return LazyLoadScrollView(
+ onEndOfPage: () => messageSearchBloc.search(
+ filter: Filter.in_(
+ 'cid',
+ [StreamChannel.of(context).channel.cid!],
+ ),
+ messageFilter: Filter.in_(
+ 'attachments.type',
+ ['image', 'video'],
+ ),
+ sort: widget.sortOptions,
+ pagination: widget.paginationParams!.copyWith(
+ offset: messageSearchBloc.messageResponses?.length ?? 0,
+ ),
+ ),
+ child: GridView.builder(
+ gridDelegate:
+ SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
+ itemBuilder: (context, position) {
+ var channel = StreamChannel.of(context).channel;
+ return Padding(
+ padding: const EdgeInsets.all(1.0),
+ child: InkWell(
+ onTap: () {
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => StreamChannel(
+ channel: channel,
+ child: FullScreenMedia(
+ mediaAttachments:
+ media.map((e) => e.attachment).toList(),
+ startIndex: position,
+ message: media[position].message,
+ userName: media[position].message.user!.name,
+ onShowMessage: widget.onShowMessage,
+ ),
+ ),
+ ),
+ );
+ },
+ child: media[position].attachment.type == 'image'
+ ? IgnorePointer(
+ child: ImageAttachment(
+ attachment: media[position].attachment,
+ message: media[position].message,
+ showTitle: false,
+ size: Size(
+ MediaQuery.of(context).size.width * 0.8,
+ MediaQuery.of(context).size.height * 0.3,
+ ),
+ messageTheme: widget.messageTheme,
+ ),
+ )
+ : VideoPlayer(media[position].videoPlayer!),
+ ),
+ );
+ },
+ itemCount: media.length,
+ ),
+ );
+ },
+ stream: messageSearchBloc.messagesStream,
+ );
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ for (var c in controllerCache.values) {
+ c!.dispose();
+ }
+ }
+}
+
+class _AssetPackage {
+ Attachment attachment;
+ Message message;
+ VideoPlayerController? videoPlayer;
+
+ _AssetPackage(this.attachment, this.message, this.videoPlayer);
+}
diff --git a/packages/stream_chat_v1/lib/channel_page.dart b/packages/stream_chat_v1/lib/channel_page.dart
new file mode 100644
index 0000000..571a54d
--- /dev/null
+++ b/packages/stream_chat_v1/lib/channel_page.dart
@@ -0,0 +1,188 @@
+import 'package:collection/collection.dart';
+import 'package:example/routes/routes.dart';
+import 'package:example/thread_page.dart';
+import 'package:flutter/material.dart';
+import 'package:stream_chat_flutter/stream_chat_flutter.dart';
+
+import 'chat_info_screen.dart';
+import 'group_info_screen.dart';
+
+class ChannelPageArgs {
+ final Channel? channel;
+ final Message? initialMessage;
+
+ const ChannelPageArgs({
+ this.channel,
+ this.initialMessage,
+ });
+}
+
+class ChannelPage extends StatefulWidget {
+ final int? initialScrollIndex;
+ final double? initialAlignment;
+ final bool highlightInitialMessage;
+
+ const ChannelPage({
+ Key? key,
+ this.initialScrollIndex,
+ this.initialAlignment,
+ this.highlightInitialMessage = false,
+ }) : super(key: key);
+
+ @override
+ _ChannelPageState createState() => _ChannelPageState();
+}
+
+class _ChannelPageState extends State {
+ Message? _quotedMessage;
+ FocusNode? _focusNode;
+
+ @override
+ void initState() {
+ _focusNode = FocusNode();
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ _focusNode!.dispose();
+ super.dispose();
+ }
+
+ void _reply(Message message) {
+ setState(() => _quotedMessage = message);
+ WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
+ _focusNode!.requestFocus();
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: StreamChatTheme.of(context).colorTheme.appBg,
+ appBar: ChannelHeader(
+ showTypingIndicator: false,
+ onImageTap: () async {
+ var channel = StreamChannel.of(context).channel;
+
+ if (channel.memberCount == 2 && channel.isDistinct) {
+ final currentUser = StreamChat.of(context).user;
+ final otherUser = channel.state!.members.firstWhereOrNull(
+ (element) => element.user!.id != currentUser!.id,
+ );
+ if (otherUser != null) {
+ final pop = await Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => StreamChannel(
+ child: ChatInfoScreen(
+ messageTheme: StreamChatTheme.of(context).ownMessageTheme,
+ user: otherUser.user,
+ ),
+ channel: channel,
+ ),
+ ),
+ );
+
+ if (pop == true) {
+ Navigator.pop(context);
+ }
+ }
+ } else {
+ await Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => StreamChannel(
+ child: GroupInfoScreen(
+ messageTheme: StreamChatTheme.of(context).ownMessageTheme,
+ ),
+ channel: channel,
+ ),
+ ),
+ );
+ }
+ },
+ ),
+ body: Column(
+ children: [
+ Expanded(
+ child: Stack(
+ children: [
+ MessageListView(
+ initialScrollIndex: widget.initialScrollIndex,
+ initialAlignment: widget.initialAlignment,
+ highlightInitialMessage: widget.highlightInitialMessage,
+ onMessageSwiped: _reply,
+ messageBuilder: (context, details, messages, defaultMessage) {
+ return defaultMessage.copyWith(
+ onReplyTap: _reply,
+ onShowMessage: (m, c) async {
+ final client = StreamChat.of(context).client;
+ final message = m;
+ final channel = client.channel(
+ c.type,
+ id: c.id,
+ );
+ if (channel.state == null) {
+ await channel.watch();
+ }
+ Navigator.pushReplacementNamed(
+ context,
+ Routes.CHANNEL_PAGE,
+ arguments: ChannelPageArgs(
+ channel: channel,
+ initialMessage: message,
+ ),
+ );
+ },
+ );
+ },
+ threadBuilder: (_, parentMessage) {
+ return ThreadPage(
+ parent: parentMessage,
+ );
+ },
+ pinPermissions: ['owner', 'admin', 'member'],
+ ),
+ Positioned(
+ bottom: 0,
+ left: 0,
+ right: 0,
+ child: Container(
+ alignment: Alignment.centerLeft,
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .appBg
+ .withOpacity(.9),
+ child: TypingIndicator(
+ alignment: Alignment.centerLeft,
+ padding: const EdgeInsets.symmetric(
+ horizontal: 8,
+ vertical: 4,
+ ),
+ style: StreamChatTheme.of(context)
+ .textTheme
+ .footnote
+ .copyWith(
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textLowEmphasis),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ MessageInput(
+ focusNode: _focusNode,
+ quotedMessage: _quotedMessage,
+ onQuotedMessageCleared: () {
+ setState(() => _quotedMessage = null);
+ _focusNode!.unfocus();
+ },
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/packages/stream_chat_v1/lib/chat_info_screen.dart b/packages/stream_chat_v1/lib/chat_info_screen.dart
index 5c8441e..6c6bb1e 100644
--- a/packages/stream_chat_v1/lib/chat_info_screen.dart
+++ b/packages/stream_chat_v1/lib/chat_info_screen.dart
@@ -1,24 +1,34 @@
+import 'package:collection/collection.dart' show IterableExtension;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:jiffy/jiffy.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
-import 'main.dart';
+import 'channel_file_display_screen.dart';
+import 'channel_media_display_screen.dart';
+import 'channel_page.dart';
+import 'pinned_messages_screen.dart';
import 'routes/routes.dart';
/// Detail screen for a 1:1 chat correspondence
class ChatInfoScreen extends StatefulWidget {
/// User in consideration
- final User user;
+ final User? user;
- const ChatInfoScreen({Key key, this.user}) : super(key: key);
+ final MessageTheme messageTheme;
+
+ const ChatInfoScreen({
+ Key? key,
+ required this.messageTheme,
+ this.user,
+ }) : super(key: key);
@override
_ChatInfoScreenState createState() => _ChatInfoScreenState();
}
class _ChatInfoScreenState extends State {
- ValueNotifier mutedBool = ValueNotifier(false);
+ ValueNotifier mutedBool = ValueNotifier(false);
@override
void initState() {
@@ -30,25 +40,25 @@ class _ChatInfoScreenState extends State {
Widget build(BuildContext context) {
final channel = StreamChannel.of(context).channel;
return Scaffold(
- backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
+ backgroundColor: StreamChatTheme.of(context).colorTheme.appBg,
body: ListView(
children: [
_buildUserHeader(),
Container(
height: 8.0,
- color: StreamChatTheme.of(context).colorTheme.greyGainsboro,
+ color: StreamChatTheme.of(context).colorTheme.disabled,
),
_buildOptionListTiles(),
Container(
height: 8.0,
- color: StreamChatTheme.of(context).colorTheme.greyGainsboro,
+ color: StreamChatTheme.of(context).colorTheme.disabled,
),
if ([
'admin',
'owner',
- ].contains(channel.state.members
- .firstWhere((m) => m.userId == channel.client.state.user.id,
- orElse: () => null)
+ ].contains(channel.state!.members
+ .firstWhereOrNull(
+ (m) => m.userId == channel.client.state.user!.id)
?.role))
_buildDeleteListTile(),
],
@@ -58,7 +68,7 @@ class _ChatInfoScreenState extends State {
Widget _buildUserHeader() {
return Material(
- color: StreamChatTheme.of(context).colorTheme.whiteSnow,
+ color: StreamChatTheme.of(context).colorTheme.appBg,
child: SafeArea(
child: Stack(
children: [
@@ -68,7 +78,7 @@ class _ChatInfoScreenState extends State {
Padding(
padding: const EdgeInsets.all(16.0),
child: UserAvatar(
- user: widget.user,
+ user: widget.user!,
constraints: BoxConstraints(
maxWidth: 72.0,
maxHeight: 72.0,
@@ -78,23 +88,23 @@ class _ChatInfoScreenState extends State {
),
),
Text(
- widget.user.name,
+ widget.user!.name,
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold),
),
SizedBox(height: 7.0),
_buildConnectedTitleState(),
SizedBox(height: 15.0),
OptionListTile(
- title: '@${widget.user.id}',
- tileColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
+ title: '@${widget.user!.id}',
+ tileColor: StreamChatTheme.of(context).colorTheme.appBg,
trailing: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
- widget.user.name,
+ widget.user!.name,
style: TextStyle(
color: StreamChatTheme.of(context)
.colorTheme
- .black
+ .textHighEmphasis
.withOpacity(0.5),
fontSize: 16.0),
),
@@ -124,7 +134,7 @@ class _ChatInfoScreenState extends State {
// title: 'Notifications',
// leading: StreamSvgIcon.Icon_notification(
// size: 24.0,
- // color: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
+ // color: StreamChatTheme.of(context).colorTheme.textHighEmphasis.withOpacity(0.5),
// ),
// trailing: CupertinoSwitch(
// value: true,
@@ -138,7 +148,7 @@ class _ChatInfoScreenState extends State {
mutedBool.value = snapshot.data;
return OptionListTile(
- tileColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
+ tileColor: StreamChatTheme.of(context).colorTheme.appBg,
title: 'Mute user',
titleTextStyle: StreamChatTheme.of(context).textTheme.body,
leading: Padding(
@@ -147,21 +157,21 @@ class _ChatInfoScreenState extends State {
size: 24.0,
color: StreamChatTheme.of(context)
.colorTheme
- .black
+ .textHighEmphasis
.withOpacity(0.5),
),
),
trailing: snapshot.data == null
? CircularProgressIndicator()
- : ValueListenableBuilder(
+ : ValueListenableBuilder(
valueListenable: mutedBool,
builder: (context, value, _) {
return CupertinoSwitch(
- value: value,
+ value: value!,
onChanged: (val) {
mutedBool.value = val;
- if (snapshot.data) {
+ if (snapshot.data!) {
channel.channel.unmute();
} else {
channel.channel.mute();
@@ -176,7 +186,7 @@ class _ChatInfoScreenState extends State {
// title: 'Block User',
// leading: StreamSvgIcon.Icon_user_delete(
// size: 24.0,
- // color: StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
+ // color: StreamChatTheme.of(context).colorTheme.textHighEmphasis.withOpacity(0.5),
// ),
// trailing: CupertinoSwitch(
// value: widget.user.banned,
@@ -190,20 +200,83 @@ class _ChatInfoScreenState extends State {
// ),
// onTap: () {},
// ),
+ OptionListTile(
+ title: 'Pinned Messages',
+ tileColor: StreamChatTheme.of(context).colorTheme.appBg,
+ titleTextStyle: StreamChatTheme.of(context).textTheme.body,
+ leading: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 22.0),
+ child: StreamSvgIcon.pin(
+ size: 24.0,
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textHighEmphasis
+ .withOpacity(0.5),
+ ),
+ ),
+ trailing: StreamSvgIcon.right(
+ color: StreamChatTheme.of(context).colorTheme.textLowEmphasis,
+ ),
+ onTap: () {
+ final channel = StreamChannel.of(context).channel;
+
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => StreamChannel(
+ channel: channel,
+ child: MessageSearchBloc(
+ child: PinnedMessagesScreen(
+ messageTheme: widget.messageTheme,
+ sortOptions: [
+ SortOption(
+ 'created_at',
+ direction: SortOption.ASC,
+ ),
+ ],
+ paginationParams: PaginationParams(limit: 20),
+ onShowMessage: (m, c) async {
+ final client = StreamChat.of(context).client;
+ final message = m;
+ final channel = client.channel(
+ c.type,
+ id: c.id,
+ );
+ if (channel.state == null) {
+ await channel.watch();
+ }
+ Navigator.pushNamed(
+ context,
+ Routes.CHANNEL_PAGE,
+ arguments: ChannelPageArgs(
+ channel: channel,
+ initialMessage: message,
+ ),
+ );
+ },
+ ),
+ ),
+ ),
+ ),
+ );
+ },
+ ),
OptionListTile(
title: 'Photos & Videos',
- tileColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
+ tileColor: StreamChatTheme.of(context).colorTheme.appBg,
titleTextStyle: StreamChatTheme.of(context).textTheme.body,
leading: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: StreamSvgIcon.pictures(
size: 36.0,
- color:
- StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textHighEmphasis
+ .withOpacity(0.5),
),
),
trailing: StreamSvgIcon.right(
- color: StreamChatTheme.of(context).colorTheme.grey,
+ color: StreamChatTheme.of(context).colorTheme.textLowEmphasis,
),
onTap: () {
final channel = StreamChannel.of(context).channel;
@@ -215,6 +288,7 @@ class _ChatInfoScreenState extends State {
channel: channel,
child: MessageSearchBloc(
child: ChannelMediaDisplayScreen(
+ messageTheme: widget.messageTheme,
sortOptions: [
SortOption(
'created_at',
@@ -250,18 +324,20 @@ class _ChatInfoScreenState extends State {
),
OptionListTile(
title: 'Files',
- tileColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
+ tileColor: StreamChatTheme.of(context).colorTheme.appBg,
titleTextStyle: StreamChatTheme.of(context).textTheme.body,
leading: Padding(
padding: const EdgeInsets.symmetric(horizontal: 18.0),
child: StreamSvgIcon.files(
size: 32.0,
- color:
- StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textHighEmphasis
+ .withOpacity(0.5),
),
),
trailing: StreamSvgIcon.right(
- color: StreamChatTheme.of(context).colorTheme.grey,
+ color: StreamChatTheme.of(context).colorTheme.textLowEmphasis,
),
onTap: () {
final channel = StreamChannel.of(context).channel;
@@ -289,18 +365,20 @@ class _ChatInfoScreenState extends State {
),
OptionListTile(
title: 'Shared groups',
- tileColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
+ tileColor: StreamChatTheme.of(context).colorTheme.appBg,
titleTextStyle: StreamChatTheme.of(context).textTheme.body,
leading: Padding(
padding: const EdgeInsets.symmetric(horizontal: 22.0),
child: StreamSvgIcon.iconGroup(
size: 24.0,
- color:
- StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5),
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textHighEmphasis
+ .withOpacity(0.5),
),
),
trailing: StreamSvgIcon.right(
- color: StreamChatTheme.of(context).colorTheme.grey,
+ color: StreamChatTheme.of(context).colorTheme.textLowEmphasis,
),
onTap: () {
Navigator.push(
@@ -317,21 +395,21 @@ class _ChatInfoScreenState extends State {
Widget _buildDeleteListTile() {
return OptionListTile(
title: 'Delete Conversation',
- tileColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
+ tileColor: StreamChatTheme.of(context).colorTheme.appBg,
titleTextStyle: StreamChatTheme.of(context).textTheme.body.copyWith(
- color: StreamChatTheme.of(context).colorTheme.accentRed,
+ color: StreamChatTheme.of(context).colorTheme.accentError,
),
leading: Padding(
padding: const EdgeInsets.symmetric(horizontal: 22.0),
child: StreamSvgIcon.delete(
- color: StreamChatTheme.of(context).colorTheme.accentRed,
+ color: StreamChatTheme.of(context).colorTheme.accentError,
size: 24.0,
),
),
onTap: () {
_showDeleteDialog();
},
- titleColor: StreamChatTheme.of(context).colorTheme.accentRed,
+ titleColor: StreamChatTheme.of(context).colorTheme.accentError,
);
}
@@ -343,7 +421,7 @@ class _ChatInfoScreenState extends State {
question: 'Are you sure you want to delete this conversation?',
cancelText: 'CANCEL',
icon: StreamSvgIcon.delete(
- color: StreamChatTheme.of(context).colorTheme.accentRed,
+ color: StreamChatTheme.of(context).colorTheme.accentError,
),
);
var channel = StreamChannel.of(context).channel;
@@ -367,7 +445,7 @@ class _ChatInfoScreenState extends State {
style: TextStyle(
color: StreamChatTheme.of(context)
.colorTheme
- .black
+ .textHighEmphasis
.withOpacity(0.5)),
);
} else {
@@ -376,7 +454,7 @@ class _ChatInfoScreenState extends State {
style: TextStyle(
color: StreamChatTheme.of(context)
.colorTheme
- .black
+ .textHighEmphasis
.withOpacity(0.5)),
);
}
@@ -385,7 +463,7 @@ class _ChatInfoScreenState extends State {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
- if (widget.user.online)
+ if (widget.user!.online)
Material(
type: MaterialType.circle,
child: Container(
@@ -396,13 +474,13 @@ class _ChatInfoScreenState extends State {
),
child: Material(
shape: CircleBorder(),
- color: StreamChatTheme.of(context).colorTheme.accentGreen,
+ color: StreamChatTheme.of(context).colorTheme.accentInfo,
),
),
- color: StreamChatTheme.of(context).colorTheme.white,
+ color: StreamChatTheme.of(context).colorTheme.barsBg,
),
alternativeWidget,
- if (widget.user.online)
+ if (widget.user!.online)
SizedBox(
width: 24.0,
),
@@ -412,8 +490,8 @@ class _ChatInfoScreenState extends State {
}
class _SharedGroupsScreen extends StatefulWidget {
- final User mainUser;
- final User otherUser;
+ final User? mainUser;
+ final User? otherUser;
_SharedGroupsScreen(this.mainUser, this.otherUser);
@@ -427,7 +505,7 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
var chat = StreamChat.of(context);
return Scaffold(
- backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
+ backgroundColor: StreamChatTheme.of(context).colorTheme.appBg,
appBar: AppBar(
brightness: Theme.of(context).brightness,
elevation: 1,
@@ -435,28 +513,18 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
title: Text(
'Shared Groups',
style: TextStyle(
- color: StreamChatTheme.of(context).colorTheme.black,
+ color: StreamChatTheme.of(context).colorTheme.textHighEmphasis,
fontSize: 16.0),
),
leading: StreamBackButton(),
- backgroundColor: StreamChatTheme.of(context).colorTheme.white,
+ backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg,
),
body: StreamBuilder>(
stream: chat.client.queryChannels(
- filter: {
- r'$and': [
- {
- 'members': {
- r'$in': [widget.otherUser.id],
- },
- },
- {
- 'members': {
- r'$in': [widget.mainUser.id],
- },
- }
- ],
- },
+ filter: Filter.and([
+ Filter.in_('members', [widget.otherUser!.id]),
+ Filter.in_('members', [widget.mainUser!.id]),
+ ]),
),
builder: (context, snapshot) {
if (!snapshot.hasData) {
@@ -465,21 +533,23 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
);
}
- if (snapshot.data.isEmpty) {
+ if (snapshot.data!.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
StreamSvgIcon.message(
size: 136.0,
- color: StreamChatTheme.of(context).colorTheme.greyGainsboro,
+ color: StreamChatTheme.of(context).colorTheme.disabled,
),
SizedBox(height: 16.0),
Text(
'No Shared Groups',
style: TextStyle(
fontSize: 14.0,
- color: StreamChatTheme.of(context).colorTheme.black,
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textHighEmphasis,
),
),
SizedBox(height: 8.0),
@@ -490,7 +560,7 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
fontSize: 14.0,
color: StreamChatTheme.of(context)
.colorTheme
- .black
+ .textHighEmphasis
.withOpacity(0.5),
),
),
@@ -499,11 +569,11 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
);
}
- final channels = snapshot.data
+ final channels = snapshot.data!
.where((c) =>
- c.state.members.any((m) =>
- m.userId != widget.mainUser.id &&
- m.userId != widget.otherUser.id) ||
+ c.state!.members.any((m) =>
+ m.userId != widget.mainUser!.id &&
+ m.userId != widget.otherUser!.id) ||
!c.isDistinct)
.toList();
@@ -523,24 +593,24 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
Widget _buildListTile(Channel channel) {
var extraData = channel.extraData;
- var members = channel.state.members;
+ var members = channel.state!.members;
var textStyle = TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold);
return Container(
height: 64.0,
child: LayoutBuilder(builder: (context, constraints) {
- String title;
+ String? title;
if (extraData['name'] == null) {
final otherMembers = members.where(
- (member) => member.userId != StreamChat.of(context).user.id);
+ (member) => member.userId != StreamChat.of(context).user!.id);
if (otherMembers.isNotEmpty) {
final maxWidth = constraints.maxWidth;
- final maxChars = maxWidth / textStyle.fontSize;
+ final maxChars = maxWidth / textStyle.fontSize!;
var currentChars = 0;
final currentMembers = [];
otherMembers.forEach((element) {
- final newLength = currentChars + element.user.name.length;
+ final newLength = currentChars + element.user!.name.length;
if (newLength < maxChars) {
currentChars = newLength;
currentMembers.add(element);
@@ -550,12 +620,12 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
final exceedingMembers =
otherMembers.length - currentMembers.length;
title =
- '${currentMembers.map((e) => e.user.name).join(', ')} ${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}';
+ '${currentMembers.map((e) => e.user!.name).join(', ')} ${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}';
} else {
title = 'No title';
}
} else {
- title = extraData['name'];
+ title = extraData['name'] as String;
}
return Column(
@@ -565,7 +635,7 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
children: [
Padding(
padding: const EdgeInsets.all(8.0),
- child: ChannelImage(
+ child: ChannelAvatar(
channel: channel,
constraints:
BoxConstraints(maxWidth: 40.0, maxHeight: 40.0),
@@ -583,7 +653,7 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
style: TextStyle(
color: StreamChatTheme.of(context)
.colorTheme
- .black
+ .textHighEmphasis
.withOpacity(0.5)),
),
)
@@ -592,8 +662,10 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
),
Container(
height: 1.0,
- color:
- StreamChatTheme.of(context).colorTheme.black.withOpacity(.08),
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textHighEmphasis
+ .withOpacity(.08),
),
],
);
diff --git a/packages/stream_chat_v1/lib/chips_input_text_field.dart b/packages/stream_chat_v1/lib/chips_input_text_field.dart
index 0fc0791..a67895e 100644
--- a/packages/stream_chat_v1/lib/chips_input_text_field.dart
+++ b/packages/stream_chat_v1/lib/chips_input_text_field.dart
@@ -6,18 +6,18 @@ typedef OnChipAdded = void Function(T chip);
typedef OnChipRemoved = void Function(T chip);
class ChipsInputTextField extends StatefulWidget {
- final TextEditingController controller;
- final FocusNode focusNode;
- final ValueChanged onInputChanged;
+ final TextEditingController? controller;
+ final FocusNode? focusNode;
+ final ValueChanged? onInputChanged;
final ChipBuilder chipBuilder;
- final OnChipAdded onChipAdded;
- final OnChipRemoved onChipRemoved;
+ final OnChipAdded? onChipAdded;
+ final OnChipRemoved? onChipRemoved;
final String hint;
const ChipsInputTextField({
- Key key,
- @required this.chipBuilder,
- @required this.controller,
+ Key? key,
+ required this.chipBuilder,
+ required this.controller,
this.onInputChanged,
this.focusNode,
this.onChipAdded,
@@ -35,7 +35,7 @@ class ChipInputTextFieldState extends State> {
void addItem(T item) {
setState(() => _chips.add(item));
- if (widget.onChipAdded != null) widget.onChipAdded(item);
+ if (widget.onChipAdded != null) widget.onChipAdded!(item);
}
void removeItem(T item) {
@@ -43,7 +43,7 @@ class ChipInputTextFieldState extends State> {
_chips.remove(item);
if (_chips.isEmpty) resumeItemAddition();
});
- if (widget.onChipRemoved != null) widget.onChipRemoved(item);
+ if (widget.onChipRemoved != null) widget.onChipRemoved!(item);
}
void pauseItemAddition() {
@@ -66,7 +66,7 @@ class ChipInputTextFieldState extends State> {
onTap: _pauseItemAddition ? resumeItemAddition : null,
child: Material(
elevation: 1,
- color: StreamChatTheme.of(context).colorTheme.white,
+ color: StreamChatTheme.of(context).colorTheme.barsBg,
child: Container(
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 16),
@@ -82,7 +82,7 @@ class ChipInputTextFieldState extends State> {
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
- .black
+ .textHighEmphasis
.withOpacity(.5)),
),
),
@@ -119,7 +119,7 @@ class ChipInputTextFieldState extends State> {
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
- .black
+ .textHighEmphasis
.withOpacity(.5)),
),
),
@@ -134,14 +134,14 @@ class ChipInputTextFieldState extends State> {
? StreamSvgIcon.user(
color: StreamChatTheme.of(context)
.colorTheme
- .black
+ .textHighEmphasis
.withOpacity(0.5),
size: 24,
)
: StreamSvgIcon.userAdd(
color: StreamChatTheme.of(context)
.colorTheme
- .black
+ .textHighEmphasis
.withOpacity(0.5),
size: 24,
),
diff --git a/packages/stream_chat_v1/lib/choose_user_page.dart b/packages/stream_chat_v1/lib/choose_user_page.dart
index a6dd5e1..e487acd 100644
--- a/packages/stream_chat_v1/lib/choose_user_page.dart
+++ b/packages/stream_chat_v1/lib/choose_user_page.dart
@@ -1,4 +1,5 @@
import 'package:example/app_config.dart';
+import 'package:example/home_page.dart';
import 'package:example/stream_version.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@@ -18,7 +19,7 @@ class ChooseUserPage extends StatelessWidget {
final users = defaultUsers;
return Scaffold(
- backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
+ backgroundColor: StreamChatTheme.of(context).colorTheme.appBg,
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
@@ -32,7 +33,7 @@ class ChooseUserPage extends StatelessWidget {
child: SvgPicture.asset(
'assets/logo.svg',
height: 40,
- color: StreamChatTheme.of(context).colorTheme.accentBlue,
+ color: StreamChatTheme.of(context).colorTheme.accentPrimary,
),
),
),
@@ -54,7 +55,7 @@ class ChooseUserPage extends StatelessWidget {
separatorBuilder: (context, i) {
return Container(
height: 1,
- color: StreamChatTheme.of(context).colorTheme.greyWhisper,
+ color: StreamChatTheme.of(context).colorTheme.borders,
);
},
itemCount: users.length + 1,
@@ -78,7 +79,7 @@ class ChooseUserPage extends StatelessWidget {
borderRadius: BorderRadius.circular(16),
color: StreamChatTheme.of(context)
.colorTheme
- .white,
+ .barsBg,
),
height: 100,
width: 100,
@@ -89,8 +90,11 @@ class ChooseUserPage extends StatelessWidget {
),
);
- final client = StreamChat.of(context).client;
- client.apiKey = kDefaultStreamApiKey;
+ final client = StreamChatClient(
+ kDefaultStreamApiKey,
+ logLevel: Level.INFO,
+ );
+
await client.connectUser(
user,
token,
@@ -115,6 +119,7 @@ class ChooseUserPage extends StatelessWidget {
context,
Routes.HOME,
ModalRoute.withName(Routes.HOME),
+ arguments: HomePageArgs(client),
);
},
leading: UserAvatar(
@@ -136,13 +141,13 @@ class ChooseUserPage extends StatelessWidget {
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
- .grey,
+ .textLowEmphasis,
),
),
trailing: StreamSvgIcon.arrowRight(
color: StreamChatTheme.of(context)
.colorTheme
- .accentBlue,
+ .accentPrimary,
),
);
}),
@@ -152,11 +157,12 @@ class ChooseUserPage extends StatelessWidget {
},
leading: CircleAvatar(
child: StreamSvgIcon.settings(
- color: StreamChatTheme.of(context).colorTheme.black,
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textHighEmphasis,
),
- backgroundColor: StreamChatTheme.of(context)
- .colorTheme
- .greyWhisper,
+ backgroundColor:
+ StreamChatTheme.of(context).colorTheme.borders,
),
title: Text(
'Advanced Options',
@@ -168,8 +174,9 @@ class ChooseUserPage extends StatelessWidget {
.textTheme
.footnote
.copyWith(
- color:
- StreamChatTheme.of(context).colorTheme.grey,
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textLowEmphasis,
),
),
trailing: SvgPicture.asset(
diff --git a/packages/stream_chat_v1/lib/group_chat_details_screen.dart b/packages/stream_chat_v1/lib/group_chat_details_screen.dart
index afc9d12..a2f51ed 100644
--- a/packages/stream_chat_v1/lib/group_chat_details_screen.dart
+++ b/packages/stream_chat_v1/lib/group_chat_details_screen.dart
@@ -2,15 +2,15 @@ import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:uuid/uuid.dart';
-import 'main.dart';
+import 'channel_page.dart';
import 'routes/routes.dart';
class GroupChatDetailsScreen extends StatefulWidget {
- final List selectedUsers;
+ final List? selectedUsers;
const GroupChatDetailsScreen({
- Key key,
- @required this.selectedUsers,
+ Key? key,
+ required this.selectedUsers,
}) : super(key: key);
@override
@@ -20,14 +20,14 @@ class GroupChatDetailsScreen extends StatefulWidget {
class _GroupChatDetailsScreenState extends State {
final _selectedUsers = [];
- TextEditingController _groupNameController;
+ TextEditingController? _groupNameController;
bool _isGroupNameEmpty = true;
int get _totalUsers => _selectedUsers.length;
void _groupNameListener() {
- final name = _groupNameController.text;
+ final name = _groupNameController!.text;
if (mounted) {
setState(() {
_isGroupNameEmpty = name.isEmpty;
@@ -38,7 +38,7 @@ class _GroupChatDetailsScreenState extends State {
@override
void initState() {
super.initState();
- _selectedUsers.addAll(widget.selectedUsers);
+ _selectedUsers.addAll(widget.selectedUsers!);
_groupNameController = TextEditingController()
..addListener(_groupNameListener);
}
@@ -59,16 +59,16 @@ class _GroupChatDetailsScreenState extends State {
return false;
},
child: Scaffold(
- backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
+ backgroundColor: StreamChatTheme.of(context).colorTheme.appBg,
appBar: AppBar(
brightness: Theme.of(context).brightness,
elevation: 1,
- backgroundColor: StreamChatTheme.of(context).colorTheme.white,
+ backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg,
leading: const StreamBackButton(),
title: Text(
'Name of Group Chat',
style: TextStyle(
- color: StreamChatTheme.of(context).colorTheme.black,
+ color: StreamChatTheme.of(context).colorTheme.textHighEmphasis,
fontSize: 16,
),
),
@@ -83,7 +83,9 @@ class _GroupChatDetailsScreenState extends State {
'NAME',
style: TextStyle(
fontSize: 12,
- color: StreamChatTheme.of(context).colorTheme.grey,
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textLowEmphasis,
),
),
SizedBox(width: 16),
@@ -101,7 +103,9 @@ class _GroupChatDetailsScreenState extends State {
hintText: 'Choose a group chat name',
hintStyle: TextStyle(
fontSize: 14,
- color: StreamChatTheme.of(context).colorTheme.grey,
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textLowEmphasis,
),
),
),
@@ -117,20 +121,20 @@ class _GroupChatDetailsScreenState extends State {
icon: StreamSvgIcon.check(
size: 24,
color: _isGroupNameEmpty
- ? StreamChatTheme.of(context).colorTheme.grey
- : StreamChatTheme.of(context).colorTheme.accentBlue,
+ ? StreamChatTheme.of(context).colorTheme.textLowEmphasis
+ : StreamChatTheme.of(context).colorTheme.accentPrimary,
),
onPressed: _isGroupNameEmpty
? null
: () async {
try {
- final groupName = _groupNameController.text;
+ final groupName = _groupNameController!.text;
final client = StreamChat.of(context).client;
final channel = client.channel('messaging',
id: Uuid().v4(),
extraData: {
'members': [
- client.state.user.id,
+ client.state.user!.id,
..._selectedUsers.map((e) => e.id),
],
'name': groupName,
@@ -188,7 +192,9 @@ class _GroupChatDetailsScreenState extends State {
child: Text(
'$_totalUsers ${_totalUsers > 1 ? 'Members' : 'Member'}',
style: TextStyle(
- color: StreamChatTheme.of(context).colorTheme.grey,
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textLowEmphasis,
),
),
),
@@ -201,9 +207,7 @@ class _GroupChatDetailsScreenState extends State {
itemCount: _selectedUsers.length + 1,
separatorBuilder: (_, __) => Container(
height: 1,
- color: StreamChatTheme.of(context)
- .colorTheme
- .greyWhisper,
+ color: StreamChatTheme.of(context).colorTheme.borders,
),
itemBuilder: (_, index) {
if (index == _selectedUsers.length) {
@@ -211,7 +215,7 @@ class _GroupChatDetailsScreenState extends State {
height: 1,
color: StreamChatTheme.of(context)
.colorTheme
- .greyWhisper,
+ .borders,
);
}
final user = _selectedUsers[index];
@@ -237,7 +241,7 @@ class _GroupChatDetailsScreenState extends State {
Icons.clear_rounded,
color: StreamChatTheme.of(context)
.colorTheme
- .black,
+ .textHighEmphasis,
),
padding: const EdgeInsets.all(0),
splashRadius: 24,
@@ -266,7 +270,8 @@ class _GroupChatDetailsScreenState extends State {
void _showErrorAlert() {
showModalBottomSheet(
- backgroundColor: StreamChatTheme.of(context).colorTheme.white,
+ useRootNavigator: false,
+ backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg,
context: context,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
@@ -281,7 +286,7 @@ class _GroupChatDetailsScreenState extends State {
height: 26.0,
),
StreamSvgIcon.error(
- color: StreamChatTheme.of(context).colorTheme.accentRed,
+ color: StreamChatTheme.of(context).colorTheme.accentError,
size: 24.0,
),
SizedBox(
@@ -299,14 +304,16 @@ class _GroupChatDetailsScreenState extends State {
height: 36.0,
),
Container(
- color:
- StreamChatTheme.of(context).colorTheme.black.withOpacity(.08),
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textHighEmphasis
+ .withOpacity(.08),
height: 1.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
- FlatButton(
+ TextButton(
child: Text(
'OK',
style: StreamChatTheme.of(context)
@@ -315,7 +322,7 @@ class _GroupChatDetailsScreenState extends State {
.copyWith(
color: StreamChatTheme.of(context)
.colorTheme
- .accentBlue),
+ .accentPrimary),
),
onPressed: () {
Navigator.of(context).pop();
diff --git a/packages/stream_chat_v1/lib/group_info_screen.dart b/packages/stream_chat_v1/lib/group_info_screen.dart
index b76cc9b..4193213 100644
--- a/packages/stream_chat_v1/lib/group_info_screen.dart
+++ b/packages/stream_chat_v1/lib/group_info_screen.dart
@@ -1,5 +1,6 @@
import 'dart:async';
+import 'package:collection/collection.dart' show IterableExtension;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:jiffy/jiffy.dart';
@@ -7,39 +8,49 @@ import 'package:stream_chat_flutter/src/option_list_tile.dart';
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
+import 'channel_file_display_screen.dart';
+import 'channel_media_display_screen.dart';
+import 'channel_page.dart';
import 'chat_info_screen.dart';
-import 'main.dart';
+import 'pinned_messages_screen.dart';
import 'routes/routes.dart';
class GroupInfoScreen extends StatefulWidget {
+ final MessageTheme messageTheme;
+
+ const GroupInfoScreen({
+ Key? key,
+ required this.messageTheme,
+ }) : super(key: key);
+
@override
_GroupInfoScreenState createState() => _GroupInfoScreenState();
}
class _GroupInfoScreenState extends State {
- TextEditingController _nameController;
+ TextEditingController? _nameController;
- TextEditingController _searchController;
+ TextEditingController? _searchController;
String _userNameQuery = '';
- Timer _debounce;
- Function modalSetStateCallback;
+ Timer? _debounce;
+ Function? modalSetStateCallback;
final FocusNode _focusNode = FocusNode();
bool listExpanded = false;
- ValueNotifier mutedBool = ValueNotifier(false);
+ ValueNotifier mutedBool = ValueNotifier(false);
void _userNameListener() {
- if (_searchController.text == _userNameQuery) {
+ if (_searchController!.text == _userNameQuery) {
return;
}
- if (_debounce?.isActive ?? false) _debounce.cancel();
+ if (_debounce?.isActive ?? false) _debounce!.cancel();
_debounce = Timer(const Duration(milliseconds: 350), () {
if (mounted && modalSetStateCallback != null) {
- modalSetStateCallback(() {
- _userNameQuery = _searchController.text;
+ modalSetStateCallback!(() {
+ _userNameQuery = _searchController!.text;
});
}
});
@@ -50,10 +61,12 @@ class _GroupInfoScreenState extends State {
super.initState();
var channel = StreamChannel.of(context);
_nameController = TextEditingController.fromValue(
- TextEditingValue(text: channel.channel.extraData['name'] ?? ''));
+ TextEditingValue(
+ text: (channel.channel.extraData['name'] as String?) ?? ''),
+ );
_searchController = TextEditingController()..addListener(_userNameListener);
- _nameController.addListener(() {
+ _nameController!.addListener(() {
setState(() {});
});
mutedBool = ValueNotifier(StreamChannel.of(context).channel.isMuted);
@@ -64,28 +77,27 @@ class _GroupInfoScreenState extends State {
var channel = StreamChannel.of(context);
return StreamBuilder>(
- stream: channel.channel.state.membersStream,
+ stream: channel.channel.state!.membersStream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Container(
- color: StreamChatTheme.of(context).colorTheme.greyGainsboro,
+ color: StreamChatTheme.of(context).colorTheme.disabled,
child: Center(child: CircularProgressIndicator()),
);
}
- var userMember = snapshot.data.firstWhere(
- (e) => e.user.id == StreamChat.of(context).user.id,
- orElse: () => null,
+ var userMember = snapshot.data!.firstWhereOrNull(
+ (e) => e.user!.id == StreamChat.of(context).user!.id,
);
var isOwner = userMember?.role == 'owner';
return Scaffold(
- backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
+ backgroundColor: StreamChatTheme.of(context).colorTheme.appBg,
appBar: AppBar(
brightness: Theme.of(context).brightness,
elevation: 1.0,
toolbarHeight: 56.0,
- backgroundColor: StreamChatTheme.of(context).colorTheme.white,
+ backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg,
leading: StreamBackButton(),
title: Column(
children: [
@@ -96,8 +108,9 @@ class _GroupInfoScreenState extends State {
return Text(
'Loading...',
style: TextStyle(
- color:
- StreamChatTheme.of(context).colorTheme.black,
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textHighEmphasis,
fontSize: 16,
),
maxLines: 1,
@@ -109,11 +122,13 @@ class _GroupInfoScreenState extends State {
_getChannelName(
2 * MediaQuery.of(context).size.width / 3,
members: snapshot.data,
- extraData: state.data.channel.extraData,
+ extraData: state.data!.channel!.extraData,
maxFontSize: 16.0,
- ),
+ )!,
style: TextStyle(
- color: StreamChatTheme.of(context).colorTheme.black,
+ color: StreamChatTheme.of(context)
+ .colorTheme
+ .textHighEmphasis,
fontSize: 16,
),
maxLines: 1,
@@ -124,11 +139,11 @@ class _GroupInfoScreenState extends State {
height: 3.0,
),
Text(
- '${channel.channel.memberCount} Members, ${snapshot?.data?.where((e) => e.user.online)?.length ?? 0} Online',
+ '${channel.channel.memberCount} Members, ${snapshot.data?.where((e) => e.user!.online).length ?? 0} Online',
style: TextStyle(
color: StreamChatTheme.of(context)
.colorTheme
- .black
+ .textHighEmphasis
.withOpacity(0.5),
fontSize: 12.0,
),
@@ -148,7 +163,7 @@ class _GroupInfoScreenState extends State {
child: StreamSvgIcon.userAdd(
color: StreamChatTheme.of(context)
.colorTheme
- .accentBlue),
+ .accentPrimary),
),
),
),
@@ -156,10 +171,10 @@ class _GroupInfoScreenState extends State {
),
body: ListView(
children: [
- _buildMembers(snapshot.data),
+ _buildMembers(snapshot.data!),
Container(
height: 8.0,
- color: StreamChatTheme.of(context).colorTheme.greyGainsboro,
+ color: StreamChatTheme.of(context).colorTheme.disabled,
),
if (isOwner) _buildNameTile(),
_buildOptionListTiles(),
@@ -195,9 +210,8 @@ class _GroupInfoScreenState extends State {
return Material(
child: InkWell(
onTap: () {
- final userMember = groupMembers.firstWhere(
- (e) => e.user.id == StreamChat.of(context).user.id,
- orElse: () => null,
+ final userMember = groupMembers.firstWhereOrNull(
+ (e) => e.user!.id == StreamChat.of(context).user!.id,
);
_showUserInfoModal(member.user, userMember?.role == 'owner');
},
@@ -211,7 +225,7 @@ class _GroupInfoScreenState extends State {
padding: const EdgeInsets.symmetric(
horizontal: 8.0, vertical: 12.0),
child: UserAvatar(
- user: member.user,
+ user: member.user!,
constraints: BoxConstraints(
maxHeight: 40.0, maxWidth: 40.0),
),
@@ -222,18 +236,18 @@ class _GroupInfoScreenState extends State {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
- member.user.name,
+ member.user!.name,
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(
height: 1.0,
),
Text(
- _getLastSeen(member.user),
+ _getLastSeen(member.user!),
style: TextStyle(
color: StreamChatTheme.of(context)
.colorTheme
- .black
+ .textHighEmphasis
.withOpacity(0.5)),
),
],
@@ -246,7 +260,7 @@ class _GroupInfoScreenState extends State {
style: TextStyle(
color: StreamChatTheme.of(context)
.colorTheme
- .black
+ .textHighEmphasis
.withOpacity(0.5)),
),
),
@@ -256,13 +270,13 @@ class _GroupInfoScreenState extends State {
height: 1.0,
color: StreamChatTheme.of(context)
.colorTheme
- .greyGainsboro,
+ .disabled,
),
],
),
),
),
- color: StreamChatTheme.of(context).colorTheme.whiteSnow,
+ color: StreamChatTheme.of(context).colorTheme.appBg,
);
},
),
@@ -274,7 +288,7 @@ class _GroupInfoScreenState extends State