refactor: Migrate to null safety

This commit is contained in:
Krille Fear 2022-01-29 12:35:03 +01:00 committed by Christian Pauly
commit 55f0300f9f
163 changed files with 1759 additions and 1903 deletions

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'dart:async';
import 'dart:io';

View file

@ -13,17 +13,17 @@ import '../../../utils/url_launcher.dart';
class HtmlMessage extends StatelessWidget {
final String html;
final int maxLines;
final int? maxLines;
final Room room;
final TextStyle defaultTextStyle;
final TextStyle linkStyle;
final double emoteSize;
final TextStyle? defaultTextStyle;
final TextStyle? linkStyle;
final double? emoteSize;
const HtmlMessage({
Key key,
this.html,
Key? key,
required this.html,
this.maxLines,
this.room,
required this.room,
this.defaultTextStyle,
this.linkStyle,
this.emoteSize,
@ -52,7 +52,7 @@ class HtmlMessage extends StatelessWidget {
defaultTextStyle: defaultTextStyle,
emoteSize: emoteSize,
linkStyle: linkStyle ??
themeData.textTheme.bodyText2.copyWith(
themeData.textTheme.bodyText2!.copyWith(
color: themeData.colorScheme.secondary,
decoration: TextDecoration.underline,
),
@ -60,11 +60,11 @@ class HtmlMessage extends StatelessWidget {
maxLines: maxLines,
onLinkTap: (url) => UrlLauncher(context, url).launchUrl(),
onPillTap: (url) => UrlLauncher(context, url).launchUrl(),
getMxcUrl: (String mxc, double width, double height,
{bool animated = false}) {
getMxcUrl: (String mxc, double? width, double? height,
{bool? animated = false}) {
final ratio = MediaQuery.of(context).devicePixelRatio;
return Uri.parse(mxc)
?.getThumbnail(
.getThumbnail(
matrix.client,
width: (width ?? 800) * ratio,
height: (height ?? 800) * ratio,
@ -92,9 +92,6 @@ class HtmlMessage extends StatelessWidget {
return await matrix.store.getItem('${SettingKeys.codeLanguage}.$key');
},
getPillInfo: (String url) async {
if (room == null) {
return null;
}
final identityParts = url.parseIdentifierIntoParts();
final identifier = identityParts?.primaryIdentifier;
if (identifier == null) {
@ -108,13 +105,10 @@ class HtmlMessage extends StatelessWidget {
}
// there might still be a profile...
final profile = await room.client.getProfileFromUserId(identifier);
if (profile != null) {
return {
'displayname': profile.displayName,
'avatar_url': profile.avatarUrl.toString(),
};
}
return null;
return {
'displayname': profile.displayName,
'avatar_url': profile.avatarUrl.toString(),
};
}
if (identifier.sigil == '#') {
// we have an alias pill
@ -128,7 +122,7 @@ class HtmlMessage extends StatelessWidget {
// we have a room!
return {
'displayname':
r.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
r.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
'avatar_url': r.getState('m.room.avatar')?.content['url'],
};
}
@ -143,12 +137,12 @@ class HtmlMessage extends StatelessWidget {
}
return {
'displayname':
r.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
r.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)),
'avatar_url': r.getState('m.room.avatar')?.content['url'],
};
}
return null;
},
} as Future<Map<String, dynamic>> Function(String)?,
);
}
}

View file

@ -19,13 +19,13 @@ class ImageBubble extends StatefulWidget {
final bool tapToView;
final BoxFit fit;
final bool maxSize;
final Color backgroundColor;
final Color? backgroundColor;
final bool thumbnailOnly;
final bool animated;
final double width;
final double height;
final void Function() onLoaded;
final void Function() onTap;
final void Function()? onLoaded;
final void Function()? onTap;
const ImageBubble(
this.event, {
@ -39,7 +39,7 @@ class ImageBubble extends StatefulWidget {
this.height = 300,
this.animated = false,
this.onTap,
Key key,
Key? key,
}) : super(key: key);
@override
@ -48,17 +48,17 @@ class ImageBubble extends StatefulWidget {
class _ImageBubbleState extends State<ImageBubble> {
// for plaintext: holds the http URL for the thumbnail
String thumbnailUrl;
String? thumbnailUrl;
// for plaintext. holds the http URL for the thumbnial, without the animated flag
String thumbnailUrlNoAnimated;
String? thumbnailUrlNoAnimated;
// for plaintext: holds the http URL of the original
String attachmentUrl;
MatrixFile _file;
MatrixFile _thumbnail;
String? attachmentUrl;
MatrixFile? _file;
MatrixFile? _thumbnail;
bool _requestedThumbnailOnFailure = false;
// In case we have animated = false, this will hold the first frame so that we make
// sure that things are never animated
Widget _firstFrame;
Widget? _firstFrame;
// the mimetypes that we know how to render, from the flutter Image widget
final _knownMimetypes = <String>{
@ -82,8 +82,8 @@ class _ImageBubbleState extends State<ImageBubble> {
? widget.event.thumbnailMimetype.toLowerCase()
: widget.event.attachmentMimetype.toLowerCase();
MatrixFile get _displayFile => _file ?? _thumbnail;
String get displayUrl => widget.thumbnailOnly ? thumbnailUrl : attachmentUrl;
MatrixFile? get _displayFile => _file ?? _thumbnail;
String? get displayUrl => widget.thumbnailOnly ? thumbnailUrl : attachmentUrl;
dynamic _error;
@ -91,14 +91,14 @@ class _ImageBubbleState extends State<ImageBubble> {
try {
final res = await widget.event
.downloadAndDecryptAttachmentCached(getThumbnail: getThumbnail);
WidgetsBinding.instance.addPostFrameCallback((_) {
WidgetsBinding.instance!.addPostFrameCallback((_) {
if (getThumbnail) {
if (mounted) {
setState(() => _thumbnail = res);
}
} else {
if (widget.onLoaded != null) {
widget.onLoaded();
widget.onLoaded!();
}
if (mounted) {
setState(() => _file = res);
@ -106,7 +106,7 @@ class _ImageBubbleState extends State<ImageBubble> {
}
});
} catch (err) {
WidgetsBinding.instance.addPostFrameCallback((_) {
WidgetsBinding.instance!.addPostFrameCallback((_) {
if (mounted) {
setState(() => _error = err);
}
@ -114,7 +114,7 @@ class _ImageBubbleState extends State<ImageBubble> {
}
}
Widget frameBuilder(_, Widget child, int frame, __) {
Widget frameBuilder(_, Widget child, int? frame, __) {
// as servers might return animated gifs as thumbnails and we want them to *not* play
// animated, we'll have to store the first frame in a variable and display that instead
if (widget.animated) {
@ -135,12 +135,14 @@ class _ImageBubbleState extends State<ImageBubble> {
key: ValueKey(key),
fit: widget.fit,
),
network: (String url) => SvgPicture.network(
url,
key: ValueKey(url),
placeholderBuilder: (context) => getPlaceholderWidget(),
fit: widget.fit,
),
network: (String? url) => url == null
? Container()
: SvgPicture.network(
url,
key: ValueKey(url),
placeholderBuilder: (context) => getPlaceholderWidget(),
fit: widget.fit,
),
);
_contentRenderers['image/lottie+json'] = _ImageBubbleContentRenderer(
memory: (Uint8List bytes, String key) => Lottie.memory(
@ -151,14 +153,16 @@ class _ImageBubbleState extends State<ImageBubble> {
getErrorWidget(context, error),
animate: widget.animated,
),
network: (String url) => Lottie.network(
url,
key: ValueKey(url),
fit: widget.fit,
errorBuilder: (context, error, stacktrace) =>
getErrorWidget(context, error),
animate: widget.animated,
),
network: (String? url) => url == null
? Container()
: Lottie.network(
url,
key: ValueKey(url),
fit: widget.fit,
errorBuilder: (context, error, stacktrace) =>
getErrorWidget(context, error),
animate: widget.animated,
),
);
// add all the custom content renderer mimetypes to the known mimetypes set
@ -203,7 +207,7 @@ class _ImageBubbleState extends State<ImageBubble> {
OutlinedButton.icon(
style: OutlinedButton.styleFrom(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
primary: Theme.of(context).textTheme.bodyText1.color,
primary: Theme.of(context).textTheme.bodyText1!.color,
),
icon: const Icon(Icons.download_outlined),
onPressed: () => widget.event.saveFile(context),
@ -215,7 +219,7 @@ class _ImageBubbleState extends State<ImageBubble> {
),
),
const SizedBox(height: 8),
if (widget.event.sizeString != null) Text(widget.event.sizeString),
if (widget.event.sizeString != null) Text(widget.event.sizeString!),
const SizedBox(height: 8),
Text((error ?? _error).toString()),
],
@ -223,8 +227,8 @@ class _ImageBubbleState extends State<ImageBubble> {
);
}
Widget getPlaceholderWidget({Widget child}) {
Widget blurhash;
Widget getPlaceholderWidget({Widget? child}) {
Widget? blurhash;
if (widget.event.infoMap['xyz.amorgan.blurhash'] is String) {
final ratio =
widget.event.infoMap['w'] is int && widget.event.infoMap['h'] is int
@ -265,16 +269,16 @@ class _ImageBubbleState extends State<ImageBubble> {
: widget.event.thumbnailMxcUrl.toString();
final mimetype = getMimetype(!isOriginal);
if (_contentRenderers.containsKey(mimetype)) {
return _contentRenderers[mimetype].memory(_displayFile.bytes, key);
return _contentRenderers[mimetype]!.memory!(_displayFile!.bytes, key);
} else {
return Image.memory(
_displayFile.bytes,
_displayFile!.bytes,
key: ValueKey(key),
fit: widget.fit,
errorBuilder: (context, error, stacktrace) {
if (widget.event.hasThumbnail && !_requestedThumbnailOnFailure) {
_requestedThumbnailOnFailure = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
WidgetsBinding.instance!.addPostFrameCallback((_) {
setState(() {
_file = null;
_requestFile(getThumbnail: true);
@ -299,12 +303,12 @@ class _ImageBubbleState extends State<ImageBubble> {
final mimetype = getMimetype(_requestedThumbnailOnFailure);
if (displayUrl == attachmentUrl &&
_contentRenderers.containsKey(mimetype)) {
return _contentRenderers[mimetype].network(displayUrl);
return _contentRenderers[mimetype]!.network!(displayUrl);
} else {
return CachedNetworkImage(
// as we change the url on-error we need a key so that the widget actually updates
key: ValueKey(displayUrl),
imageUrl: displayUrl,
imageUrl: displayUrl!,
placeholder: (context, url) {
if (!widget.thumbnailOnly &&
displayUrl != thumbnailUrl &&
@ -313,7 +317,7 @@ class _ImageBubbleState extends State<ImageBubble> {
return FutureBuilder<bool>(
future: (() async {
return await DefaultCacheManager()
.getFileFromCache(thumbnailUrl) !=
.getFileFromCache(thumbnailUrl!) !=
null;
})(),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
@ -321,8 +325,8 @@ class _ImageBubbleState extends State<ImageBubble> {
return getPlaceholderWidget();
}
final effectiveUrl = snapshot.data == true
? thumbnailUrl
: thumbnailUrlNoAnimated;
? thumbnailUrl!
: thumbnailUrlNoAnimated!;
return CachedNetworkImage(
key: ValueKey(effectiveUrl),
imageUrl: effectiveUrl,
@ -348,7 +352,7 @@ class _ImageBubbleState extends State<ImageBubble> {
// the image failed to load but the event has a thumbnail attached....so we can
// try to load this one!
_requestedThumbnailOnFailure = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
WidgetsBinding.instance!.addPostFrameCallback((_) {
setState(() {
thumbnailUrl = widget.event
.getAttachmentUrl(
@ -382,11 +386,11 @@ class _ImageBubbleState extends State<ImageBubble> {
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
primary: Theme.of(context).textTheme.bodyText1.color,
primary: Theme.of(context).textTheme.bodyText1!.color,
),
onPressed: () => onTap(context),
child: Text(
L10n.of(context).tapToShowImage,
L10n.of(context)!.tapToShowImage,
overflow: TextOverflow.fade,
softWrap: false,
maxLines: 1,
@ -394,7 +398,7 @@ class _ImageBubbleState extends State<ImageBubble> {
),
if (widget.event.sizeString != null) ...[
const SizedBox(height: 8),
Text(widget.event.sizeString),
Text(widget.event.sizeString!),
]
],
));
@ -451,7 +455,7 @@ class _ImageBubbleState extends State<ImageBubble> {
void onTap(BuildContext context) {
if (widget.onTap != null) {
widget.onTap();
widget.onTap!();
return;
}
if (!widget.tapToView) return;
@ -476,8 +480,8 @@ class _ImageBubbleState extends State<ImageBubble> {
}
class _ImageBubbleContentRenderer {
final Widget Function(Uint8List, String) memory;
final Widget Function(String) network;
final Widget Function(Uint8List, String)? memory;
final Widget Function(String?)? network;
_ImageBubbleContentRenderer({this.memory, this.network});
}

View file

@ -11,13 +11,13 @@ class MapBubble extends StatelessWidget {
final double height;
final double radius;
const MapBubble({
this.latitude,
this.longitude,
required this.latitude,
required this.longitude,
this.zoom = 14.0,
this.width = 400,
this.height = 400,
this.radius = 10.0,
Key key,
Key? key,
}) : super(key: key);
@override

View file

@ -16,14 +16,14 @@ import 'verification_request_content.dart';
class Message extends StatelessWidget {
final Event event;
final Event nextEvent;
final void Function(Event) onSelect;
final void Function(Event) onAvatarTab;
final void Function(Event) onInfoTab;
final void Function(String) scrollToEventId;
final Event? nextEvent;
final void Function(Event)? onSelect;
final void Function(Event)? onAvatarTab;
final void Function(Event)? onInfoTab;
final void Function(String)? scrollToEventId;
final void Function(String) unfold;
final bool longPressSelect;
final bool selected;
final bool? longPressSelect;
final bool? selected;
final Timeline timeline;
const Message(this.event,
@ -33,10 +33,10 @@ class Message extends StatelessWidget {
this.onInfoTab,
this.onAvatarTab,
this.scrollToEventId,
@required this.unfold,
required this.unfold,
this.selected,
this.timeline,
Key key})
required this.timeline,
Key? key})
: super(key: key);
/// Indicates wheither the user may use a mouse instead
@ -61,14 +61,14 @@ class Message extends StatelessWidget {
var color = Theme.of(context).appBarTheme.backgroundColor;
final displayTime = event.type == EventTypes.RoomCreate ||
nextEvent == null ||
!event.originServerTs.sameEnvironment(nextEvent.originServerTs);
!event.originServerTs.sameEnvironment(nextEvent!.originServerTs);
final sameSender = nextEvent != null &&
[
EventTypes.Message,
EventTypes.Sticker,
EventTypes.Encrypted,
].contains(nextEvent.type)
? nextEvent.sender.id == event.sender.id && !displayTime
].contains(nextEvent!.type)
? nextEvent!.sender.id == event.sender.id && !displayTime
: false;
final textColor = ownMessage
? Colors.white
@ -119,7 +119,7 @@ class Message extends StatelessWidget {
: Avatar(
mxContent: event.sender.avatarUrl,
name: event.sender.calcDisplayname(),
onTap: () => onAvatarTab(event),
onTap: () => onAvatarTab!(event),
),
Expanded(
child: Column(
@ -152,10 +152,11 @@ class Message extends StatelessWidget {
clipBehavior: Clip.antiAlias,
child: InkWell(
onHover: (b) => useMouse = true,
onTap: !useMouse && longPressSelect
? () => null
: () => onSelect(event),
onLongPress: !longPressSelect ? null : () => onSelect(event),
onTap: !useMouse && longPressSelect!
? () {}
: () => onSelect!(event),
onLongPress:
!longPressSelect! ? null : () => onSelect!(event),
borderRadius: borderRadius,
child: Container(
decoration: BoxDecoration(
@ -175,13 +176,13 @@ class Message extends StatelessWidget {
children: <Widget>[
if (event.relationshipType ==
RelationshipTypes.reply)
FutureBuilder<Event>(
FutureBuilder<Event?>(
future: event.getReplyEvent(timeline),
builder: (BuildContext context, snapshot) {
final replyEvent = snapshot.hasData
? snapshot.data
? snapshot.data!
: Event(
eventId: event.relationshipEventId,
eventId: event.relationshipEventId!,
content: {
'msgtype': 'm.text',
'body': '...'
@ -195,7 +196,7 @@ class Message extends StatelessWidget {
return InkWell(
onTap: () {
if (scrollToEventId != null) {
scrollToEventId(replyEvent.eventId);
scrollToEventId!(replyEvent.eventId);
}
},
child: AbsorbPointer(
@ -300,7 +301,7 @@ class Message extends StatelessWidget {
return Center(
child: Container(
color: selected
color: selected!
? Theme.of(context).primaryColor.withAlpha(100)
: Theme.of(context).primaryColor.withAlpha(0),
constraints:

View file

@ -22,10 +22,10 @@ import 'sticker.dart';
class MessageContent extends StatelessWidget {
final Event event;
final Color textColor;
final void Function(Event) onInfoTab;
final Color? textColor;
final void Function(Event)? onInfoTab;
const MessageContent(this.event, {this.onInfoTab, Key key, this.textColor})
const MessageContent(this.event, {this.onInfoTab, Key? key, this.textColor})
: super(key: key);
void _verifyOrRequestKey(BuildContext context) async {
@ -33,15 +33,15 @@ class MessageContent extends StatelessWidget {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
event.type == EventTypes.Encrypted
? L10n.of(context).needPantalaimonWarning
? L10n.of(context)!.needPantalaimonWarning
: event.getLocalizedBody(
MatrixLocals(L10n.of(context)),
MatrixLocals(L10n.of(context)!),
),
)));
return;
}
final client = Matrix.of(context).client;
if (client.isUnknownSession && client.encryption.crossSigning.enabled) {
if (client.isUnknownSession && client.encryption!.crossSigning.enabled) {
await BootstrapDialog(
client: Matrix.of(context).client,
).show(context);
@ -55,7 +55,7 @@ class MessageContent extends StatelessWidget {
);
if (success.error == null) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(L10n.of(context).requestToReadOlderMessages)));
content: Text(L10n.of(context)!.requestToReadOlderMessages)));
}
}
}
@ -83,17 +83,17 @@ class MessageContent extends StatelessWidget {
if (PlatformInfos.isMobile) {
return AudioPlayerWidget(
event,
color: textColor,
color: textColor!,
);
}
return MessageDownloadContent(event, textColor);
return MessageDownloadContent(event, textColor!);
case MessageTypes.Video:
if (PlatformInfos.isMobile || PlatformInfos.isWeb) {
return EventVideoPlayer(event);
}
return MessageDownloadContent(event, textColor);
return MessageDownloadContent(event, textColor!);
case MessageTypes.File:
return MessageDownloadContent(event, textColor);
return MessageDownloadContent(event, textColor!);
case MessageTypes.Text:
case MessageTypes.Notice:
@ -115,7 +115,7 @@ class MessageContent extends StatelessWidget {
fontSize: bigEmotes ? fontSize * 3 : fontSize,
),
linkStyle: TextStyle(
color: textColor.withAlpha(150),
color: textColor!.withAlpha(150),
fontSize: bigEmotes ? fontSize * 3 : fontSize,
decoration: TextDecoration.underline,
),
@ -131,14 +131,12 @@ class MessageContent extends StatelessWidget {
textColor: buttonTextColor,
onPressed: () => _verifyOrRequestKey(context),
icon: const Icon(Icons.lock_outline),
label: L10n.of(context).encrypted,
label: L10n.of(context)!.encrypted,
);
case MessageTypes.Location:
final geoUri =
Uri.tryParse(event.content.tryGet<String>('geo_uri'));
if (geoUri != null &&
geoUri.scheme == 'geo' &&
geoUri.path != null) {
Uri.tryParse(event.content.tryGet<String>('geo_uri')!);
if (geoUri != null && geoUri.scheme == 'geo') {
final latlong = geoUri.path
.split(';')
.first
@ -152,8 +150,8 @@ class MessageContent extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
MapBubble(
latitude: latlong.first,
longitude: latlong.last,
latitude: latlong.first!,
longitude: latlong.last!,
),
const SizedBox(height: 6),
OutlinedButton.icon(
@ -161,7 +159,7 @@ class MessageContent extends StatelessWidget {
onPressed:
UrlLauncher(context, geoUri.toString()).launchUrl,
label: Text(
L10n.of(context).openInMaps,
L10n.of(context)!.openInMaps,
style: TextStyle(color: textColor),
),
),
@ -177,24 +175,24 @@ class MessageContent extends StatelessWidget {
return _ButtonContent(
onPressed: () => launch(event.body),
icon: const Icon(Icons.phone_outlined, color: Colors.green),
label: L10n.of(context).videoCall,
label: L10n.of(context)!.videoCall,
textColor: buttonTextColor,
);
}
if (event.redacted) {
return _ButtonContent(
label: L10n.of(context)
label: L10n.of(context)!
.redactedAnEvent(event.sender.calcDisplayname()),
icon: const Icon(Icons.delete_outlined),
textColor: buttonTextColor,
onPressed: () => onInfoTab(event),
onPressed: () => onInfoTab!(event),
);
}
final bigEmotes = event.onlyEmotes &&
event.numberEmotes > 0 &&
event.numberEmotes <= 10;
return LinkText(
text: event.getLocalizedBody(MatrixLocals(L10n.of(context)),
text: event.getLocalizedBody(MatrixLocals(L10n.of(context)!),
hideReply: true),
textStyle: TextStyle(
color: textColor,
@ -202,24 +200,22 @@ class MessageContent extends StatelessWidget {
decoration: event.redacted ? TextDecoration.lineThrough : null,
),
linkStyle: TextStyle(
color: textColor.withAlpha(150),
color: textColor!.withAlpha(150),
fontSize: bigEmotes ? fontSize * 3 : fontSize,
decoration: TextDecoration.underline,
),
onLinkTap: (url) => UrlLauncher(context, url).launchUrl(),
);
}
break;
default:
return _ButtonContent(
label: L10n.of(context)
label: L10n.of(context)!
.userSentUnknownEvent(event.sender.calcDisplayname(), event.type),
icon: const Icon(Icons.info_outlined),
textColor: buttonTextColor,
onPressed: () => onInfoTab(event),
onPressed: () => onInfoTab!(event),
);
}
return Container(); // else flutter analyze complains
}
}
@ -227,14 +223,14 @@ class _ButtonContent extends StatelessWidget {
final void Function() onPressed;
final String label;
final Icon icon;
final Color textColor;
final Color? textColor;
const _ButtonContent({
@required this.label,
@required this.icon,
@required this.textColor,
@required this.onPressed,
Key key,
required this.label,
required this.icon,
required this.textColor,
required this.onPressed,
Key? key,
}) : super(key: key);
@override

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';

View file

@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:collection/collection.dart' show IterableExtension;
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:matrix/matrix.dart';
@ -14,14 +15,14 @@ class MessageReactions extends StatelessWidget {
final Event event;
final Timeline timeline;
const MessageReactions(this.event, this.timeline, {Key key})
const MessageReactions(this.event, this.timeline, {Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
final allReactionEvents =
event.aggregatedEvents(timeline, RelationshipTypes.reaction);
final reactionMap = <String, _ReactionEntry>{};
final reactionMap = <String?, _ReactionEntry>{};
final client = Matrix.of(context).client;
for (final e in allReactionEvents) {
@ -35,9 +36,9 @@ class MessageReactions extends StatelessWidget {
reactors: [],
);
}
reactionMap[key].count++;
reactionMap[key].reactors.add(e.sender);
reactionMap[key].reacted |= e.senderId == e.room.client.userID;
reactionMap[key]!.count++;
reactionMap[key]!.reactors!.add(e.sender);
reactionMap[key]!.reacted |= e.senderId == e.room.client.userID;
}
}
@ -52,11 +53,9 @@ class MessageReactions extends StatelessWidget {
reacted: r.reacted,
onTap: () {
if (r.reacted) {
final evt = allReactionEvents.firstWhere(
(e) =>
e.senderId == e.room.client.userID &&
e.content['m.relates_to']['key'] == r.key,
orElse: () => null);
final evt = allReactionEvents.firstWhereOrNull((e) =>
e.senderId == e.room.client.userID &&
e.content['m.relates_to']['key'] == r.key);
if (evt != null) {
showFutureLoadingDialog(
context: context,
@ -67,7 +66,7 @@ class MessageReactions extends StatelessWidget {
showFutureLoadingDialog(
context: context,
future: () =>
event.room.sendReaction(event.eventId, r.key));
event.room.sendReaction(event.eventId, r.key!));
}
},
onLongPress: () async => await _AdaptableReactorsDialog(
@ -91,11 +90,11 @@ class MessageReactions extends StatelessWidget {
}
class _Reaction extends StatelessWidget {
final String reactionKey;
final int count;
final bool reacted;
final void Function() onTap;
final void Function() onLongPress;
final String? reactionKey;
final int? count;
final bool? reacted;
final void Function()? onTap;
final void Function()? onLongPress;
const _Reaction({
this.reactionKey,
@ -113,11 +112,11 @@ class _Reaction extends StatelessWidget {
final color = Theme.of(context).scaffoldBackgroundColor;
final fontSize = DefaultTextStyle.of(context).style.fontSize;
Widget content;
if (reactionKey.startsWith('mxc://')) {
final src = Uri.parse(reactionKey)?.getThumbnail(
if (reactionKey!.startsWith('mxc://')) {
final src = Uri.parse(reactionKey!).getThumbnail(
Matrix.of(context).client,
width: 9999,
height: fontSize * MediaQuery.of(context).devicePixelRatio,
height: fontSize! * MediaQuery.of(context).devicePixelRatio,
method: ThumbnailMethod.scale,
);
content = Row(
@ -136,7 +135,7 @@ class _Reaction extends StatelessWidget {
],
);
} else {
var renderKey = Characters(reactionKey);
var renderKey = Characters(reactionKey!);
if (renderKey.length > 10) {
renderKey = renderKey.getRange(0, 9) + Characters('');
}
@ -147,13 +146,13 @@ class _Reaction extends StatelessWidget {
));
}
return InkWell(
onTap: () => onTap != null ? onTap() : null,
onLongPress: () => onLongPress != null ? onLongPress() : null,
onTap: () => onTap != null ? onTap!() : null,
onLongPress: () => onLongPress != null ? onLongPress!() : null,
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
child: Container(
decoration: BoxDecoration(
color: color,
border: reacted
border: reacted!
? Border.all(
width: 1,
color: Theme.of(context).primaryColor,
@ -169,25 +168,30 @@ class _Reaction extends StatelessWidget {
}
class _ReactionEntry {
String key;
String? key;
int count;
bool reacted;
List<User> reactors;
List<User>? reactors;
_ReactionEntry({this.key, this.count, this.reacted, this.reactors});
_ReactionEntry({
this.key,
required this.count,
required this.reacted,
this.reactors,
});
}
class _AdaptableReactorsDialog extends StatelessWidget {
final Client client;
final _ReactionEntry reactionEntry;
final Client? client;
final _ReactionEntry? reactionEntry;
const _AdaptableReactorsDialog({
Key key,
Key? key,
this.client,
this.reactionEntry,
}) : super(key: key);
Future<bool> show(BuildContext context) => PlatformInfos.isCupertinoStyle
Future<bool?> show(BuildContext context) => PlatformInfos.isCupertinoStyle
? showCupertinoDialog(
context: context,
builder: (context) => this,
@ -209,20 +213,20 @@ class _AdaptableReactorsDialog extends StatelessWidget {
runSpacing: 4.0,
alignment: WrapAlignment.center,
children: <Widget>[
for (var reactor in reactionEntry.reactors)
for (var reactor in reactionEntry!.reactors!)
Chip(
avatar: Avatar(
mxContent: reactor.avatarUrl,
name: reactor.displayName,
client: client,
),
label: Text(reactor.displayName),
label: Text(reactor.displayName!),
),
],
),
);
final title = Center(child: Text(reactionEntry.key));
final title = Center(child: Text(reactionEntry!.key!));
return PlatformInfos.isCupertinoStyle
? CupertinoAlertDialog(

View file

@ -10,21 +10,23 @@ import 'html_message.dart';
class ReplyContent extends StatelessWidget {
final Event replyEvent;
final bool lightText;
final Timeline timeline;
final Timeline? timeline;
const ReplyContent(this.replyEvent,
{this.lightText = false, Key key, this.timeline})
: super(key: key);
const ReplyContent(
this.replyEvent, {
this.lightText = false,
Key? key,
this.timeline,
}) : super(key: key);
@override
Widget build(BuildContext context) {
Widget replyBody;
final displayEvent = replyEvent != null && timeline != null
? replyEvent.getDisplayEvent(timeline)
: replyEvent;
final timeline = this.timeline;
final displayEvent =
timeline != null ? replyEvent.getDisplayEvent(timeline) : replyEvent;
final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor;
if (displayEvent != null &&
AppConfig.renderHtml &&
if (AppConfig.renderHtml &&
[EventTypes.Message, EventTypes.Encrypted]
.contains(displayEvent.type) &&
[MessageTypes.Text, MessageTypes.Notice, MessageTypes.Emote]
@ -32,16 +34,16 @@ class ReplyContent extends StatelessWidget {
!displayEvent.redacted &&
displayEvent.content['format'] == 'org.matrix.custom.html' &&
displayEvent.content['formatted_body'] is String) {
String html = displayEvent.content['formatted_body'];
String? html = displayEvent.content['formatted_body'];
if (displayEvent.messageType == MessageTypes.Emote) {
html = '* $html';
}
replyBody = HtmlMessage(
html: html,
html: html!,
defaultTextStyle: TextStyle(
color: lightText
? Colors.white
: Theme.of(context).textTheme.bodyText2.color,
: Theme.of(context).textTheme.bodyText2!.color,
fontSize: fontSize,
),
maxLines: 1,
@ -50,18 +52,17 @@ class ReplyContent extends StatelessWidget {
);
} else {
replyBody = Text(
displayEvent?.getLocalizedBody(
MatrixLocals(L10n.of(context)),
withSenderNamePrefix: false,
hideReply: true,
) ??
'',
displayEvent.getLocalizedBody(
MatrixLocals(L10n.of(context)!),
withSenderNamePrefix: false,
hideReply: true,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
color: lightText
? Colors.white
: Theme.of(context).textTheme.bodyText2.color,
: Theme.of(context).textTheme.bodyText2!.color,
fontSize: fontSize,
),
);
@ -81,7 +82,7 @@ class ReplyContent extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
(displayEvent?.sender?.calcDisplayname() ?? '') + ':',
displayEvent.sender.calcDisplayname() + ':',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(

View file

@ -9,16 +9,16 @@ import '../../../config/app_config.dart';
class StateMessage extends StatelessWidget {
final Event event;
final void Function(String) unfold;
const StateMessage(this.event, {@required this.unfold, Key key})
const StateMessage(this.event, {required this.unfold, Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
if (event.unsigned['im.fluffychat.collapsed_state_event'] == true) {
if (event.unsigned!['im.fluffychat.collapsed_state_event'] == true) {
return Container();
}
final int counter =
event.unsigned['im.fluffychat.collapsed_state_event_count'] ?? 0;
event.unsigned!['im.fluffychat.collapsed_state_event_count'] ?? 0;
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
@ -40,18 +40,18 @@ class StateMessage extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
Text(
event.getLocalizedBody(MatrixLocals(L10n.of(context))),
event.getLocalizedBody(MatrixLocals(L10n.of(context)!)),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14 * AppConfig.fontSizeFactor,
color: Theme.of(context).textTheme.bodyText2.color,
color: Theme.of(context).textTheme.bodyText2!.color,
decoration:
event.redacted ? TextDecoration.lineThrough : null,
),
),
if (counter != 0)
Text(
L10n.of(context).moreEvents(counter),
L10n.of(context)!.moreEvents(counter),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14 * AppConfig.fontSizeFactor,

View file

@ -10,14 +10,14 @@ import 'image_bubble.dart';
class Sticker extends StatefulWidget {
final Event event;
const Sticker(this.event, {Key key}) : super(key: key);
const Sticker(this.event, {Key? key}) : super(key: key);
@override
_StickerState createState() => _StickerState();
}
class _StickerState extends State<Sticker> {
bool animated;
bool? animated;
@override
Widget build(BuildContext context) {
@ -31,7 +31,7 @@ class _StickerState extends State<Sticker> {
showOkAlertDialog(
context: context,
message: widget.event.body,
okLabel: L10n.of(context).ok,
okLabel: L10n.of(context)!.ok,
);
},
animated: animated ?? AppConfig.autoplayImages,

View file

@ -9,7 +9,8 @@ class VerificationRequestContent extends StatelessWidget {
final Event event;
final Timeline timeline;
const VerificationRequestContent({this.event, this.timeline, Key key})
const VerificationRequestContent(
{required this.event, required this.timeline, Key? key})
: super(key: key);
@override
@ -50,10 +51,10 @@ class VerificationRequestContent extends StatelessWidget {
Text(canceled
? 'Error ${cancel.first.content.tryGet<String>('code')}: ${cancel.first.content.tryGet<String>('reason')}'
: (fullyDone
? L10n.of(context).verifySuccess
? L10n.of(context)!.verifySuccess
: (started
? L10n.of(context).loadingPleaseWait
: L10n.of(context).newVerificationRequest)))
? L10n.of(context)!.loadingPleaseWait
: L10n.of(context)!.newVerificationRequest)))
],
),
),

View file

@ -1,5 +1,3 @@
//@dart=2.12
import 'dart:io';
import 'package:flutter/foundation.dart';