migrate chatty
This commit is contained in:
+1
-1
@@ -2,6 +2,6 @@
|
|||||||
<Workspace
|
<Workspace
|
||||||
version = "1.0">
|
version = "1.0">
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "group:Runner.xcodeproj">
|
location = "self:">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
</Workspace>
|
</Workspace>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:stream_chatter/domain/models/auth_user.dart';
|
import 'package:stream_chatter/domain/models/auth_user.dart';
|
||||||
|
|
||||||
abstract class AuthRepository {
|
abstract class AuthRepository {
|
||||||
Future<AuthUser> getAuthUser();
|
Future<AuthUser?> getAuthUser();
|
||||||
Future<AuthUser> signIn();
|
Future<AuthUser> signIn();
|
||||||
Future<void> logout();
|
Future<void> logout();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
abstract class ImagePickerRepository {
|
abstract class ImagePickerRepository {
|
||||||
Future<File> pickImage();
|
Future<File?> pickImage();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,17 @@ import 'package:stream_chatter/data/image_picker_repository.dart';
|
|||||||
|
|
||||||
class ImagePickerImpl extends ImagePickerRepository {
|
class ImagePickerImpl extends ImagePickerRepository {
|
||||||
@override
|
@override
|
||||||
Future<File> pickImage() async {
|
Future<File?> pickImage() async {
|
||||||
final picker = ImagePicker();
|
final picker = ImagePicker();
|
||||||
final pickedFile =
|
final pickedFile = await picker.getImage(
|
||||||
await picker.getImage(source: ImageSource.gallery, maxWidth: 400);
|
source: ImageSource.gallery,
|
||||||
|
maxWidth: 400,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (pickedFile == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return File(pickedFile.path);
|
return File(pickedFile.path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class StreamApiLocalImpl extends StreamApiRepository {
|
|||||||
final StreamChatClient _client;
|
final StreamChatClient _client;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<ChatUser> connectUser(ChatUser user, String token) async {
|
Future<ChatUser> connectUser(ChatUser user, String? token) async {
|
||||||
Map<String, dynamic> extraData = {};
|
Map<String, dynamic> extraData = {};
|
||||||
if (user.image != null) {
|
if (user.image != null) {
|
||||||
extraData['image'] = user.image;
|
extraData['image'] = user.image;
|
||||||
@@ -18,7 +18,7 @@ class StreamApiLocalImpl extends StreamApiRepository {
|
|||||||
}
|
}
|
||||||
await _client.disconnect();
|
await _client.disconnect();
|
||||||
await _client.connectUser(
|
await _client.connectUser(
|
||||||
User(id: user.id, extraData: extraData),
|
User(id: user.id!, extraData: extraData as Map<String, Object>),
|
||||||
token,
|
token,
|
||||||
);
|
);
|
||||||
return user;
|
return user;
|
||||||
@@ -28,12 +28,12 @@ class StreamApiLocalImpl extends StreamApiRepository {
|
|||||||
Future<List<ChatUser>> getChatUsers() async {
|
Future<List<ChatUser>> getChatUsers() async {
|
||||||
final result = await _client.queryUsers();
|
final result = await _client.queryUsers();
|
||||||
final chatUsers = result.users
|
final chatUsers = result.users
|
||||||
.where((element) => element.id != _client.state.user.id)
|
.where((element) => element.id != _client.state.user!.id)
|
||||||
.map(
|
.map(
|
||||||
(e) => ChatUser(
|
(e) => ChatUser(
|
||||||
id: e.id,
|
id: e.id,
|
||||||
name: e.name,
|
name: e.name,
|
||||||
image: e.extraData['image'],
|
image: e.extraData['image'] as String?,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
@@ -47,25 +47,25 @@ class StreamApiLocalImpl extends StreamApiRepository {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Channel> createGroupChat(
|
Future<Channel> createGroupChat(
|
||||||
String channelId, String name, List<String> members,
|
String channelId, String? name, List<String?>? members,
|
||||||
{String image}) async {
|
{String? image}) async {
|
||||||
final channel = _client.channel('messaging', id: channelId, extraData: {
|
final channel = _client.channel('messaging', id: channelId, extraData: {
|
||||||
'name': name,
|
'name': name!,
|
||||||
'image': image,
|
'image': image!,
|
||||||
'members': [_client.state.user.id, ...members],
|
'members': [_client.state.user!.id, ...members!],
|
||||||
});
|
});
|
||||||
await channel.watch();
|
await channel.watch();
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Channel> createSimpleChat(String friendId) async {
|
Future<Channel> createSimpleChat(String? friendId) async {
|
||||||
final channel = _client.channel('messaging',
|
final channel = _client.channel('messaging',
|
||||||
id: '${_client.state.user.id.hashCode}${friendId.hashCode}',
|
id: '${_client.state.user!.id.hashCode}${friendId.hashCode}',
|
||||||
extraData: {
|
extraData: {
|
||||||
'members': [
|
'members': [
|
||||||
friendId,
|
friendId,
|
||||||
_client.state.user.id,
|
_client.state.user!.id,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
await channel.watch();
|
await channel.watch();
|
||||||
@@ -84,6 +84,6 @@ class StreamApiLocalImpl extends StreamApiRepository {
|
|||||||
User(id: userId),
|
User(id: userId),
|
||||||
token,
|
token,
|
||||||
);
|
);
|
||||||
return _client.state.user.name != null && _client.state.user.name != userId;
|
return _client.state.user!.name != null && _client.state.user!.name != userId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import 'package:stream_chatter/data/upload_storage_repository.dart';
|
|||||||
|
|
||||||
class UploadStorageLocalImpl extends UploadStorageRepository {
|
class UploadStorageLocalImpl extends UploadStorageRepository {
|
||||||
@override
|
@override
|
||||||
Future<String> uploadPhoto(File file, String path) async {
|
Future<String> uploadPhoto(File? file, String path) async {
|
||||||
return 'https://lh3.googleusercontent.com/a-/AOh14GjhqGZ-V7tNXS1pOIp9vbBij4OS9JbzxXgxgy1t=s600-k-no-rp-mo';
|
return 'https://lh3.googleusercontent.com/a-/AOh14GjhqGZ-V7tNXS1pOIp9vbBij4OS9JbzxXgxgy1t=s600-k-no-rp-mo';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ class AuthImpl extends AuthRepository {
|
|||||||
FirebaseAuth _auth = FirebaseAuth.instance;
|
FirebaseAuth _auth = FirebaseAuth.instance;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<AuthUser> getAuthUser() async {
|
Future<AuthUser?> getAuthUser() async {
|
||||||
final user = _auth.currentUser;
|
final user = _auth.currentUser;
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
return AuthUser(user.uid);
|
return AuthUser(user.uid);
|
||||||
@@ -19,16 +19,21 @@ class AuthImpl extends AuthRepository {
|
|||||||
Future<AuthUser> signIn() async {
|
Future<AuthUser> signIn() async {
|
||||||
try {
|
try {
|
||||||
UserCredential userCredential;
|
UserCredential userCredential;
|
||||||
final GoogleSignInAccount googleUser = await GoogleSignIn().signIn();
|
final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
|
||||||
|
|
||||||
|
if (googleUser == null) {
|
||||||
|
throw Exception('login error');
|
||||||
|
}
|
||||||
|
|
||||||
final GoogleSignInAuthentication googleAuth =
|
final GoogleSignInAuthentication googleAuth =
|
||||||
await googleUser.authentication;
|
await googleUser.authentication;
|
||||||
final GoogleAuthCredential googleAuthCredential =
|
final GoogleAuthCredential googleAuthCredential =
|
||||||
GoogleAuthProvider.credential(
|
GoogleAuthProvider.credential(
|
||||||
accessToken: googleAuth.accessToken,
|
accessToken: googleAuth.accessToken,
|
||||||
idToken: googleAuth.idToken,
|
idToken: googleAuth.idToken,
|
||||||
);
|
) as GoogleAuthCredential;
|
||||||
userCredential = await _auth.signInWithCredential(googleAuthCredential);
|
userCredential = await _auth.signInWithCredential(googleAuthCredential);
|
||||||
final user = userCredential.user;
|
final user = userCredential.user!;
|
||||||
return AuthUser(user.uid);
|
return AuthUser(user.uid);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ class PersistentStorageImpl extends PersistentStorageRepository {
|
|||||||
@override
|
@override
|
||||||
Future<void> updateDarkMode(bool isDarkMode) async {
|
Future<void> updateDarkMode(bool isDarkMode) async {
|
||||||
final preference = await SharedPreferences.getInstance();
|
final preference = await SharedPreferences.getInstance();
|
||||||
return await preference.setBool(_isDarkMode, isDarkMode);
|
await preference.setBool(_isDarkMode, isDarkMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class StreamApiImpl extends StreamApiRepository {
|
|||||||
final StreamChatClient _client;
|
final StreamChatClient _client;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<ChatUser> connectUser(ChatUser user, String token) async {
|
Future<ChatUser> connectUser(ChatUser user, String? token) async {
|
||||||
Map<String, dynamic> extraData = {};
|
Map<String, dynamic> extraData = {};
|
||||||
if (user.image != null) {
|
if (user.image != null) {
|
||||||
extraData['image'] = user.image;
|
extraData['image'] = user.image;
|
||||||
@@ -21,7 +21,7 @@ class StreamApiImpl extends StreamApiRepository {
|
|||||||
}
|
}
|
||||||
await _client.disconnect();
|
await _client.disconnect();
|
||||||
await _client.connectUser(
|
await _client.connectUser(
|
||||||
User(id: user.id, extraData: extraData),
|
User(id: user.id!, extraData: extraData as Map<String, Object>),
|
||||||
token,
|
token,
|
||||||
);
|
);
|
||||||
return user;
|
return user;
|
||||||
@@ -31,12 +31,12 @@ class StreamApiImpl extends StreamApiRepository {
|
|||||||
Future<List<ChatUser>> getChatUsers() async {
|
Future<List<ChatUser>> getChatUsers() async {
|
||||||
final result = await _client.queryUsers();
|
final result = await _client.queryUsers();
|
||||||
final chatUsers = result.users
|
final chatUsers = result.users
|
||||||
.where((element) => element.id != _client.state.user.id)
|
.where((element) => element.id != _client.state.user!.id)
|
||||||
.map(
|
.map(
|
||||||
(e) => ChatUser(
|
(e) => ChatUser(
|
||||||
id: e.id,
|
id: e.id,
|
||||||
name: e.name,
|
name: e.name,
|
||||||
image: e.extraData['image'],
|
image: e.extraData['image'] as String?,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
@@ -44,10 +44,10 @@ class StreamApiImpl extends StreamApiRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> getToken(String userId) async {
|
Future<String?> getToken(String userId) async {
|
||||||
//TODO: use your own implementation in Production
|
//TODO: use your own implementation in Production
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
'your_backend_url',
|
Uri.parse('your_backend_url'),
|
||||||
body: jsonEncode(<String, String>{'id': userId}),
|
body: jsonEncode(<String, String>{'id': userId}),
|
||||||
headers: <String, String>{
|
headers: <String, String>{
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
@@ -62,25 +62,25 @@ class StreamApiImpl extends StreamApiRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Channel> createGroupChat(String id, String name, List<String> members,
|
Future<Channel> createGroupChat(String id, String? name, List<String?>? members,
|
||||||
{String image}) async {
|
{String? image}) async {
|
||||||
final channel = _client.channel('messaging', id: id, extraData: {
|
final channel = _client.channel('messaging', id: id, extraData: {
|
||||||
'name': name,
|
'name': name!,
|
||||||
'image': image,
|
'image': image!,
|
||||||
'members': [_client.state.user.id, ...members],
|
'members': [_client.state.user!.id, ...members!],
|
||||||
});
|
});
|
||||||
await channel.watch();
|
await channel.watch();
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Channel> createSimpleChat(String friendId) async {
|
Future<Channel> createSimpleChat(String? friendId) async {
|
||||||
final channel = _client.channel('messaging',
|
final channel = _client.channel('messaging',
|
||||||
id: '${_client.state.user.id.hashCode}${friendId.hashCode}',
|
id: '${_client.state.user!.id.hashCode}${friendId.hashCode}',
|
||||||
extraData: {
|
extraData: {
|
||||||
'members': [
|
'members': [
|
||||||
friendId,
|
friendId,
|
||||||
_client.state.user.id,
|
_client.state.user!.id,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
await channel.watch();
|
await channel.watch();
|
||||||
@@ -99,6 +99,6 @@ class StreamApiImpl extends StreamApiRepository {
|
|||||||
User(id: userId),
|
User(id: userId),
|
||||||
token,
|
token,
|
||||||
);
|
);
|
||||||
return _client.state.user.name != null && _client.state.user.name != userId;
|
return _client.state.user!.name != null && _client.state.user!.name != userId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import 'package:stream_chatter/data/upload_storage_repository.dart';
|
|||||||
|
|
||||||
class UploadStorageImpl extends UploadStorageRepository {
|
class UploadStorageImpl extends UploadStorageRepository {
|
||||||
@override
|
@override
|
||||||
Future<String> uploadPhoto(File file, String path) async {
|
Future<String> uploadPhoto(File? file, String path) async {
|
||||||
final ref = firebase_storage.FirebaseStorage.instance.ref(path);
|
final ref = firebase_storage.FirebaseStorage.instance.ref(path);
|
||||||
final uploadTask = ref.putFile(file);
|
final uploadTask = ref.putFile(file!);
|
||||||
await uploadTask;
|
await uploadTask;
|
||||||
return await ref.getDownloadURL();
|
return await ref.getDownloadURL();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
|||||||
|
|
||||||
abstract class StreamApiRepository {
|
abstract class StreamApiRepository {
|
||||||
Future<List<ChatUser>> getChatUsers();
|
Future<List<ChatUser>> getChatUsers();
|
||||||
Future<String> getToken(String userId);
|
Future<String?> getToken(String userId);
|
||||||
Future<bool> connectIfExist(String userId);
|
Future<bool> connectIfExist(String userId);
|
||||||
Future<ChatUser> connectUser(ChatUser user, String token);
|
Future<ChatUser> connectUser(ChatUser user, String? token);
|
||||||
Future<Channel> createGroupChat(
|
Future<Channel> createGroupChat(
|
||||||
String channelId, String name, List<String> members,
|
String channelId, String? name, List<String?>? members,
|
||||||
{String image});
|
{String? image});
|
||||||
Future<Channel> createSimpleChat(String friendId);
|
Future<Channel> createSimpleChat(String? friendId);
|
||||||
Future<void> logout();
|
Future<void> logout();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
abstract class UploadStorageRepository {
|
abstract class UploadStorageRepository {
|
||||||
Future<String> uploadPhoto(File file, String path);
|
Future<String> uploadPhoto(File? file, String path);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
class ChatUser {
|
class ChatUser {
|
||||||
const ChatUser({this.name, this.image, this.id});
|
const ChatUser({this.name, this.image, this.id});
|
||||||
final String name;
|
final String? name;
|
||||||
final String image;
|
final String? image;
|
||||||
final String id;
|
final String? id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import 'package:uuid/uuid.dart';
|
|||||||
|
|
||||||
class CreateGroupInput {
|
class CreateGroupInput {
|
||||||
CreateGroupInput({this.imageFile, this.name, this.members});
|
CreateGroupInput({this.imageFile, this.name, this.members});
|
||||||
final File imageFile;
|
final File? imageFile;
|
||||||
final String name;
|
final String? name;
|
||||||
final List<String> members;
|
final List<String?>? members;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateGroupUseCase {
|
class CreateGroupUseCase {
|
||||||
@@ -23,7 +23,7 @@ class CreateGroupUseCase {
|
|||||||
|
|
||||||
Future<Channel> createGroup(CreateGroupInput input) async {
|
Future<Channel> createGroup(CreateGroupInput input) async {
|
||||||
final channelId = Uuid().v4();
|
final channelId = Uuid().v4();
|
||||||
String image;
|
String? image;
|
||||||
if (input.imageFile != null) {
|
if (input.imageFile != null) {
|
||||||
image = await _uploadStorageRepository.uploadPhoto(
|
image = await _uploadStorageRepository.uploadPhoto(
|
||||||
input.imageFile, 'channels/$channelId');
|
input.imageFile, 'channels/$channelId');
|
||||||
|
|||||||
@@ -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/data/upload_storage_repository.dart';
|
||||||
import 'package:stream_chatter/domain/models/chat_user.dart';
|
import 'package:stream_chatter/domain/models/chat_user.dart';
|
||||||
|
|
||||||
|
import '../exceptions/auth_exception.dart';
|
||||||
|
|
||||||
class ProfileInput {
|
class ProfileInput {
|
||||||
ProfileInput({this.imageFile, this.name});
|
ProfileInput({this.imageFile, this.name});
|
||||||
final File imageFile;
|
final File? imageFile;
|
||||||
final String name;
|
final String? name;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProfileSignInUseCase {
|
class ProfileSignInUseCase {
|
||||||
@@ -24,13 +26,21 @@ class ProfileSignInUseCase {
|
|||||||
|
|
||||||
Future<void> verify(ProfileInput input) async {
|
Future<void> verify(ProfileInput input) async {
|
||||||
final auth = await _authRepository.getAuthUser();
|
final auth = await _authRepository.getAuthUser();
|
||||||
|
if (auth == null) {
|
||||||
|
throw AuthException(AuthErrorCode.not_auth);
|
||||||
|
}
|
||||||
final token = await _streamApiRepository.getToken(auth.id);
|
final token = await _streamApiRepository.getToken(auth.id);
|
||||||
String image;
|
String? image;
|
||||||
if (input.imageFile != null) {
|
if (input.imageFile != null) {
|
||||||
image = await _uploadStorageRepository.uploadPhoto(
|
image = await _uploadStorageRepository.uploadPhoto(
|
||||||
input.imageFile, 'users/${auth.id}');
|
input.imageFile, 'users/${auth.id}');
|
||||||
}
|
}
|
||||||
await _streamApiRepository.connectUser(
|
await _streamApiRepository.connectUser(
|
||||||
ChatUser(name: input.name, id: auth.id, image: image), token);
|
ChatUser(
|
||||||
|
name: input.name,
|
||||||
|
id: auth.id,
|
||||||
|
image: image,
|
||||||
|
),
|
||||||
|
token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class AvatarImageView extends StatelessWidget {
|
class AvatarImageView extends StatelessWidget {
|
||||||
const AvatarImageView({Key key, this.onTap, this.child}) : super(key: key);
|
const AvatarImageView({Key? key, this.onTap, this.child}) : super(key: key);
|
||||||
final Widget child;
|
final Widget? child;
|
||||||
final VoidCallback onTap;
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ class LoadingView extends StatelessWidget {
|
|||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
const LoadingView({
|
const LoadingView({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.child,
|
required this.child,
|
||||||
this.isLoading = false,
|
this.isLoading = false,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:collection/collection.dart' show IterableExtension;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:jiffy/jiffy.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.
|
/// Modify it to change the widget appearance.
|
||||||
class MyChannelPreview extends StatelessWidget {
|
class MyChannelPreview extends StatelessWidget {
|
||||||
/// Function called when tapping this widget
|
/// Function called when tapping this widget
|
||||||
final void Function(Channel) onTap;
|
final void Function(Channel)? onTap;
|
||||||
|
|
||||||
/// Function called when long pressing this widget
|
/// Function called when long pressing this widget
|
||||||
final void Function(Channel) onLongPress;
|
final void Function(Channel)? onLongPress;
|
||||||
|
|
||||||
/// Channel displayed
|
/// Channel displayed
|
||||||
final Channel channel;
|
final Channel channel;
|
||||||
|
|
||||||
/// The function called when the image is tapped
|
/// The function called when the image is tapped
|
||||||
final VoidCallback onImageTap;
|
final VoidCallback? onImageTap;
|
||||||
|
|
||||||
final String heroTag;
|
final String? heroTag;
|
||||||
|
|
||||||
MyChannelPreview({
|
MyChannelPreview({
|
||||||
@required this.channel,
|
required this.channel,
|
||||||
Key key,
|
Key? key,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.onLongPress,
|
this.onLongPress,
|
||||||
this.onImageTap,
|
this.onImageTap,
|
||||||
@@ -59,24 +60,24 @@ class MyChannelPreview extends StatelessWidget {
|
|||||||
initialData: channel.isMuted,
|
initialData: channel.isMuted,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
return Opacity(
|
return Opacity(
|
||||||
opacity: snapshot.data ? 0.5 : 1,
|
opacity: snapshot.data! ? 0.5 : 1,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
horizontal: 8,
|
horizontal: 8,
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (onTap != null) {
|
if (onTap != null) {
|
||||||
onTap(channel);
|
onTap!(channel);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
if (onLongPress != null) {
|
if (onLongPress != null) {
|
||||||
onLongPress(channel);
|
onLongPress!(channel);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
leading: Material(
|
leading: Material(
|
||||||
child: Hero(
|
child: Hero(
|
||||||
tag: heroTag,
|
tag: heroTag!,
|
||||||
child: StreamChannel(
|
child: StreamChannel(
|
||||||
channel: channel,
|
channel: channel,
|
||||||
child: ChannelImage(
|
child: ChannelImage(
|
||||||
@@ -95,13 +96,13 @@ class MyChannelPreview extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
StreamBuilder<List<Member>>(
|
StreamBuilder<List<Member>>(
|
||||||
stream: channel.state.membersStream,
|
stream: channel.state!.membersStream,
|
||||||
initialData: channel.state.members,
|
initialData: channel.state!.members,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasData ||
|
if (!snapshot.hasData ||
|
||||||
snapshot.data.isEmpty ||
|
snapshot.data!.isEmpty ||
|
||||||
!snapshot.data.any((Member e) =>
|
!snapshot.data!.any((Member e) =>
|
||||||
e.user.id == channel.client.state.user.id)) {
|
e.user!.id == channel.client.state.user!.id)) {
|
||||||
return SizedBox();
|
return SizedBox();
|
||||||
}
|
}
|
||||||
return ChannelUnreadIndicator(
|
return ChannelUnreadIndicator(
|
||||||
@@ -116,26 +117,26 @@ class MyChannelPreview extends StatelessWidget {
|
|||||||
Flexible(child: _buildSubtitle(context)),
|
Flexible(child: _buildSubtitle(context)),
|
||||||
Builder(
|
Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final lastMessage = channel.state.messages.lastWhere(
|
final lastMessage =
|
||||||
|
channel.state!.messages.lastWhereOrNull(
|
||||||
(m) => !m.isDeleted && m.shadowed != true,
|
(m) => !m.isDeleted && m.shadowed != true,
|
||||||
orElse: () => null,
|
|
||||||
);
|
);
|
||||||
if (lastMessage?.user?.id ==
|
if (lastMessage?.user?.id ==
|
||||||
StreamChat.of(context).user.id) {
|
StreamChat.of(context).user!.id) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(right: 4.0),
|
padding: const EdgeInsets.only(right: 4.0),
|
||||||
child: SendingIndicator(
|
child: SendingIndicator(
|
||||||
message: lastMessage,
|
message: lastMessage!,
|
||||||
size: StreamChatTheme.of(context)
|
size: StreamChatTheme.of(context)
|
||||||
.channelPreviewTheme
|
.channelPreviewTheme
|
||||||
.indicatorIconSize,
|
.indicatorIconSize,
|
||||||
isMessageRead: channel.state.read
|
isMessageRead: channel.state!.read
|
||||||
?.where((element) =>
|
?.where((element) =>
|
||||||
element.user.id !=
|
element.user.id !=
|
||||||
channel.client.state.user.id)
|
channel.client.state.user!.id)
|
||||||
?.where((element) => element.lastRead
|
.where((element) => element.lastRead
|
||||||
.isAfter(lastMessage.createdAt))
|
.isAfter(lastMessage.createdAt))
|
||||||
?.isNotEmpty ==
|
.isNotEmpty ==
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -152,14 +153,14 @@ class MyChannelPreview extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDate(BuildContext context) {
|
Widget _buildDate(BuildContext context) {
|
||||||
return StreamBuilder<DateTime>(
|
return StreamBuilder<DateTime?>(
|
||||||
stream: channel.lastMessageAtStream,
|
stream: channel.lastMessageAtStream,
|
||||||
initialData: channel.lastMessageAt,
|
initialData: channel.lastMessageAt,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasData) {
|
if (!snapshot.hasData) {
|
||||||
return SizedBox();
|
return SizedBox();
|
||||||
}
|
}
|
||||||
final lastMessageAt = snapshot.data.toLocal();
|
final lastMessageAt = snapshot.data!.toLocal();
|
||||||
|
|
||||||
String stringDate;
|
String stringDate;
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
@@ -198,11 +199,11 @@ class MyChannelPreview extends StatelessWidget {
|
|||||||
' Channel is muted',
|
' Channel is muted',
|
||||||
style: StreamChatTheme.of(context)
|
style: StreamChatTheme.of(context)
|
||||||
.channelPreviewTheme
|
.channelPreviewTheme
|
||||||
.subtitle
|
.subtitle!
|
||||||
.copyWith(
|
.copyWith(
|
||||||
color: StreamChatTheme.of(context)
|
color: StreamChatTheme.of(context)
|
||||||
.channelPreviewTheme
|
.channelPreviewTheme
|
||||||
.subtitle
|
.subtitle!
|
||||||
.color,
|
.color,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -212,21 +213,20 @@ class MyChannelPreview extends StatelessWidget {
|
|||||||
return TypingIndicator(
|
return TypingIndicator(
|
||||||
channel: channel,
|
channel: channel,
|
||||||
alternativeWidget: _buildLastMessage(context),
|
alternativeWidget: _buildLastMessage(context),
|
||||||
style: StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith(
|
style: StreamChatTheme.of(context).channelPreviewTheme.subtitle!.copyWith(
|
||||||
color:
|
color:
|
||||||
StreamChatTheme.of(context).channelPreviewTheme.subtitle.color,
|
StreamChatTheme.of(context).channelPreviewTheme.subtitle!.color,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLastMessage(BuildContext context) {
|
Widget _buildLastMessage(BuildContext context) {
|
||||||
return StreamBuilder<List<Message>>(
|
return StreamBuilder<List<Message>?>(
|
||||||
stream: channel.state.messagesStream,
|
stream: channel.state!.messagesStream,
|
||||||
initialData: channel.state.messages,
|
initialData: channel.state!.messages,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
final lastMessage = snapshot.data?.lastWhere(
|
final lastMessage = snapshot.data
|
||||||
(m) => m.shadowed != true && !m.isDeleted,
|
?.lastWhereOrNull((m) => m.shadowed != true && !m.isDeleted);
|
||||||
orElse: () => null);
|
|
||||||
if (lastMessage == null) {
|
if (lastMessage == null) {
|
||||||
return SizedBox();
|
return SizedBox();
|
||||||
}
|
}
|
||||||
@@ -254,21 +254,21 @@ class MyChannelPreview extends StatelessWidget {
|
|||||||
|
|
||||||
return Text.rich(
|
return Text.rich(
|
||||||
_getDisplayText(
|
_getDisplayText(
|
||||||
text,
|
text!,
|
||||||
lastMessage.mentionedUsers,
|
lastMessage.mentionedUsers,
|
||||||
lastMessage.attachments,
|
lastMessage.attachments,
|
||||||
StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith(
|
StreamChatTheme.of(context).channelPreviewTheme.subtitle!.copyWith(
|
||||||
color: StreamChatTheme.of(context)
|
color: StreamChatTheme.of(context)
|
||||||
.channelPreviewTheme
|
.channelPreviewTheme
|
||||||
.subtitle
|
.subtitle!
|
||||||
.color,
|
.color,
|
||||||
fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
|
fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
|
||||||
? FontStyle.italic
|
? FontStyle.italic
|
||||||
: FontStyle.normal),
|
: FontStyle.normal),
|
||||||
StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith(
|
StreamChatTheme.of(context).channelPreviewTheme.subtitle!.copyWith(
|
||||||
color: StreamChatTheme.of(context)
|
color: StreamChatTheme.of(context)
|
||||||
.channelPreviewTheme
|
.channelPreviewTheme
|
||||||
.subtitle
|
.subtitle!
|
||||||
.color,
|
.color,
|
||||||
fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
|
fontStyle: (lastMessage.isSystem || lastMessage.isDeleted)
|
||||||
? FontStyle.italic
|
? FontStyle.italic
|
||||||
@@ -321,8 +321,8 @@ class MyChannelPreview extends StatelessWidget {
|
|||||||
|
|
||||||
class ChannelUnreadIndicator extends StatelessWidget {
|
class ChannelUnreadIndicator extends StatelessWidget {
|
||||||
const ChannelUnreadIndicator({
|
const ChannelUnreadIndicator({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.channel,
|
required this.channel,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final Channel channel;
|
final Channel channel;
|
||||||
@@ -330,8 +330,8 @@ class ChannelUnreadIndicator extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return StreamBuilder<int>(
|
return StreamBuilder<int>(
|
||||||
stream: channel.state.unreadCountStream,
|
stream: channel.state!.unreadCountStream,
|
||||||
initialData: channel.state.unreadCount,
|
initialData: channel.state!.unreadCount,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasData || snapshot.data == 0) {
|
if (!snapshot.hasData || snapshot.data == 0) {
|
||||||
return SizedBox();
|
return SizedBox();
|
||||||
@@ -351,7 +351,7 @@ class ChannelUnreadIndicator extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'${snapshot.data > 99 ? '99+' : snapshot.data}',
|
'${snapshot.data! > 99 ? '99+' : snapshot.data}',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
|
|||||||
@@ -23,11 +23,10 @@ class ChatView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
body: ChannelsBloc(
|
body: ChannelsBloc(
|
||||||
child: ChannelListView(
|
child: ChannelListView(
|
||||||
filter: {
|
filter: Filter.in_(
|
||||||
'members': {
|
'members',
|
||||||
'\$in': [StreamChat.of(context).user?.id],
|
[StreamChat.of(context).user!.id],
|
||||||
}
|
),
|
||||||
},
|
|
||||||
sort: [SortOption('last_message_at')],
|
sort: [SortOption('last_message_at')],
|
||||||
channelPreviewBuilder: (context, channel) {
|
channelPreviewBuilder: (context, channel) {
|
||||||
return Container(
|
return Container(
|
||||||
@@ -36,22 +35,22 @@ class ChatView extends StatelessWidget {
|
|||||||
channel: channel,
|
channel: channel,
|
||||||
heroTag: channel.id,
|
heroTag: channel.id,
|
||||||
onImageTap: () {
|
onImageTap: () {
|
||||||
String name;
|
String? name;
|
||||||
String image;
|
String? image;
|
||||||
final currentUser = StreamChat.of(context).client.state.user;
|
final currentUser = StreamChat.of(context).client.state.user;
|
||||||
if (channel.isGroup) {
|
if (channel.isGroup) {
|
||||||
name = channel.extraData['name'];
|
name = channel.extraData['name'];
|
||||||
image = channel.extraData['image'];
|
image = channel.extraData['image'];
|
||||||
} else {
|
} else {
|
||||||
final friend = channel.state.members
|
final friend = channel.state!.members
|
||||||
.where((element) => element.userId != currentUser.id)
|
.where((element) => element.userId != currentUser!.id)
|
||||||
.first
|
.first
|
||||||
.user;
|
.user!;
|
||||||
name = friend.name;
|
name = friend.name;
|
||||||
image = friend.extraData['image'];
|
image = friend.extraData['image'] as String?;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
PageRouteBuilder(
|
PageRouteBuilder(
|
||||||
barrierColor: Colors.black45,
|
barrierColor: Colors.black45,
|
||||||
barrierDismissible: true,
|
barrierDismissible: true,
|
||||||
@@ -110,15 +109,15 @@ class ChannelPage extends StatelessWidget {
|
|||||||
|
|
||||||
class ChatDetailView extends StatelessWidget {
|
class ChatDetailView extends StatelessWidget {
|
||||||
const ChatDetailView({
|
const ChatDetailView({
|
||||||
Key key,
|
Key? key,
|
||||||
this.image,
|
this.image,
|
||||||
this.name,
|
this.name,
|
||||||
this.channelId,
|
this.channelId,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final String image;
|
final String? image;
|
||||||
final String name;
|
final String? name;
|
||||||
final String channelId;
|
final String? channelId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -135,10 +134,10 @@ class ChatDetailView extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Hero(
|
Hero(
|
||||||
tag: channelId,
|
tag: channelId!,
|
||||||
child: ClipOval(
|
child: ClipOval(
|
||||||
child: Image.network(
|
child: Image.network(
|
||||||
image,
|
image!,
|
||||||
height: 180,
|
height: 180,
|
||||||
width: 180,
|
width: 180,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
@@ -146,7 +145,7 @@ class ChatDetailView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
name,
|
name!,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
|
|||||||
@@ -143,9 +143,9 @@ class FriendsSelectionView extends StatelessWidget {
|
|||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
radius: 30,
|
radius: 30,
|
||||||
backgroundImage: NetworkImage(
|
backgroundImage: NetworkImage(
|
||||||
chatUserState.chatUser.image),
|
chatUserState.chatUser.image!),
|
||||||
),
|
),
|
||||||
Text(chatUserState.chatUser.name),
|
Text(chatUserState.chatUser.name!),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
@@ -178,9 +178,9 @@ class FriendsSelectionView extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
NetworkImage(chatUserState.chatUser.image),
|
NetworkImage(chatUserState.chatUser.image!),
|
||||||
),
|
),
|
||||||
title: Text(chatUserState.chatUser.name),
|
title: Text(chatUserState.chatUser.name!),
|
||||||
trailing: isGroup
|
trailing: isGroup
|
||||||
? Checkbox(
|
? Checkbox(
|
||||||
value: chatUserState.selected,
|
value: chatUserState.selected,
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ class GroupSelectionState {
|
|||||||
this.channel,
|
this.channel,
|
||||||
this.isLoading = false,
|
this.isLoading = false,
|
||||||
});
|
});
|
||||||
final File file;
|
final File? file;
|
||||||
final Channel channel;
|
final Channel? channel;
|
||||||
final bool isLoading;
|
final bool isLoading;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class GroupSelectionView extends StatelessWidget {
|
|||||||
context,
|
context,
|
||||||
Scaffold(
|
Scaffold(
|
||||||
body: StreamChannel(
|
body: StreamChannel(
|
||||||
channel: snapshot.channel,
|
channel: snapshot.channel!,
|
||||||
child: ChannelPage(),
|
child: ChannelPage(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -60,9 +60,9 @@ class GroupSelectionView extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
AvatarImageView(
|
AvatarImageView(
|
||||||
onTap: context.read<GroupSelectionCubit>().pickImage,
|
onTap: context.read<GroupSelectionCubit>().pickImage,
|
||||||
child: snapshot?.file != null
|
child: snapshot.file != null
|
||||||
? Image.file(
|
? Image.file(
|
||||||
snapshot?.file,
|
snapshot.file!,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
)
|
)
|
||||||
: Icon(
|
: Icon(
|
||||||
@@ -104,9 +104,9 @@ class GroupSelectionView extends StatelessWidget {
|
|||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
radius: 30,
|
radius: 30,
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
NetworkImage(chatUserState.chatUser.image),
|
NetworkImage(chatUserState.chatUser.image!),
|
||||||
),
|
),
|
||||||
Text(chatUserState.chatUser.name),
|
Text(chatUserState.chatUser.name!),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class HomeView extends StatelessWidget {
|
|||||||
|
|
||||||
class HomeNavigationBar extends StatelessWidget {
|
class HomeNavigationBar extends StatelessWidget {
|
||||||
const HomeNavigationBar({
|
const HomeNavigationBar({
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -110,16 +110,16 @@ class HomeNavigationBar extends StatelessWidget {
|
|||||||
|
|
||||||
class _HomeNavItem extends StatelessWidget {
|
class _HomeNavItem extends StatelessWidget {
|
||||||
const _HomeNavItem({
|
const _HomeNavItem({
|
||||||
Key key,
|
Key? key,
|
||||||
this.iconData,
|
this.iconData,
|
||||||
this.text,
|
this.text,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.selected = false,
|
this.selected = false,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final IconData iconData;
|
final IconData? iconData;
|
||||||
final String text;
|
final String? text;
|
||||||
final VoidCallback onTap;
|
final VoidCallback? onTap;
|
||||||
final bool selected;
|
final bool selected;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -135,7 +135,7 @@ class _HomeNavItem extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Icon(iconData, color: color),
|
Icon(iconData, color: color),
|
||||||
Text(text, style: TextStyle(color: color)),
|
Text(text!, style: TextStyle(color: color)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
|||||||
class SettingsView extends StatelessWidget {
|
class SettingsView extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final user = StreamChat.of(context).client.state.user;
|
final user = StreamChat.of(context).client.state.user!;
|
||||||
final image = user?.extraData['image'];
|
final image = user.extraData['image'];
|
||||||
final textColor = Theme.of(context).appBarTheme.color;
|
final textColor = Theme.of(context).appBarTheme.color;
|
||||||
return MultiBlocProvider(
|
return MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
@@ -48,7 +48,7 @@ class SettingsView extends StatelessWidget {
|
|||||||
onTap: () => null,
|
onTap: () => null,
|
||||||
child: image != null
|
child: image != null
|
||||||
? Image.network(
|
? Image.network(
|
||||||
image,
|
image as String,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
)
|
)
|
||||||
: Icon(
|
: Icon(
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class ProfileState {
|
|||||||
this.success = false,
|
this.success = false,
|
||||||
this.loading = false,
|
this.loading = false,
|
||||||
});
|
});
|
||||||
final File file;
|
final File? file;
|
||||||
final bool success;
|
final bool success;
|
||||||
final bool loading;
|
final bool loading;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class ProfileVerifyView extends StatelessWidget {
|
|||||||
onTap: context.read<ProfileVerifyCubit>().pickImage,
|
onTap: context.read<ProfileVerifyCubit>().pickImage,
|
||||||
child: snapshot.file != null
|
child: snapshot.file != null
|
||||||
? Image.file(
|
? Image.file(
|
||||||
snapshot.file,
|
snapshot.file!,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
)
|
)
|
||||||
: Icon(
|
: Icon(
|
||||||
|
|||||||
@@ -4,22 +4,23 @@ publish_to: 'none'
|
|||||||
version: 1.0.0+1
|
version: 1.0.0+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.7.0 <3.0.0"
|
sdk: '>=2.12.0 <3.0.0'
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
flutter_bloc: 6.1.2
|
flutter_bloc: ^7.0.0
|
||||||
stream_chat_flutter: 1.3.0-beta
|
stream_chat_flutter: ^2.0.0-nullsafety.3
|
||||||
uuid: 2.2.2
|
uuid: ^3.0.4
|
||||||
|
|
||||||
firebase_core: 0.7.0
|
firebase_core: ^1.2.0
|
||||||
google_sign_in: 4.5.9
|
google_sign_in: ^5.0.3
|
||||||
firebase_auth: 0.20.0+1
|
firebase_auth: ^1.2.0
|
||||||
firebase_storage: 7.0.0
|
firebase_storage: ^8.1.0
|
||||||
shared_preferences: 0.5.12+4
|
shared_preferences: ^2.0.5
|
||||||
http: any
|
http: any
|
||||||
|
collection: ^1.15.0-nullsafety.4
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user