feat: Add rendering of lottie files

This commit is contained in:
Sorunome 2021-07-11 14:30:39 +02:00
parent fbcc8b5551
commit 0319eefbd8
No known key found for this signature in database
GPG Key ID: B19471D07FC9BE9C
3 changed files with 66 additions and 17 deletions

View File

@ -1,3 +1,5 @@
import 'dart:typed_data';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/pages/image_viewer.dart'; import 'package:fluffychat/pages/image_viewer.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -5,6 +7,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter_blurhash/flutter_blurhash.dart'; import 'package:flutter_blurhash/flutter_blurhash.dart';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:lottie/lottie.dart';
import '../../utils/matrix_sdk_extensions.dart/event_extension.dart'; import '../../utils/matrix_sdk_extensions.dart/event_extension.dart';
import '../matrix.dart'; import '../matrix.dart';
@ -42,10 +45,15 @@ class _ImageBubbleState extends State<ImageBubble> {
MatrixFile _thumbnail; MatrixFile _thumbnail;
bool _requestedThumbnailOnFailure = false; bool _requestedThumbnailOnFailure = false;
bool get isSvg => // overrides for certain mimetypes if they need different images to render
widget.event.attachmentMimetype.split('+').first == 'image/svg'; // memory are for in-memory renderers (e2ee rooms), network for network url renderers.
bool get isThumbnailSvg => // The map values themself are set in initState() as they need to be able to access
widget.event.thumbnailMimetype.split('+').first == 'image/svg'; // `this`.
final _contentRenderers = <String, _ImageBubbleContentRenderer>{};
String getMimetype([bool thumbnail = false]) => thumbnail
? widget.event.thumbnailMimetype
: widget.event.attachmentMimetype;
MatrixFile get _displayFile => _file ?? _thumbnail; MatrixFile get _displayFile => _file ?? _thumbnail;
String get displayUrl => widget.thumbnailOnly ? thumbnailUrl : attachmentUrl; String get displayUrl => widget.thumbnailOnly ? thumbnailUrl : attachmentUrl;
@ -81,6 +89,32 @@ class _ImageBubbleState extends State<ImageBubble> {
@override @override
void initState() { void initState() {
_contentRenderers['image/svg+xml'] = _ImageBubbleContentRenderer(
memory: (Uint8List bytes, String key) => SvgPicture.memory(
bytes,
key: ValueKey(key),
fit: widget.fit,
),
network: (String url) => SvgPicture.network(
url,
key: ValueKey(url),
placeholderBuilder: (context) => getPlaceholderWidget(),
fit: widget.fit,
),
);
_contentRenderers['image/lottie+json'] = _ImageBubbleContentRenderer(
memory: (Uint8List bytes, String key) => Lottie.memory(
bytes,
key: ValueKey(key),
fit: widget.fit,
),
network: (String url) => Lottie.network(
url,
key: ValueKey(url),
fit: widget.fit,
),
);
thumbnailUrl = widget.event thumbnailUrl = widget.event
.getAttachmentUrl(getThumbnail: true, animated: true) .getAttachmentUrl(getThumbnail: true, animated: true)
?.toString(); ?.toString();
@ -148,12 +182,9 @@ class _ImageBubbleState extends State<ImageBubble> {
final key = isOriginal final key = isOriginal
? widget.event.attachmentMxcUrl ? widget.event.attachmentMxcUrl
: widget.event.thumbnailMxcUrl; : widget.event.thumbnailMxcUrl;
if (isOriginal ? isSvg : isThumbnailSvg) { final mimetype = getMimetype(!isOriginal);
return SvgPicture.memory( if (_contentRenderers.containsKey(mimetype)) {
_displayFile.bytes, return _contentRenderers[mimetype].memory(_displayFile.bytes, key);
key: ValueKey(key),
fit: widget.fit,
);
} else { } else {
return Image.memory( return Image.memory(
_displayFile.bytes, _displayFile.bytes,
@ -164,14 +195,10 @@ class _ImageBubbleState extends State<ImageBubble> {
} }
Widget getNetworkWidget() { Widget getNetworkWidget() {
final mimetype = getMimetype(!_requestedThumbnailOnFailure);
if (displayUrl == attachmentUrl && if (displayUrl == attachmentUrl &&
(_requestedThumbnailOnFailure ? isSvg : isThumbnailSvg)) { _contentRenderers.containsKey(mimetype)) {
return SvgPicture.network( return _contentRenderers[mimetype].network(displayUrl);
displayUrl,
key: ValueKey(displayUrl),
placeholderBuilder: (context) => getPlaceholderWidget(),
fit: widget.fit,
);
} else { } else {
return CachedNetworkImage( return CachedNetworkImage(
// as we change the url on-error we need a key so that the widget actually updates // as we change the url on-error we need a key so that the widget actually updates
@ -272,3 +299,10 @@ class _ImageBubbleState extends State<ImageBubble> {
); );
} }
} }
class _ImageBubbleContentRenderer {
final Widget Function(Uint8List, String) memory;
final Widget Function(String) network;
_ImageBubbleContentRenderer({this.memory, this.network});
}

View File

@ -43,6 +43,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.0" version: "2.0.0"
archive:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.2"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -597,6 +604,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.1" version: "1.0.1"
lottie:
dependency: "direct main"
description:
name: lottie
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
markdown: markdown:
dependency: transitive dependency: transitive
description: description:

View File

@ -41,6 +41,7 @@ dependencies:
image_picker: ^0.8.1+3 image_picker: ^0.8.1+3
intl: any intl: any
localstorage: ^4.0.0+1 localstorage: ^4.0.0+1
lottie: ^1.1.0
matrix: ^0.1.7 matrix: ^0.1.7
mime_type: ^1.0.0 mime_type: ^1.0.0
native_imaging: native_imaging: