mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-11-30 16:29:30 +01:00
refactor: Make widgets null safe
This commit is contained in:
parent
227869d84d
commit
c10b38b27b
@ -45,7 +45,7 @@ class SearchController extends State<Search> {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (c) => PublicRoomBottomSheet(
|
||||
roomAlias: room.canonicalAlias,
|
||||
roomAlias: room.canonicalAlias ?? room.roomId,
|
||||
outerContext: context,
|
||||
chunk: room,
|
||||
),
|
||||
|
@ -1,3 +1,5 @@
|
||||
//@dart=2.12
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
@ -10,8 +12,8 @@ class ContentBanner extends StatelessWidget {
|
||||
final double height;
|
||||
final IconData defaultIcon;
|
||||
final bool loading;
|
||||
final Function onEdit;
|
||||
final Client client;
|
||||
final void Function()? onEdit;
|
||||
final Client? client;
|
||||
final double opacity;
|
||||
|
||||
const ContentBanner(this.mxContent,
|
||||
@ -21,7 +23,7 @@ class ContentBanner extends StatelessWidget {
|
||||
this.onEdit,
|
||||
this.client,
|
||||
this.opacity = 0.75,
|
||||
Key key})
|
||||
Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@ -29,7 +31,8 @@ class ContentBanner extends StatelessWidget {
|
||||
final mediaQuery = MediaQuery.of(context);
|
||||
final bannerSize =
|
||||
(mediaQuery.size.width * mediaQuery.devicePixelRatio).toInt();
|
||||
final src = mxContent?.getThumbnail(
|
||||
final onEdit = this.onEdit;
|
||||
final src = mxContent.getThumbnail(
|
||||
client ?? Matrix.of(context).client,
|
||||
width: bannerSize,
|
||||
height: bannerSize,
|
||||
@ -51,8 +54,7 @@ class ContentBanner extends StatelessWidget {
|
||||
bottom: 0,
|
||||
child: Opacity(
|
||||
opacity: opacity,
|
||||
child:
|
||||
(!loading && mxContent != null && mxContent.host.isNotEmpty)
|
||||
child: (!loading && mxContent.host.isNotEmpty)
|
||||
? CachedNetworkImage(
|
||||
imageUrl: src.toString(),
|
||||
height: 300,
|
||||
@ -69,7 +71,7 @@ class ContentBanner extends StatelessWidget {
|
||||
mini: true,
|
||||
onPressed: onEdit,
|
||||
backgroundColor: Theme.of(context).backgroundColor,
|
||||
foregroundColor: Theme.of(context).textTheme.bodyText1.color,
|
||||
foregroundColor: Theme.of(context).textTheme.bodyText1?.color,
|
||||
child: const Icon(Icons.camera_alt_outlined),
|
||||
),
|
||||
),
|
||||
|
@ -1,3 +1,5 @@
|
||||
//@dart=2.12
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
@ -5,22 +7,22 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import '../config/app_config.dart';
|
||||
|
||||
class DefaultAppBarSearchField extends StatefulWidget {
|
||||
final TextEditingController searchController;
|
||||
final void Function(String) onChanged;
|
||||
final void Function(String) onSubmit;
|
||||
final Widget suffix;
|
||||
final TextEditingController? searchController;
|
||||
final void Function(String)? onChanged;
|
||||
final void Function(String)? onSubmit;
|
||||
final Widget? suffix;
|
||||
final bool autofocus;
|
||||
final String prefixText;
|
||||
final String hintText;
|
||||
final String labelText;
|
||||
final EdgeInsets padding;
|
||||
final String? prefixText;
|
||||
final String? hintText;
|
||||
final String? labelText;
|
||||
final EdgeInsets? padding;
|
||||
final bool readOnly;
|
||||
final Widget prefixIcon;
|
||||
final Widget? prefixIcon;
|
||||
final bool unfocusOnClear;
|
||||
final bool autocorrect;
|
||||
|
||||
const DefaultAppBarSearchField({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.searchController,
|
||||
this.onChanged,
|
||||
this.onSubmit,
|
||||
@ -42,14 +44,14 @@ class DefaultAppBarSearchField extends StatefulWidget {
|
||||
}
|
||||
|
||||
class DefaultAppBarSearchFieldState extends State<DefaultAppBarSearchField> {
|
||||
TextEditingController _searchController;
|
||||
late final TextEditingController _searchController;
|
||||
bool _lastTextWasEmpty = false;
|
||||
final FocusNode _focusNode = FocusNode();
|
||||
|
||||
void requestFocus() => _focusNode.requestFocus();
|
||||
|
||||
void _updateSearchController() {
|
||||
final thisTextIsEmpty = _searchController.text?.isEmpty ?? false;
|
||||
final thisTextIsEmpty = _searchController.text.isEmpty;
|
||||
if (_lastTextWasEmpty != thisTextIsEmpty) {
|
||||
setState(() => _lastTextWasEmpty = thisTextIsEmpty);
|
||||
}
|
||||
@ -61,7 +63,7 @@ class DefaultAppBarSearchFieldState extends State<DefaultAppBarSearchField> {
|
||||
_searchController = widget.searchController ?? TextEditingController();
|
||||
// we need to remove the listener in the dispose method, so we need a reference to the callback
|
||||
_searchController.addListener(_updateSearchController);
|
||||
_focusNode.addListener(() => setState(() => null));
|
||||
_focusNode.addListener(() => setState(() {}));
|
||||
}
|
||||
|
||||
@override
|
||||
@ -108,9 +110,9 @@ class DefaultAppBarSearchFieldState extends State<DefaultAppBarSearchField> {
|
||||
suffixIcon: !widget.readOnly &&
|
||||
(_focusNode.hasFocus ||
|
||||
(widget.suffix == null &&
|
||||
(_searchController.text?.isNotEmpty ?? false)))
|
||||
(_searchController.text.isNotEmpty)))
|
||||
? IconButton(
|
||||
tooltip: L10n.of(context).clearText,
|
||||
tooltip: L10n.of(context)!.clearText,
|
||||
icon: const Icon(Icons.backspace_outlined),
|
||||
onPressed: () {
|
||||
_searchController.clear();
|
||||
|
@ -1,3 +1,5 @@
|
||||
//@dart=2.12
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
@ -18,14 +20,18 @@ extension LocalNotificationsExtension on MatrixState {
|
||||
final roomId = eventUpdate.roomID;
|
||||
if (webHasFocus && activeRoomId == roomId) return;
|
||||
final room = client.getRoomById(roomId);
|
||||
if (room == null) {
|
||||
Logs().w('Can not display notification for unknown room $roomId');
|
||||
return;
|
||||
}
|
||||
if (room.notificationCount == 0) return;
|
||||
final event = Event.fromJson(eventUpdate.content, room);
|
||||
final title =
|
||||
room.getLocalizedDisplayname(MatrixLocals(L10n.of(widget.context)));
|
||||
room.getLocalizedDisplayname(MatrixLocals(L10n.of(widget.context)!));
|
||||
final body = event.getLocalizedBody(
|
||||
MatrixLocals(L10n.of(widget.context)),
|
||||
MatrixLocals(L10n.of(widget.context)!),
|
||||
withSenderNamePrefix:
|
||||
!room.isDirectChat || room.lastEvent.senderId == client.userID,
|
||||
!room.isDirectChat || room.lastEvent?.senderId == client.userID,
|
||||
plaintextBody: true,
|
||||
hideReply: true,
|
||||
hideEdit: true,
|
||||
@ -51,7 +57,7 @@ extension LocalNotificationsExtension on MatrixState {
|
||||
width: 56,
|
||||
height: 56,
|
||||
);
|
||||
File appIconFile;
|
||||
File? appIconFile;
|
||||
if (appIconUrl != null) {
|
||||
final tempDirectory = await getApplicationSupportDirectory();
|
||||
final avatarDirectory =
|
||||
@ -68,15 +74,15 @@ extension LocalNotificationsExtension on MatrixState {
|
||||
body: body,
|
||||
replacesId: linuxNotificationIds[roomId] ?? 0,
|
||||
appName: AppConfig.applicationName,
|
||||
appIcon: appIconFile.path,
|
||||
appIcon: appIconFile?.path ?? '',
|
||||
actions: [
|
||||
NotificationAction(
|
||||
DesktopNotificationActions.dismiss.name,
|
||||
L10n.of(widget.context).dismiss,
|
||||
L10n.of(widget.context)!.dismiss,
|
||||
),
|
||||
NotificationAction(
|
||||
DesktopNotificationActions.seen.name,
|
||||
L10n.of(widget.context).markAsRead,
|
||||
L10n.of(widget.context)!.markAsRead,
|
||||
),
|
||||
],
|
||||
hints: [
|
||||
|
@ -1,3 +1,5 @@
|
||||
//@dart=2.12
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_app_lock/flutter_app_lock.dart';
|
||||
@ -12,7 +14,7 @@ import 'package:fluffychat/config/themes.dart';
|
||||
import 'layouts/one_page_card.dart';
|
||||
|
||||
class LockScreen extends StatefulWidget {
|
||||
const LockScreen({Key key}) : super(key: key);
|
||||
const LockScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_LockScreenState createState() => _LockScreenState();
|
||||
@ -37,7 +39,7 @@ class _LockScreenState extends State<LockScreen> {
|
||||
automaticallyImplyLeading: false,
|
||||
elevation: 0,
|
||||
centerTitle: true,
|
||||
title: Text(L10n.of(context).pleaseEnterYourPin),
|
||||
title: Text(L10n.of(context)!.pleaseEnterYourPin),
|
||||
backgroundColor: Colors.transparent,
|
||||
),
|
||||
extendBodyBehindAppBar: true,
|
||||
@ -78,7 +80,7 @@ class _LockScreenState extends State<LockScreen> {
|
||||
prefs.getString(SettingKeys.appLockKey))
|
||||
: const FlutterSecureStorage()
|
||||
.read(key: SettingKeys.appLockKey))) {
|
||||
AppLock.of(context).didUnlock();
|
||||
AppLock.of(context)!.didUnlock();
|
||||
} else {
|
||||
_textEditingController.clear();
|
||||
setState(() => _wrongInput = true);
|
||||
|
@ -1,9 +1,11 @@
|
||||
//@dart=2.12
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
class LogViewer extends StatefulWidget {
|
||||
const LogViewer({Key key}) : super(key: key);
|
||||
const LogViewer({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_LogViewerState createState() => _LogViewerState();
|
||||
|
@ -1,3 +1,5 @@
|
||||
//@dart=2.12
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@ -8,11 +10,11 @@ import 'package:fluffychat/widgets/adaptive_flat_button.dart';
|
||||
|
||||
class PermissionSliderDialog extends StatefulWidget {
|
||||
const PermissionSliderDialog({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.initialPermission = 0,
|
||||
}) : super(key: key);
|
||||
|
||||
Future<int> show(BuildContext context) => PlatformInfos.isCupertinoStyle
|
||||
Future<int?> show(BuildContext context) => PlatformInfos.isCupertinoStyle
|
||||
? showCupertinoDialog<int>(
|
||||
context: context,
|
||||
builder: (context) => this,
|
||||
@ -30,7 +32,7 @@ class PermissionSliderDialog extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _PermissionSliderDialogState extends State<PermissionSliderDialog> {
|
||||
int _permission;
|
||||
late int _permission;
|
||||
@override
|
||||
void initState() {
|
||||
_permission = widget.initialPermission;
|
||||
@ -40,7 +42,7 @@ class _PermissionSliderDialogState extends State<PermissionSliderDialog> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final title = Text(
|
||||
L10n.of(context).setPermissionsLevel,
|
||||
L10n.of(context)!.setPermissionsLevel,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
final content = Column(
|
||||
@ -48,9 +50,9 @@ class _PermissionSliderDialogState extends State<PermissionSliderDialog> {
|
||||
children: [
|
||||
Text('Level: ' +
|
||||
(_permission == 100
|
||||
? '$_permission (${L10n.of(context).admin})'
|
||||
? '$_permission (${L10n.of(context)!.admin})'
|
||||
: _permission >= 50
|
||||
? '$_permission (${L10n.of(context).moderator})'
|
||||
? '$_permission (${L10n.of(context)!.moderator})'
|
||||
: _permission.toString())),
|
||||
SizedBox(
|
||||
height: 56,
|
||||
@ -65,12 +67,12 @@ class _PermissionSliderDialogState extends State<PermissionSliderDialog> {
|
||||
);
|
||||
final buttons = [
|
||||
AdaptiveFlatButton(
|
||||
label: L10n.of(context).cancel,
|
||||
label: L10n.of(context)!.cancel,
|
||||
onPressed: () =>
|
||||
Navigator.of(context, rootNavigator: false).pop<int>(null),
|
||||
),
|
||||
AdaptiveFlatButton(
|
||||
label: L10n.of(context).confirm,
|
||||
label: L10n.of(context)!.confirm,
|
||||
onPressed: () =>
|
||||
Navigator.of(context, rootNavigator: false).pop<int>(_permission),
|
||||
),
|
||||
|
@ -1,3 +1,5 @@
|
||||
//@dart=2.12
|
||||
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
@ -16,9 +18,9 @@ class ProfileBottomSheet extends StatelessWidget {
|
||||
final String userId;
|
||||
final BuildContext outerContext;
|
||||
const ProfileBottomSheet({
|
||||
@required this.userId,
|
||||
@required this.outerContext,
|
||||
Key key,
|
||||
required this.userId,
|
||||
required this.outerContext,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
void _startDirectChat(BuildContext context) async {
|
||||
@ -28,7 +30,7 @@ class ProfileBottomSheet extends StatelessWidget {
|
||||
future: () => client.startDirectChat(userId),
|
||||
);
|
||||
if (result.error == null) {
|
||||
VRouter.of(context).toSegments(['rooms', result.result]);
|
||||
VRouter.of(context).toSegments(['rooms', result.result!]);
|
||||
Navigator.of(context, rootNavigator: false).pop();
|
||||
return;
|
||||
}
|
||||
@ -52,7 +54,7 @@ class ProfileBottomSheet extends StatelessWidget {
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_downward_outlined),
|
||||
onPressed: Navigator.of(context, rootNavigator: false).pop,
|
||||
tooltip: L10n.of(context).close,
|
||||
tooltip: L10n.of(context)!.close,
|
||||
),
|
||||
),
|
||||
body: FutureBuilder<Profile>(
|
||||
@ -69,19 +71,20 @@ class ProfileBottomSheet extends StatelessWidget {
|
||||
alignment: Alignment.center,
|
||||
color: Theme.of(context).secondaryHeaderColor,
|
||||
child: snapshot.hasError
|
||||
? Text(snapshot.error
|
||||
? Text(snapshot.error!
|
||||
.toLocalizedString(context))
|
||||
: const CircularProgressIndicator
|
||||
.adaptive(strokeWidth: 2),
|
||||
)
|
||||
: ContentBanner(
|
||||
profile.avatarUrl,
|
||||
profile.avatarUrl!,
|
||||
defaultIcon: Icons.person_outline,
|
||||
client: Matrix.of(context).client,
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(profile?.displayName ?? userId.localpart),
|
||||
title: Text(
|
||||
profile?.displayName ?? userId.localpart ?? ''),
|
||||
subtitle: Text(userId),
|
||||
trailing: const Icon(Icons.account_box_outlined),
|
||||
),
|
||||
@ -90,7 +93,7 @@ class ProfileBottomSheet extends StatelessWidget {
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () => _startDirectChat(context),
|
||||
label: Text(L10n.of(context).newChat),
|
||||
label: Text(L10n.of(context)!.newChat),
|
||||
icon: const Icon(Icons.send_outlined),
|
||||
),
|
||||
),
|
||||
|
@ -1,3 +1,5 @@
|
||||
//@dart=2.12
|
||||
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
@ -16,12 +18,12 @@ import '../utils/localized_exception_extension.dart';
|
||||
class PublicRoomBottomSheet extends StatelessWidget {
|
||||
final String roomAlias;
|
||||
final BuildContext outerContext;
|
||||
final PublicRoomsChunk chunk;
|
||||
final PublicRoomsChunk? chunk;
|
||||
const PublicRoomBottomSheet({
|
||||
@required this.roomAlias,
|
||||
@required this.outerContext,
|
||||
required this.roomAlias,
|
||||
required this.outerContext,
|
||||
this.chunk,
|
||||
Key key,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
void _joinRoom(BuildContext context) async {
|
||||
@ -31,11 +33,11 @@ class PublicRoomBottomSheet extends StatelessWidget {
|
||||
future: () => client.joinRoom(roomAlias),
|
||||
);
|
||||
if (result.error == null) {
|
||||
if (client.getRoomById(result.result) == null) {
|
||||
if (client.getRoomById(result.result!) == null) {
|
||||
await client.onSync.stream.firstWhere(
|
||||
(sync) => sync.rooms?.join?.containsKey(result.result) ?? false);
|
||||
}
|
||||
VRouter.of(context).toSegments(['rooms', result.result]);
|
||||
VRouter.of(context).toSegments(['rooms', result.result!]);
|
||||
Navigator.of(context, rootNavigator: false).pop();
|
||||
return;
|
||||
}
|
||||
@ -46,6 +48,7 @@ class PublicRoomBottomSheet extends StatelessWidget {
|
||||
(r.aliases?.contains(roomAlias) ?? false);
|
||||
|
||||
Future<PublicRoomsChunk> _search(BuildContext context) async {
|
||||
final chunk = this.chunk;
|
||||
if (chunk != null) return chunk;
|
||||
final query = await Matrix.of(context).client.queryPublicRooms(
|
||||
server: roomAlias.domain,
|
||||
@ -53,16 +56,15 @@ class PublicRoomBottomSheet extends StatelessWidget {
|
||||
genericSearchTerm: roomAlias,
|
||||
),
|
||||
);
|
||||
if (!query.chunk.any(_testRoom) ?? true) {
|
||||
throw (L10n.of(context).noRoomsFound);
|
||||
if (!query.chunk.any(_testRoom)) {
|
||||
throw (L10n.of(context)!.noRoomsFound);
|
||||
}
|
||||
return query.chunk.firstWhere(_testRoom);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final roomAlias =
|
||||
this.roomAlias ?? chunk.canonicalAlias ?? chunk.aliases?.first ?? '';
|
||||
final roomAlias = this.roomAlias;
|
||||
return Center(
|
||||
child: SizedBox(
|
||||
width: min(
|
||||
@ -84,12 +86,12 @@ class PublicRoomBottomSheet extends StatelessWidget {
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_downward_outlined),
|
||||
onPressed: Navigator.of(context, rootNavigator: false).pop,
|
||||
tooltip: L10n.of(context).close,
|
||||
tooltip: L10n.of(context)!.close,
|
||||
),
|
||||
actions: [
|
||||
TextButton.icon(
|
||||
onPressed: () => _joinRoom(context),
|
||||
label: Text(L10n.of(context).joinRoom),
|
||||
label: Text(L10n.of(context)!.joinRoom),
|
||||
icon: const Icon(Icons.login_outlined),
|
||||
),
|
||||
],
|
||||
@ -108,26 +110,27 @@ class PublicRoomBottomSheet extends StatelessWidget {
|
||||
color: Theme.of(context).secondaryHeaderColor,
|
||||
child: snapshot.hasError
|
||||
? Text(
|
||||
snapshot.error.toLocalizedString(context))
|
||||
snapshot.error!.toLocalizedString(context))
|
||||
: const CircularProgressIndicator.adaptive(
|
||||
strokeWidth: 2),
|
||||
)
|
||||
else
|
||||
ContentBanner(
|
||||
profile.avatarUrl,
|
||||
profile.avatarUrl!,
|
||||
height: 156,
|
||||
defaultIcon: Icons.person_outline,
|
||||
client: Matrix.of(context).client,
|
||||
),
|
||||
ListTile(
|
||||
title: Text(profile?.name ?? roomAlias.localpart),
|
||||
title:
|
||||
Text(profile?.name ?? roomAlias.localpart ?? ''),
|
||||
subtitle: Text(
|
||||
'${L10n.of(context).participant}: ${profile?.numJoinedMembers ?? 0}'),
|
||||
'${L10n.of(context)!.participant}: ${profile?.numJoinedMembers ?? 0}'),
|
||||
trailing: const Icon(Icons.account_box_outlined),
|
||||
),
|
||||
if (profile?.topic != null && profile.topic.isNotEmpty)
|
||||
if (profile?.topic?.isNotEmpty ?? false)
|
||||
ListTile(
|
||||
subtitle: Html(data: profile.topic),
|
||||
subtitle: Html(data: profile!.topic!),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -1,13 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
//@dart=2.12
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/utils/sentry_controller.dart';
|
||||
|
||||
class SentrySwitchListTile extends StatefulWidget {
|
||||
final String label;
|
||||
|
||||
const SentrySwitchListTile.adaptive({Key key, this.label}) : super(key: key);
|
||||
const SentrySwitchListTile.adaptive({Key? key, required this.label})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
_SentrySwitchListTileState createState() => _SentrySwitchListTileState();
|
||||
@ -23,11 +24,11 @@ class _SentrySwitchListTileState extends State<SentrySwitchListTile> {
|
||||
builder: (context, snapshot) {
|
||||
_enabled = snapshot.data ?? false;
|
||||
return SwitchListTile.adaptive(
|
||||
title: Text(widget.label ?? L10n.of(context).sendBugReports),
|
||||
title: Text(widget.label),
|
||||
value: _enabled,
|
||||
onChanged: (b) =>
|
||||
SentryController.toggleSentryAction(context, b).then(
|
||||
(_) => setState(() => null),
|
||||
(_) => setState(() {}),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
@ -1,3 +1,5 @@
|
||||
//@dart=2.12
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
@ -9,8 +11,8 @@ class UnreadBadgeBackButton extends StatelessWidget {
|
||||
final String roomId;
|
||||
|
||||
const UnreadBadgeBackButton({
|
||||
Key key,
|
||||
@required this.roomId,
|
||||
Key? key,
|
||||
required this.roomId,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
Loading…
Reference in New Issue
Block a user