migrate sample app
This commit is contained in:
@@ -17,13 +17,13 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
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();
|
||||
|
||||
@@ -73,7 +73,7 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
}
|
||||
},
|
||||
validator: (value) {
|
||||
if (value.isEmpty) {
|
||||
if (value!.isEmpty) {
|
||||
setState(() {
|
||||
_apiKeyError =
|
||||
'Please enter the Chat API Key'.toUpperCase();
|
||||
@@ -119,7 +119,7 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
}
|
||||
},
|
||||
validator: (value) {
|
||||
if (value.isEmpty) {
|
||||
if (value!.isEmpty) {
|
||||
setState(() {
|
||||
_userIdError =
|
||||
'Please enter the User ID'.toUpperCase();
|
||||
@@ -165,7 +165,7 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
},
|
||||
controller: _userTokenController,
|
||||
validator: (value) {
|
||||
if (value.isEmpty) {
|
||||
if (value!.isEmpty) {
|
||||
setState(() {
|
||||
_userTokenError =
|
||||
'Please enter the user token'.toUpperCase();
|
||||
@@ -243,7 +243,7 @@ class _AdvancedOptionsPageState extends State<AdvancedOptionsPage> {
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
if (_formKey.currentState.validate()) {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
final apiKey = _apiKeyController.text;
|
||||
final userId = _userIdController.text;
|
||||
final userToken = _userTokenController.text;
|
||||
|
||||
@@ -6,16 +6,16 @@ class ChannelFileDisplayScreen extends StatefulWidget {
|
||||
/// 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<SortOption> sortOptions;
|
||||
final List<SortOption>? 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;
|
||||
final PaginationParams? paginationParams;
|
||||
|
||||
/// The builder used when the file list is empty.
|
||||
final WidgetBuilder emptyBuilder;
|
||||
final WidgetBuilder? emptyBuilder;
|
||||
|
||||
const ChannelFileDisplayScreen({
|
||||
this.sortOptions,
|
||||
@@ -36,7 +36,7 @@ class _ChannelFileDisplayScreenState extends State<ChannelFileDisplayScreen> {
|
||||
messageSearchBloc.search(
|
||||
filter: Filter.in_(
|
||||
'cid',
|
||||
[StreamChannel.of(context).channel.cid],
|
||||
[StreamChannel.of(context).channel.cid!],
|
||||
),
|
||||
messageFilter: Filter.in_(
|
||||
'attachments.type',
|
||||
@@ -93,9 +93,9 @@ class _ChannelFileDisplayScreenState extends State<ChannelFileDisplayScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
if (snapshot.data.isEmpty) {
|
||||
if (snapshot.data!.isEmpty) {
|
||||
if (widget.emptyBuilder != null) {
|
||||
return widget.emptyBuilder(context);
|
||||
return widget.emptyBuilder!(context);
|
||||
}
|
||||
return Center(
|
||||
child: Column(
|
||||
@@ -132,7 +132,7 @@ class _ChannelFileDisplayScreenState extends State<ChannelFileDisplayScreen> {
|
||||
|
||||
final media = <Attachment, Message>{};
|
||||
|
||||
for (var item in snapshot.data) {
|
||||
for (var item in snapshot.data!) {
|
||||
item.message.attachments.where((e) => e.type == 'file').forEach((e) {
|
||||
media[e] = item.message;
|
||||
});
|
||||
@@ -142,14 +142,14 @@ class _ChannelFileDisplayScreenState extends State<ChannelFileDisplayScreen> {
|
||||
onEndOfPage: () => messageSearchBloc.search(
|
||||
filter: Filter.in_(
|
||||
'cid',
|
||||
[StreamChannel.of(context).channel.cid],
|
||||
[StreamChannel.of(context).channel.cid!],
|
||||
),
|
||||
messageFilter: Filter.in_(
|
||||
'attachments.type',
|
||||
['file'],
|
||||
),
|
||||
sort: widget.sortOptions,
|
||||
pagination: widget.paginationParams.copyWith(
|
||||
pagination: widget.paginationParams!.copyWith(
|
||||
offset: messageSearchBloc.messageResponses?.length ?? 0,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -7,23 +7,23 @@ class ChannelMediaDisplayScreen extends StatefulWidget {
|
||||
/// 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<SortOption> sortOptions;
|
||||
final List<SortOption>? 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;
|
||||
final PaginationParams? paginationParams;
|
||||
|
||||
/// The builder used when the file list is empty.
|
||||
final WidgetBuilder emptyBuilder;
|
||||
final WidgetBuilder? emptyBuilder;
|
||||
|
||||
final ShowMessageCallback onShowMessage;
|
||||
final ShowMessageCallback? onShowMessage;
|
||||
|
||||
final MessageTheme messageTheme;
|
||||
|
||||
const ChannelMediaDisplayScreen({
|
||||
@required this.messageTheme,
|
||||
required this.messageTheme,
|
||||
this.sortOptions,
|
||||
this.paginationParams,
|
||||
this.emptyBuilder,
|
||||
@@ -36,7 +36,7 @@ class ChannelMediaDisplayScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ChannelMediaDisplayScreenState extends State<ChannelMediaDisplayScreen> {
|
||||
Map<String, VideoPlayerController> controllerCache = {};
|
||||
Map<String?, VideoPlayerController?> controllerCache = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -45,7 +45,7 @@ class _ChannelMediaDisplayScreenState extends State<ChannelMediaDisplayScreen> {
|
||||
messageSearchBloc.search(
|
||||
filter: Filter.in_(
|
||||
'cid',
|
||||
[StreamChannel.of(context).channel.cid],
|
||||
[StreamChannel.of(context).channel.cid!],
|
||||
),
|
||||
messageFilter: Filter.in_(
|
||||
'attachments.type',
|
||||
@@ -103,9 +103,9 @@ class _ChannelMediaDisplayScreenState extends State<ChannelMediaDisplayScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
if (snapshot.data.isEmpty) {
|
||||
if (snapshot.data!.isEmpty) {
|
||||
if (widget.emptyBuilder != null) {
|
||||
return widget.emptyBuilder(context);
|
||||
return widget.emptyBuilder!(context);
|
||||
}
|
||||
return Center(
|
||||
child: Column(
|
||||
@@ -142,18 +142,18 @@ class _ChannelMediaDisplayScreenState extends State<ChannelMediaDisplayScreen> {
|
||||
|
||||
final media = <_AssetPackage>[];
|
||||
|
||||
for (var item in snapshot.data) {
|
||||
for (var item in snapshot.data!) {
|
||||
item.message.attachments
|
||||
.where((e) =>
|
||||
(e.type == 'image' || e.type == 'video') &&
|
||||
e.ogScrapeUrl == null)
|
||||
.forEach((e) {
|
||||
VideoPlayerController controller;
|
||||
VideoPlayerController? controller;
|
||||
if (e.type == 'video') {
|
||||
var cachedController = controllerCache[e.assetUrl];
|
||||
|
||||
if (cachedController == null) {
|
||||
controller = VideoPlayerController.network(e.assetUrl);
|
||||
controller = VideoPlayerController.network(e.assetUrl!);
|
||||
controller.initialize();
|
||||
controllerCache[e.assetUrl] = controller;
|
||||
} else {
|
||||
@@ -168,14 +168,14 @@ class _ChannelMediaDisplayScreenState extends State<ChannelMediaDisplayScreen> {
|
||||
onEndOfPage: () => messageSearchBloc.search(
|
||||
filter: Filter.in_(
|
||||
'cid',
|
||||
[StreamChannel.of(context).channel.cid],
|
||||
[StreamChannel.of(context).channel.cid!],
|
||||
),
|
||||
messageFilter: Filter.in_(
|
||||
'attachments.type',
|
||||
['image', 'video'],
|
||||
),
|
||||
sort: widget.sortOptions,
|
||||
pagination: widget.paginationParams.copyWith(
|
||||
pagination: widget.paginationParams!.copyWith(
|
||||
offset: messageSearchBloc.messageResponses?.length ?? 0,
|
||||
),
|
||||
),
|
||||
@@ -198,7 +198,7 @@ class _ChannelMediaDisplayScreenState extends State<ChannelMediaDisplayScreen> {
|
||||
media.map((e) => e.attachment).toList(),
|
||||
startIndex: position,
|
||||
message: media[position].message,
|
||||
userName: media[position].message.user.name,
|
||||
userName: media[position].message.user!.name,
|
||||
onShowMessage: widget.onShowMessage,
|
||||
),
|
||||
),
|
||||
@@ -218,7 +218,7 @@ class _ChannelMediaDisplayScreenState extends State<ChannelMediaDisplayScreen> {
|
||||
messageTheme: widget.messageTheme,
|
||||
),
|
||||
)
|
||||
: VideoPlayer(media[position].videoPlayer),
|
||||
: VideoPlayer(media[position].videoPlayer!),
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -234,7 +234,7 @@ class _ChannelMediaDisplayScreenState extends State<ChannelMediaDisplayScreen> {
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
for (var c in controllerCache.values) {
|
||||
c.dispose();
|
||||
c!.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,7 +242,7 @@ class _ChannelMediaDisplayScreenState extends State<ChannelMediaDisplayScreen> {
|
||||
class _AssetPackage {
|
||||
Attachment attachment;
|
||||
Message message;
|
||||
VideoPlayerController videoPlayer;
|
||||
VideoPlayerController? videoPlayer;
|
||||
|
||||
_AssetPackage(this.attachment, this.message, this.videoPlayer);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:jiffy/jiffy.dart';
|
||||
@@ -11,13 +12,13 @@ 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;
|
||||
|
||||
final MessageTheme messageTheme;
|
||||
|
||||
const ChatInfoScreen({
|
||||
Key key,
|
||||
@required this.messageTheme,
|
||||
Key? key,
|
||||
required this.messageTheme,
|
||||
this.user,
|
||||
}) : super(key: key);
|
||||
|
||||
@@ -26,7 +27,7 @@ class ChatInfoScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
ValueNotifier<bool> mutedBool = ValueNotifier(false);
|
||||
ValueNotifier<bool?> mutedBool = ValueNotifier(false);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -54,9 +55,8 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
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(),
|
||||
],
|
||||
@@ -76,7 +76,7 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: UserAvatar(
|
||||
user: widget.user,
|
||||
user: widget.user!,
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: 72.0,
|
||||
maxHeight: 72.0,
|
||||
@@ -86,19 +86,19 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
),
|
||||
),
|
||||
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}',
|
||||
title: '@${widget.user!.id}',
|
||||
tileColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
trailing: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Text(
|
||||
widget.user.name,
|
||||
widget.user!.name,
|
||||
style: TextStyle(
|
||||
color: StreamChatTheme.of(context)
|
||||
.colorTheme
|
||||
@@ -161,15 +161,15 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
),
|
||||
trailing: snapshot.data == null
|
||||
? CircularProgressIndicator()
|
||||
: ValueListenableBuilder<bool>(
|
||||
: ValueListenableBuilder<bool?>(
|
||||
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();
|
||||
@@ -394,7 +394,7 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (widget.user.online)
|
||||
if (widget.user!.online)
|
||||
Material(
|
||||
type: MaterialType.circle,
|
||||
child: Container(
|
||||
@@ -411,7 +411,7 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
color: StreamChatTheme.of(context).colorTheme.white,
|
||||
),
|
||||
alternativeWidget,
|
||||
if (widget.user.online)
|
||||
if (widget.user!.online)
|
||||
SizedBox(
|
||||
width: 24.0,
|
||||
),
|
||||
@@ -421,8 +421,8 @@ class _ChatInfoScreenState extends State<ChatInfoScreen> {
|
||||
}
|
||||
|
||||
class _SharedGroupsScreen extends StatefulWidget {
|
||||
final User mainUser;
|
||||
final User otherUser;
|
||||
final User? mainUser;
|
||||
final User? otherUser;
|
||||
|
||||
_SharedGroupsScreen(this.mainUser, this.otherUser);
|
||||
|
||||
@@ -453,8 +453,8 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
|
||||
body: StreamBuilder<List<Channel>>(
|
||||
stream: chat.client.queryChannels(
|
||||
filter: Filter.and([
|
||||
Filter.in_('members', [widget.otherUser.id]),
|
||||
Filter.in_('members', [widget.mainUser.id]),
|
||||
Filter.in_('members', [widget.otherUser!.id]),
|
||||
Filter.in_('members', [widget.mainUser!.id]),
|
||||
]),
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
@@ -464,7 +464,7 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
if (snapshot.data.isEmpty) {
|
||||
if (snapshot.data!.isEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@@ -498,11 +498,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();
|
||||
|
||||
@@ -522,24 +522,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 = <Member>[];
|
||||
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);
|
||||
@@ -549,7 +549,7 @@ 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';
|
||||
}
|
||||
@@ -572,7 +572,7 @@ class __SharedGroupsScreenState extends State<_SharedGroupsScreen> {
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
title!,
|
||||
style: textStyle,
|
||||
)),
|
||||
Padding(
|
||||
|
||||
@@ -6,18 +6,18 @@ typedef OnChipAdded<T> = void Function(T chip);
|
||||
typedef OnChipRemoved<T> = void Function(T chip);
|
||||
|
||||
class ChipsInputTextField<T> extends StatefulWidget {
|
||||
final TextEditingController controller;
|
||||
final FocusNode focusNode;
|
||||
final ValueChanged<String> onInputChanged;
|
||||
final TextEditingController? controller;
|
||||
final FocusNode? focusNode;
|
||||
final ValueChanged<String>? onInputChanged;
|
||||
final ChipBuilder<T> chipBuilder;
|
||||
final OnChipAdded<T> onChipAdded;
|
||||
final OnChipRemoved<T> onChipRemoved;
|
||||
final OnChipAdded<T>? onChipAdded;
|
||||
final OnChipRemoved<T>? 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<T> extends State<ChipsInputTextField<T>> {
|
||||
|
||||
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<T> extends State<ChipsInputTextField<T>> {
|
||||
_chips.remove(item);
|
||||
if (_chips.isEmpty) resumeItemAddition();
|
||||
});
|
||||
if (widget.onChipRemoved != null) widget.onChipRemoved(item);
|
||||
if (widget.onChipRemoved != null) widget.onChipRemoved!(item);
|
||||
}
|
||||
|
||||
void pauseItemAddition() {
|
||||
|
||||
@@ -6,11 +6,11 @@ import 'main.dart';
|
||||
import 'routes/routes.dart';
|
||||
|
||||
class GroupChatDetailsScreen extends StatefulWidget {
|
||||
final List<User> selectedUsers;
|
||||
final List<User>? 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<GroupChatDetailsScreen> {
|
||||
final _selectedUsers = <User>[];
|
||||
|
||||
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<GroupChatDetailsScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedUsers.addAll(widget.selectedUsers);
|
||||
_selectedUsers.addAll(widget.selectedUsers!);
|
||||
_groupNameController = TextEditingController()
|
||||
..addListener(_groupNameListener);
|
||||
}
|
||||
@@ -124,13 +124,13 @@ class _GroupChatDetailsScreenState extends State<GroupChatDetailsScreen> {
|
||||
? 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,
|
||||
|
||||
@@ -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';
|
||||
@@ -17,8 +18,8 @@ class GroupInfoScreen extends StatefulWidget {
|
||||
final MessageTheme messageTheme;
|
||||
|
||||
const GroupInfoScreen({
|
||||
Key key,
|
||||
@required this.messageTheme,
|
||||
Key? key,
|
||||
required this.messageTheme,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -26,29 +27,29 @@ class GroupInfoScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
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<bool> mutedBool = ValueNotifier(false);
|
||||
ValueNotifier<bool?> 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;
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -62,7 +63,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
TextEditingValue(text: channel.channel.extraData['name'] ?? ''));
|
||||
_searchController = TextEditingController()..addListener(_userNameListener);
|
||||
|
||||
_nameController.addListener(() {
|
||||
_nameController!.addListener(() {
|
||||
setState(() {});
|
||||
});
|
||||
mutedBool = ValueNotifier(StreamChannel.of(context).channel.isMuted);
|
||||
@@ -73,7 +74,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
var channel = StreamChannel.of(context);
|
||||
|
||||
return StreamBuilder<List<Member>>(
|
||||
stream: channel.channel.state.membersStream,
|
||||
stream: channel.channel.state!.membersStream,
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return Container(
|
||||
@@ -82,9 +83,8 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
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';
|
||||
|
||||
@@ -118,9 +118,9 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
_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,
|
||||
fontSize: 16,
|
||||
@@ -133,7 +133,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
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
|
||||
@@ -165,7 +165,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
_buildMembers(snapshot.data),
|
||||
_buildMembers(snapshot.data!),
|
||||
Container(
|
||||
height: 8.0,
|
||||
color: StreamChatTheme.of(context).colorTheme.greyGainsboro,
|
||||
@@ -204,9 +204,8 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
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');
|
||||
},
|
||||
@@ -220,7 +219,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
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),
|
||||
),
|
||||
@@ -231,14 +230,14 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
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
|
||||
@@ -378,7 +377,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
),
|
||||
),
|
||||
if ((channelName == null) ||
|
||||
(channelName != _nameController.text.trim()))
|
||||
(channelName != _nameController!.text.trim()))
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
@@ -386,12 +385,12 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
child: StreamSvgIcon.closeSmall(),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_nameController.text = _getChannelName(
|
||||
_nameController!.text = _getChannelName(
|
||||
2 * MediaQuery.of(context).size.width / 3,
|
||||
members: channel.state.members,
|
||||
members: channel.state!.members,
|
||||
extraData: channel.extraData,
|
||||
maxFontSize: 16.0,
|
||||
);
|
||||
)!;
|
||||
_focusNode.unfocus();
|
||||
});
|
||||
},
|
||||
@@ -406,10 +405,10 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
),
|
||||
onTap: () {
|
||||
StreamChannel.of(context).channel.update({
|
||||
'name': _nameController.text.trim(),
|
||||
'name': _nameController!.text.trim(),
|
||||
}).catchError((err) {
|
||||
setState(() {
|
||||
_nameController.text = channelName;
|
||||
_nameController!.text = channelName;
|
||||
_focusNode.unfocus();
|
||||
});
|
||||
});
|
||||
@@ -464,15 +463,15 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
),
|
||||
trailing: snapshot.data == null
|
||||
? CircularProgressIndicator()
|
||||
: ValueListenableBuilder<bool>(
|
||||
: ValueListenableBuilder<bool?>(
|
||||
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();
|
||||
@@ -617,7 +616,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
);
|
||||
if (res == true) {
|
||||
final channel = StreamChannel.of(context).channel;
|
||||
await channel.removeMembers([StreamChat.of(context).user.id]);
|
||||
await channel.removeMembers([StreamChat.of(context).user!.id]);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
@@ -655,7 +654,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
child: UserListView(
|
||||
selectedUsers: {},
|
||||
onUserTap: (user, _) async {
|
||||
_searchController.clear();
|
||||
_searchController!.clear();
|
||||
|
||||
await channel.addMembers([user.id]);
|
||||
Navigator.pop(context);
|
||||
@@ -667,11 +666,12 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
),
|
||||
filter: Filter.and(
|
||||
[
|
||||
if (_searchController.text.isNotEmpty)
|
||||
if (_searchController!.text.isNotEmpty)
|
||||
Filter.autoComplete('name', _userNameQuery),
|
||||
Filter.notIn('id', [
|
||||
StreamChat.of(context).user.id,
|
||||
...channel.state.members.map((e) => e.userId),
|
||||
StreamChat.of(context).user!.id,
|
||||
...channel.state!.members.map(((e) => e.userId!)
|
||||
as Object Function(Member)),
|
||||
]),
|
||||
],
|
||||
),
|
||||
@@ -784,7 +784,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
void _showUserInfoModal(User user, bool isUserAdmin) {
|
||||
void _showUserInfoModal(User? user, bool isUserAdmin) {
|
||||
var channel = StreamChannel.of(context).channel;
|
||||
|
||||
showModalBottomSheet(
|
||||
@@ -804,7 +804,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
),
|
||||
Center(
|
||||
child: Text(
|
||||
user.name,
|
||||
user!.name,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -814,7 +814,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
SizedBox(
|
||||
height: 5.0,
|
||||
),
|
||||
_buildConnectedTitleState(user),
|
||||
_buildConnectedTitleState(user)!,
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
@@ -828,7 +828,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (StreamChat.of(context).user.id != user.id)
|
||||
if (StreamChat.of(context).user!.id != user.id)
|
||||
_buildModalListTile(
|
||||
context,
|
||||
StreamSvgIcon.user(
|
||||
@@ -842,7 +842,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
var c = client.channel('messaging', extraData: {
|
||||
'members': [
|
||||
user.id,
|
||||
StreamChat.of(context).user.id,
|
||||
StreamChat.of(context).user!.id,
|
||||
],
|
||||
});
|
||||
|
||||
@@ -862,7 +862,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
);
|
||||
},
|
||||
),
|
||||
if (StreamChat.of(context).user.id != user.id)
|
||||
if (StreamChat.of(context).user!.id != user.id)
|
||||
_buildModalListTile(
|
||||
context,
|
||||
StreamSvgIcon.message(
|
||||
@@ -876,7 +876,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
var c = client.channel('messaging', extraData: {
|
||||
'members': [
|
||||
user.id,
|
||||
StreamChat.of(context).user.id,
|
||||
StreamChat.of(context).user!.id,
|
||||
],
|
||||
});
|
||||
|
||||
@@ -894,7 +894,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
},
|
||||
),
|
||||
if (!channel.isDistinct &&
|
||||
StreamChat.of(context).user.id != user.id &&
|
||||
StreamChat.of(context).user!.id != user.id &&
|
||||
isUserAdmin)
|
||||
_buildModalListTile(
|
||||
context,
|
||||
@@ -906,7 +906,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
// TODO: Add make owner implementation (Remaining from backend)
|
||||
}),
|
||||
if (!channel.isDistinct &&
|
||||
StreamChat.of(context).user.id != user.id &&
|
||||
StreamChat.of(context).user!.id != user.id &&
|
||||
isUserAdmin)
|
||||
_buildModalListTile(
|
||||
context,
|
||||
@@ -941,7 +941,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildConnectedTitleState(User user) {
|
||||
Widget? _buildConnectedTitleState(User? user) {
|
||||
var alternativeWidget;
|
||||
|
||||
final otherMember = user;
|
||||
@@ -973,7 +973,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
|
||||
Widget _buildModalListTile(
|
||||
BuildContext context, Widget leading, String title, VoidCallback onTap,
|
||||
{Color color}) {
|
||||
{Color? color}) {
|
||||
color ??= StreamChatTheme.of(context).colorTheme.black;
|
||||
|
||||
return Material(
|
||||
@@ -1010,24 +1010,24 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
String _getChannelName(
|
||||
String? _getChannelName(
|
||||
double width, {
|
||||
List<Member> members,
|
||||
Map extraData,
|
||||
double maxFontSize,
|
||||
List<Member>? members,
|
||||
required Map extraData,
|
||||
double? maxFontSize,
|
||||
}) {
|
||||
String title;
|
||||
String? title;
|
||||
var client = StreamChat.of(context);
|
||||
if (extraData['name'] == null) {
|
||||
final otherMembers =
|
||||
members.where((member) => member.user.id != client.user.id);
|
||||
members!.where((member) => member.user!.id != client.user!.id);
|
||||
if (otherMembers.isNotEmpty) {
|
||||
final maxWidth = width;
|
||||
final maxChars = maxWidth / maxFontSize;
|
||||
final maxChars = maxWidth / maxFontSize!;
|
||||
var currentChars = 0;
|
||||
final currentMembers = <Member>[];
|
||||
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);
|
||||
@@ -1036,7 +1036,7 @@ class _GroupInfoScreenState extends State<GroupInfoScreen> {
|
||||
|
||||
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';
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:example/chat_info_screen.dart';
|
||||
import 'package:example/choose_user_page.dart';
|
||||
import 'package:example/group_info_screen.dart';
|
||||
@@ -38,15 +39,15 @@ class MyApp extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
|
||||
InitData _initData;
|
||||
InitData? _initData;
|
||||
bool _animCompleted = false;
|
||||
Animation<double> _animation, _scaleAnimation;
|
||||
AnimationController _animationController, _scaleAnimationController;
|
||||
Animation<Color> _colorAnimation;
|
||||
int timeOfStartMs;
|
||||
Animation<double>? _animation, _scaleAnimation;
|
||||
AnimationController? _animationController, _scaleAnimationController;
|
||||
Animation<Color?>? _colorAnimation;
|
||||
late int timeOfStartMs;
|
||||
|
||||
Future<InitData> _initConnection() async {
|
||||
String apiKey, userId, token;
|
||||
String? apiKey, userId, token;
|
||||
|
||||
if (!kIsWeb) {
|
||||
final secureStorage = FlutterSecureStorage();
|
||||
@@ -84,7 +85,7 @@ class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
|
||||
begin: 1.0,
|
||||
end: 1.5,
|
||||
).animate(CurvedAnimation(
|
||||
parent: _scaleAnimationController,
|
||||
parent: _scaleAnimationController!,
|
||||
curve: Curves.easeInOutBack,
|
||||
));
|
||||
|
||||
@@ -98,21 +99,21 @@ class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
|
||||
begin: 0.0,
|
||||
end: 1000.0,
|
||||
).animate(CurvedAnimation(
|
||||
parent: _animationController,
|
||||
parent: _animationController!,
|
||||
curve: Curves.easeInOut,
|
||||
));
|
||||
_colorAnimation = ColorTween(
|
||||
begin: Color(0xff005FFF),
|
||||
end: Color(0xff005FFF),
|
||||
).animate(CurvedAnimation(
|
||||
parent: _animationController,
|
||||
parent: _animationController!,
|
||||
curve: Curves.easeInOut,
|
||||
));
|
||||
_colorAnimation = ColorTween(
|
||||
begin: Color(0xff005FFF),
|
||||
end: Colors.transparent,
|
||||
).animate(CurvedAnimation(
|
||||
parent: _animationController,
|
||||
parent: _animationController!,
|
||||
curve: Curves.easeInOut,
|
||||
));
|
||||
}
|
||||
@@ -132,22 +133,22 @@ class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
|
||||
var now = DateTime.now().millisecondsSinceEpoch;
|
||||
|
||||
if (now - timeOfStartMs > 1500) {
|
||||
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
_scaleAnimationController.forward().whenComplete(() {
|
||||
_animationController.forward();
|
||||
SchedulerBinding.instance!.addPostFrameCallback((timeStamp) {
|
||||
_scaleAnimationController?.forward().whenComplete(() {
|
||||
_animationController?.forward();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
Future.delayed(Duration(milliseconds: 1500)).then((value) {
|
||||
_scaleAnimationController.forward().whenComplete(() {
|
||||
_animationController.forward();
|
||||
_scaleAnimationController?.forward().whenComplete(() {
|
||||
_animationController?.forward();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (!kIsWeb) {
|
||||
_initData.client.state?.totalUnreadCountStream?.listen((count) {
|
||||
if (count > 0) {
|
||||
_initData!.client.state.totalUnreadCountStream.listen((count) {
|
||||
if (count! > 0) {
|
||||
FlutterAppBadger.updateBadgeCount(count);
|
||||
} else {
|
||||
FlutterAppBadger.removeBadge();
|
||||
@@ -156,7 +157,7 @@ class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
|
||||
}
|
||||
},
|
||||
);
|
||||
_animationController.addStatusListener((status) {
|
||||
_animationController?.addStatusListener((status) {
|
||||
if (status == AnimationStatus.completed) {
|
||||
setState(() {
|
||||
_animCompleted = true;
|
||||
@@ -174,20 +175,20 @@ class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
AnimatedBuilder(
|
||||
animation: _scaleAnimation,
|
||||
animation: _scaleAnimation!,
|
||||
builder: (context, _) {
|
||||
return Transform.scale(
|
||||
scale: _scaleAnimation.value,
|
||||
scale: _scaleAnimation!.value,
|
||||
child: AnimatedBuilder(
|
||||
animation: _colorAnimation,
|
||||
animation: _colorAnimation!,
|
||||
builder: (context, snapshot) {
|
||||
return Container(
|
||||
alignment: Alignment.center,
|
||||
constraints: BoxConstraints.expand(),
|
||||
color: _colorAnimation == null
|
||||
? Color(0xff005FFF)
|
||||
: _colorAnimation.value,
|
||||
child: !_animationController.isAnimating
|
||||
: _colorAnimation!.value,
|
||||
child: !_animationController!.isAnimating
|
||||
? Lottie.asset(
|
||||
'assets/floating_boat.json',
|
||||
alignment: Alignment.center,
|
||||
@@ -199,16 +200,16 @@ class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
|
||||
},
|
||||
),
|
||||
AnimatedBuilder(
|
||||
animation: _animation,
|
||||
animation: _animation!,
|
||||
builder: (context, snapshot) {
|
||||
return Transform.scale(
|
||||
scale: _animation.value,
|
||||
scale: _animation!.value,
|
||||
child: Container(
|
||||
width: 1.0,
|
||||
height: 1.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white
|
||||
.withOpacity(1 - _animationController.value),
|
||||
.withOpacity(1 - _animationController!.value),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
@@ -227,19 +228,19 @@ class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
|
||||
children: [
|
||||
if (_initData != null)
|
||||
PreferenceBuilder<int>(
|
||||
preference: _initData.preferences.getInt(
|
||||
preference: _initData!.preferences.getInt(
|
||||
'theme',
|
||||
defaultValue: 0,
|
||||
),
|
||||
builder: (context, snapshot) => MaterialApp(
|
||||
builder: (context, child) {
|
||||
return StreamChat(
|
||||
client: _initData.client,
|
||||
onBackgroundEventReceived: (e) =>
|
||||
showLocalNotification(e, _initData.client.state.user.id),
|
||||
client: _initData!.client,
|
||||
onBackgroundEventReceived: (e) => showLocalNotification(
|
||||
e, _initData!.client.state.user!.id),
|
||||
child: Builder(
|
||||
builder: (context) => AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
child: child,
|
||||
child: child!,
|
||||
value: SystemUiOverlayStyle(
|
||||
systemNavigationBarColor:
|
||||
StreamChatTheme.of(context).colorTheme.white,
|
||||
@@ -260,7 +261,7 @@ class _MyAppState extends State<MyApp> with TickerProviderStateMixin {
|
||||
1: ThemeMode.light,
|
||||
}[snapshot],
|
||||
onGenerateRoute: AppRoutes.generateRoute,
|
||||
initialRoute: _initData.client.state.user == null
|
||||
initialRoute: _initData!.client.state.user == null
|
||||
? Routes.CHOOSE_USER
|
||||
: Routes.HOME,
|
||||
),
|
||||
@@ -319,7 +320,7 @@ class _HomePageState extends State<HomePage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final user = StreamChat.of(context).user;
|
||||
final user = StreamChat.of(context).user!;
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
appBar: ChannelListHeader(
|
||||
@@ -495,7 +496,7 @@ class _HomePageState extends State<HomePage> {
|
||||
class UserMentionPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final user = StreamChat.of(context).user;
|
||||
final user = StreamChat.of(context).user!;
|
||||
return MessageSearchBloc(
|
||||
child: MessageSearchListView(
|
||||
filters: Filter.in_('members', [user.id]),
|
||||
@@ -555,8 +556,8 @@ class UserMentionPage extends StatelessWidget {
|
||||
final client = StreamChat.of(context).client;
|
||||
final message = messageResponse.message;
|
||||
final channel = client.channel(
|
||||
messageResponse.channel.type,
|
||||
id: messageResponse.channel.id,
|
||||
messageResponse.channel!.type,
|
||||
id: messageResponse.channel!.id,
|
||||
);
|
||||
if (channel.state == null) {
|
||||
await channel.watch();
|
||||
@@ -581,20 +582,20 @@ class ChannelListPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ChannelListPageState extends State<ChannelListPage> {
|
||||
TextEditingController _controller;
|
||||
TextEditingController? _controller;
|
||||
|
||||
String _channelQuery = '';
|
||||
|
||||
bool _isSearchActive = false;
|
||||
|
||||
Timer _debounce;
|
||||
Timer? _debounce;
|
||||
|
||||
void _channelQueryListener() {
|
||||
if (_debounce?.isActive ?? false) _debounce.cancel();
|
||||
if (_debounce?.isActive ?? false) _debounce!.cancel();
|
||||
_debounce = Timer(const Duration(milliseconds: 350), () {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_channelQuery = _controller.text;
|
||||
_channelQuery = _controller!.text;
|
||||
_isSearchActive = _channelQuery.isNotEmpty;
|
||||
});
|
||||
}
|
||||
@@ -620,7 +621,7 @@ class _ChannelListPageState extends State<ChannelListPage> {
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
if (_isSearchActive) {
|
||||
_controller.clear();
|
||||
_controller!.clear();
|
||||
setState(() => _isSearchActive = false);
|
||||
return false;
|
||||
}
|
||||
@@ -647,7 +648,7 @@ class _ChannelListPageState extends State<ChannelListPage> {
|
||||
? MessageSearchListView(
|
||||
showErrorTile: true,
|
||||
messageQuery: _channelQuery,
|
||||
filters: Filter.in_('members', [user.id]),
|
||||
filters: Filter.in_('members', [user!.id]),
|
||||
sortOptions: [
|
||||
SortOption(
|
||||
'created_at',
|
||||
@@ -691,8 +692,8 @@ class _ChannelListPageState extends State<ChannelListPage> {
|
||||
final client = StreamChat.of(context).client;
|
||||
final message = messageResponse.message;
|
||||
final channel = client.channel(
|
||||
messageResponse.channel.type,
|
||||
id: messageResponse.channel.id,
|
||||
messageResponse.channel!.type,
|
||||
id: messageResponse.channel!.id,
|
||||
);
|
||||
if (channel.state == null) {
|
||||
await channel.watch();
|
||||
@@ -712,7 +713,7 @@ class _ChannelListPageState extends State<ChannelListPage> {
|
||||
Navigator.pushNamed(context, Routes.NEW_CHAT);
|
||||
},
|
||||
swipeToAction: true,
|
||||
filter: Filter.in_('members', [user.id]),
|
||||
filter: Filter.in_('members', [user!.id]),
|
||||
options: {
|
||||
'presence': true,
|
||||
},
|
||||
@@ -731,10 +732,10 @@ class _ChannelListPageState extends State<ChannelListPage> {
|
||||
child: ChatInfoScreen(
|
||||
messageTheme: StreamChatTheme.of(context)
|
||||
.ownMessageTheme,
|
||||
user: channel.state.members
|
||||
user: channel.state!.members
|
||||
.where((m) =>
|
||||
m.userId !=
|
||||
channel.client.state.user.id)
|
||||
channel.client.state.user!.id)
|
||||
.first
|
||||
.user,
|
||||
),
|
||||
@@ -767,8 +768,8 @@ class _ChannelListPageState extends State<ChannelListPage> {
|
||||
}
|
||||
|
||||
class ChannelPageArgs {
|
||||
final Channel channel;
|
||||
final Message initialMessage;
|
||||
final Channel? channel;
|
||||
final Message? initialMessage;
|
||||
|
||||
const ChannelPageArgs({
|
||||
this.channel,
|
||||
@@ -777,12 +778,12 @@ class ChannelPageArgs {
|
||||
}
|
||||
|
||||
class ChannelPage extends StatefulWidget {
|
||||
final int initialScrollIndex;
|
||||
final double initialAlignment;
|
||||
final int? initialScrollIndex;
|
||||
final double? initialAlignment;
|
||||
final bool highlightInitialMessage;
|
||||
|
||||
const ChannelPage({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.initialScrollIndex,
|
||||
this.initialAlignment,
|
||||
this.highlightInitialMessage = false,
|
||||
@@ -793,8 +794,8 @@ class ChannelPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ChannelPageState extends State<ChannelPage> {
|
||||
Message _quotedMessage;
|
||||
FocusNode _focusNode;
|
||||
Message? _quotedMessage;
|
||||
FocusNode? _focusNode;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -804,14 +805,14 @@ class _ChannelPageState extends State<ChannelPage> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_focusNode.dispose();
|
||||
_focusNode!.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _reply(Message message) {
|
||||
setState(() => _quotedMessage = message);
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
_focusNode.requestFocus();
|
||||
WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
|
||||
_focusNode!.requestFocus();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -826,9 +827,8 @@ class _ChannelPageState extends State<ChannelPage> {
|
||||
|
||||
if (channel.memberCount == 2 && channel.isDistinct) {
|
||||
final currentUser = StreamChat.of(context).user;
|
||||
final otherUser = channel.state.members.firstWhere(
|
||||
(element) => element.user.id != currentUser.id,
|
||||
orElse: () => null,
|
||||
final otherUser = channel.state!.members.firstWhereOrNull(
|
||||
(element) => element.user!.id != currentUser!.id,
|
||||
);
|
||||
if (otherUser != null) {
|
||||
final pop = await Navigator.push(
|
||||
@@ -932,7 +932,7 @@ class _ChannelPageState extends State<ChannelPage> {
|
||||
quotedMessage: _quotedMessage,
|
||||
onQuotedMessageCleared: () {
|
||||
setState(() => _quotedMessage = null);
|
||||
_focusNode.unfocus();
|
||||
_focusNode!.unfocus();
|
||||
},
|
||||
),
|
||||
],
|
||||
@@ -942,12 +942,12 @@ class _ChannelPageState extends State<ChannelPage> {
|
||||
}
|
||||
|
||||
class ThreadPage extends StatefulWidget {
|
||||
final Message parent;
|
||||
final int initialScrollIndex;
|
||||
final double initialAlignment;
|
||||
final Message? parent;
|
||||
final int? initialScrollIndex;
|
||||
final double? initialAlignment;
|
||||
|
||||
ThreadPage({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.parent,
|
||||
this.initialScrollIndex,
|
||||
this.initialAlignment,
|
||||
@@ -958,7 +958,7 @@ class ThreadPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ThreadPageState extends State<ThreadPage> {
|
||||
Message _quotedMessage;
|
||||
Message? _quotedMessage;
|
||||
FocusNode _focusNode = FocusNode();
|
||||
|
||||
@override
|
||||
@@ -969,7 +969,7 @@ class _ThreadPageState extends State<ThreadPage> {
|
||||
|
||||
void _reply(Message message) {
|
||||
setState(() => _quotedMessage = message);
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
|
||||
_focusNode.requestFocus();
|
||||
});
|
||||
}
|
||||
@@ -979,7 +979,7 @@ class _ThreadPageState extends State<ThreadPage> {
|
||||
return Scaffold(
|
||||
backgroundColor: StreamChatTheme.of(context).colorTheme.whiteSnow,
|
||||
appBar: ThreadHeader(
|
||||
parent: widget.parent,
|
||||
parent: widget.parent!,
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
@@ -992,7 +992,7 @@ class _ThreadPageState extends State<ThreadPage> {
|
||||
onReplyTap: _reply,
|
||||
),
|
||||
),
|
||||
if (widget.parent.type != 'deleted')
|
||||
if (widget.parent!.type != 'deleted')
|
||||
MessageInput(
|
||||
parentMessage: widget.parent,
|
||||
focusNode: _focusNode,
|
||||
@@ -1017,8 +1017,8 @@ class InitData {
|
||||
|
||||
class HolePainter extends CustomPainter {
|
||||
HolePainter({
|
||||
@required this.color,
|
||||
@required this.holeSize,
|
||||
required this.color,
|
||||
required this.holeSize,
|
||||
});
|
||||
|
||||
Color color;
|
||||
|
||||
@@ -16,9 +16,9 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
final _chipInputTextFieldStateKey =
|
||||
GlobalKey<ChipInputTextFieldState<User>>();
|
||||
|
||||
TextEditingController _controller;
|
||||
late TextEditingController _controller;
|
||||
|
||||
ChipInputTextFieldState get _chipInputTextFieldState =>
|
||||
ChipInputTextFieldState? get _chipInputTextFieldState =>
|
||||
_chipInputTextFieldStateKey.currentState;
|
||||
|
||||
String _userNameQuery = '';
|
||||
@@ -30,14 +30,14 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
|
||||
bool _isSearchActive = false;
|
||||
|
||||
Channel channel;
|
||||
Channel? channel;
|
||||
|
||||
Timer _debounce;
|
||||
Timer? _debounce;
|
||||
|
||||
bool _showUserList = true;
|
||||
|
||||
void _userNameListener() {
|
||||
if (_debounce?.isActive ?? false) _debounce.cancel();
|
||||
if (_debounce?.isActive ?? false) _debounce!.cancel();
|
||||
_debounce = Timer(const Duration(milliseconds: 350), () {
|
||||
if (mounted)
|
||||
setState(() {
|
||||
@@ -73,7 +73,7 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
filter: Filter.and([
|
||||
Filter.equal('members', [
|
||||
..._selectedUsers.map((e) => e.id),
|
||||
chatState.user.id,
|
||||
chatState.user!.id,
|
||||
]),
|
||||
Filter.equal('distinct', true),
|
||||
]),
|
||||
@@ -86,14 +86,14 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
final _channelExisted = res.length == 1;
|
||||
if (_channelExisted) {
|
||||
channel = res.first;
|
||||
await channel.watch();
|
||||
await channel!.watch();
|
||||
} else {
|
||||
channel = chatState.client.channel(
|
||||
'messaging',
|
||||
extraData: {
|
||||
'members': [
|
||||
..._selectedUsers.map((e) => e.id),
|
||||
chatState.user.id,
|
||||
chatState.user!.id,
|
||||
],
|
||||
},
|
||||
);
|
||||
@@ -110,9 +110,9 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
void dispose() {
|
||||
_searchFocusNode.dispose();
|
||||
_messageInputFocusNode.dispose();
|
||||
_controller?.clear();
|
||||
_controller?.removeListener(_userNameListener);
|
||||
_controller?.dispose();
|
||||
_controller.clear();
|
||||
_controller.removeListener(_userNameListener);
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
message: statusString,
|
||||
child: StreamChannel(
|
||||
showLoading: false,
|
||||
channel: channel,
|
||||
channel: channel!,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -169,7 +169,7 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
chipBuilder: (context, user) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
_chipInputTextFieldState.removeItem(user);
|
||||
_chipInputTextFieldState?.removeItem(user);
|
||||
_searchFocusNode.requestFocus();
|
||||
},
|
||||
child: Stack(
|
||||
@@ -299,10 +299,10 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
_controller.clear();
|
||||
if (!_selectedUsers.contains(user)) {
|
||||
_chipInputTextFieldState
|
||||
..addItem(user)
|
||||
?..addItem(user)
|
||||
..pauseItemAddition();
|
||||
} else {
|
||||
_chipInputTextFieldState.removeItem(user);
|
||||
_chipInputTextFieldState!.removeItem(user);
|
||||
}
|
||||
},
|
||||
pagination: PaginationParams(
|
||||
@@ -312,7 +312,7 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
if (_userNameQuery.isNotEmpty)
|
||||
Filter.autoComplete('name', _userNameQuery),
|
||||
Filter.notEqual(
|
||||
'id', StreamChat.of(context).user.id),
|
||||
'id', StreamChat.of(context).user!.id),
|
||||
]),
|
||||
sort: [
|
||||
SortOption(
|
||||
@@ -367,7 +367,7 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
),
|
||||
)
|
||||
: FutureBuilder<bool>(
|
||||
future: channel.initialized,
|
||||
future: channel!.initialized,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.data == true) {
|
||||
return MessageListView();
|
||||
@@ -391,7 +391,7 @@ class _NewChatScreenState extends State<NewChatScreen> {
|
||||
MessageInput(
|
||||
focusNode: _messageInputFocusNode,
|
||||
preMessageSending: (message) async {
|
||||
await channel.watch();
|
||||
await channel!.watch();
|
||||
return message;
|
||||
},
|
||||
onMessageSent: (m) {
|
||||
|
||||
@@ -12,7 +12,7 @@ class NewGroupChatScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
|
||||
TextEditingController _controller;
|
||||
TextEditingController? _controller;
|
||||
|
||||
String _userNameQuery = '';
|
||||
|
||||
@@ -20,14 +20,14 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
|
||||
|
||||
bool _isSearchActive = false;
|
||||
|
||||
Timer _debounce;
|
||||
Timer? _debounce;
|
||||
|
||||
void _userNameListener() {
|
||||
if (_debounce?.isActive ?? false) _debounce.cancel();
|
||||
if (_debounce?.isActive ?? false) _debounce!.cancel();
|
||||
_debounce = Timer(const Duration(milliseconds: 350), () {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_userNameQuery = _controller.text;
|
||||
_userNameQuery = _controller!.text;
|
||||
_isSearchActive = _userNameQuery.isNotEmpty;
|
||||
});
|
||||
}
|
||||
@@ -80,7 +80,7 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
|
||||
setState(() {
|
||||
_selectedUsers
|
||||
..clear()
|
||||
..addAll(updatedList);
|
||||
..addAll(updatedList as Iterable<User>);
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -247,7 +247,7 @@ class _NewGroupChatScreenState extends State<NewGroupChatScreen> {
|
||||
filter: Filter.and([
|
||||
if (_userNameQuery.isNotEmpty)
|
||||
Filter.autoComplete('name', _userNameQuery),
|
||||
Filter.notEqual('id', StreamChat.of(context).user.id),
|
||||
Filter.notEqual('id', StreamChat.of(context).user!.id),
|
||||
]),
|
||||
sort: [
|
||||
SortOption(
|
||||
@@ -311,8 +311,8 @@ class _HeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||
final double height;
|
||||
|
||||
const _HeaderDelegate({
|
||||
@required this.child,
|
||||
@required this.height,
|
||||
required this.child,
|
||||
required this.height,
|
||||
});
|
||||
|
||||
@override
|
||||
|
||||
@@ -7,7 +7,7 @@ void showLocalNotification(Event event, String currentUserId) async {
|
||||
EventType.messageNew,
|
||||
EventType.notificationMessageNew,
|
||||
].contains(event.type) ||
|
||||
event.user.id == currentUserId) {
|
||||
event.user!.id == currentUserId) {
|
||||
return;
|
||||
}
|
||||
if (event.message == null) return;
|
||||
@@ -21,9 +21,9 @@ void showLocalNotification(Event event, String currentUserId) async {
|
||||
);
|
||||
await flutterLocalNotificationsPlugin.initialize(initializationSettings);
|
||||
await flutterLocalNotificationsPlugin.show(
|
||||
event.message.id.hashCode,
|
||||
event.message.user.name,
|
||||
event.message.text,
|
||||
event.message!.id.hashCode,
|
||||
event.message!.user!.name,
|
||||
event.message!.text,
|
||||
NotificationDetails(
|
||||
android: AndroidNotificationDetails(
|
||||
'message channel',
|
||||
|
||||
@@ -12,7 +12,7 @@ import '../group_info_screen.dart';
|
||||
|
||||
class AppRoutes {
|
||||
/// Add entry for new route here
|
||||
static Route<dynamic> generateRoute(RouteSettings settings) {
|
||||
static Route<dynamic>? generateRoute(RouteSettings settings) {
|
||||
final args = settings.arguments;
|
||||
switch (settings.name) {
|
||||
case Routes.APP:
|
||||
@@ -44,7 +44,7 @@ class AppRoutes {
|
||||
builder: (_) {
|
||||
final arg = args as ChannelPageArgs;
|
||||
return StreamChannel(
|
||||
channel: arg.channel,
|
||||
channel: arg.channel!,
|
||||
initialMessageId: arg.initialMessage?.id,
|
||||
child: ChannelPage(
|
||||
highlightInitialMessage: arg.initialMessage != null,
|
||||
@@ -68,7 +68,7 @@ class AppRoutes {
|
||||
settings: const RouteSettings(name: Routes.NEW_GROUP_CHAT_DETAILS),
|
||||
builder: (_) {
|
||||
return GroupChatDetailsScreen(
|
||||
selectedUsers: args,
|
||||
selectedUsers: args as List<User>?,
|
||||
);
|
||||
});
|
||||
case Routes.CHAT_INFO_SCREEN:
|
||||
@@ -76,7 +76,7 @@ class AppRoutes {
|
||||
settings: const RouteSettings(name: Routes.CHAT_INFO_SCREEN),
|
||||
builder: (context) {
|
||||
return ChatInfoScreen(
|
||||
user: args,
|
||||
user: args as User?,
|
||||
messageTheme: StreamChatTheme.of(context).ownMessageTheme,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2,15 +2,15 @@ import 'package:flutter/material.dart';
|
||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
class SearchTextField extends StatelessWidget {
|
||||
final TextEditingController controller;
|
||||
final ValueChanged<String> onChanged;
|
||||
final TextEditingController? controller;
|
||||
final ValueChanged<String>? onChanged;
|
||||
final String hintText;
|
||||
final VoidCallback onTap;
|
||||
final VoidCallback? onTap;
|
||||
final bool showCloseButton;
|
||||
|
||||
const SearchTextField({
|
||||
Key key,
|
||||
@required this.controller,
|
||||
Key? key,
|
||||
required this.controller,
|
||||
this.onChanged,
|
||||
this.onTap,
|
||||
this.hintText = 'Search',
|
||||
@@ -76,11 +76,11 @@ class SearchTextField extends StatelessWidget {
|
||||
),
|
||||
splashRadius: 24,
|
||||
onPressed: () {
|
||||
if (controller.text.isNotEmpty) {
|
||||
if (controller!.text.isNotEmpty) {
|
||||
Future.microtask(
|
||||
() => [
|
||||
controller.clear(),
|
||||
if (onChanged != null) onChanged(''),
|
||||
controller!.clear(),
|
||||
if (onChanged != null) onChanged!(''),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||
|
||||
class StreamVersion extends StatelessWidget {
|
||||
const StreamVersion({
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -20,7 +20,7 @@ class StreamVersion extends StatelessWidget {
|
||||
return SizedBox();
|
||||
}
|
||||
|
||||
final pubspec = snapshot.data;
|
||||
final pubspec = snapshot.data!;
|
||||
final yaml = loadYaml(pubspec);
|
||||
final streamChatDep =
|
||||
yaml['packages']['stream_chat_flutter']['version'];
|
||||
|
||||
@@ -4,7 +4,7 @@ publish_to: 'none'
|
||||
version: 1.5.4+1
|
||||
|
||||
environment:
|
||||
sdk: ">=2.2.2 <3.0.0"
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
|
||||
dependencies:
|
||||
flutter_app_badger: ^1.2.0
|
||||
@@ -27,6 +27,7 @@ dependencies:
|
||||
uuid: ^3.0.4
|
||||
streaming_shared_preferences: ^2.0.0
|
||||
lottie: ^1.0.1
|
||||
collection: ^1.15.0-nullsafety.4
|
||||
|
||||
dependency_overrides:
|
||||
stream_chat:
|
||||
|
||||
Reference in New Issue
Block a user