Merge pull request #36 from krawieck/media-view

This commit is contained in:
Marcin Wojnarowski 2020-09-13 21:25:23 +02:00 committed by GitHub
commit abb107fbbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 222 additions and 30 deletions

View File

@ -13,6 +13,7 @@ import '../util/intl.dart';
import '../util/text_color.dart'; import '../util/text_color.dart';
import '../widgets/badge.dart'; import '../widgets/badge.dart';
import '../widgets/bottom_modal.dart'; import '../widgets/bottom_modal.dart';
import '../widgets/fullscreenable_image.dart';
import '../widgets/markdown_text.dart'; import '../widgets/markdown_text.dart';
class CommunityPage extends HookWidget { class CommunityPage extends HookWidget {
@ -251,16 +252,20 @@ class _CommunityOverview extends StatelessWidget {
Container( Container(
width: 83, width: 83,
height: 83, height: 83,
child: CachedNetworkImage( child: FullscreenableImage(
imageUrl: community.icon, url: community.icon,
imageBuilder: (context, imageProvider) => Container( child: CachedNetworkImage(
decoration: BoxDecoration( imageUrl: community.icon,
shape: BoxShape.circle, imageBuilder: (context, imageProvider) => Container(
image: DecorationImage( decoration: BoxDecoration(
fit: BoxFit.cover, shape: BoxShape.circle,
image: imageProvider, image: DecorationImage(
fit: BoxFit.cover,
image: imageProvider,
),
), ),
), ),
errorWidget: (_, __, ___) => Icon(Icons.warning),
), ),
), ),
), ),
@ -271,7 +276,13 @@ class _CommunityOverview extends StatelessWidget {
return Stack(children: [ return Stack(children: [
if (community.banner != null) if (community.banner != null)
CachedNetworkImage(imageUrl: community.banner), FullscreenableImage(
url: community.banner,
child: CachedNetworkImage(
imageUrl: community.banner,
errorWidget: (_, __, ___) => Container(),
),
),
SafeArea( SafeArea(
child: Padding( child: Padding(
padding: const EdgeInsets.only(top: 45), padding: const EdgeInsets.only(top: 45),

View File

@ -11,6 +11,7 @@ import '../util/goto.dart';
import '../util/text_color.dart'; import '../util/text_color.dart';
import '../widgets/badge.dart'; import '../widgets/badge.dart';
import '../widgets/bottom_modal.dart'; import '../widgets/bottom_modal.dart';
import '../widgets/fullscreenable_image.dart';
import '../widgets/markdown_text.dart'; import '../widgets/markdown_text.dart';
import 'communities_list.dart'; import 'communities_list.dart';
import 'users_list.dart'; import 'users_list.dart';
@ -159,17 +160,29 @@ class InstancePage extends HookWidget {
flexibleSpace: FlexibleSpaceBar( flexibleSpace: FlexibleSpaceBar(
background: Stack(children: [ background: Stack(children: [
if (site.site.banner != null) if (site.site.banner != null)
CachedNetworkImage(imageUrl: site.site.banner), FullscreenableImage(
url: site.site.banner,
child: CachedNetworkImage(
imageUrl: site.site.banner,
errorWidget: (_, __, ___) => Container(),
),
),
SafeArea( SafeArea(
child: Center( child: Center(
child: Column( child: Column(
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.only(top: 40), padding: const EdgeInsets.only(top: 40),
child: CachedNetworkImage( child: FullscreenableImage(
url: site.site.icon,
child: CachedNetworkImage(
width: 100, width: 100,
height: 100, height: 100,
imageUrl: site.site.icon), imageUrl: site.site.icon,
errorWidget: (_, __, ___) =>
Icon(Icons.warning),
),
),
), ),
Text(site.site.name, Text(site.site.name,
style: theme.textTheme.headline6), style: theme.textTheme.headline6),
@ -325,6 +338,8 @@ class _AboutTab extends HookWidget {
height: 50, height: 50,
width: 50, width: 50,
imageUrl: e.icon, imageUrl: e.icon,
errorWidget: (_, __, ___) =>
SizedBox(width: 50, height: 50),
imageBuilder: (context, imageProvider) => Container( imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
@ -373,6 +388,8 @@ class _AboutTab extends HookWidget {
height: 50, height: 50,
width: 50, width: 50,
imageUrl: e.avatar, imageUrl: e.avatar,
errorWidget: (_, __, ___) =>
SizedBox(width: 50, height: 50),
imageBuilder: (context, imageProvider) => Container( imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,

118
lib/pages/media_view.dart Normal file
View File

@ -0,0 +1,118 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:esys_flutter_share/esys_flutter_share.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:photo_view/photo_view.dart';
import '../widgets/bottom_modal.dart';
class MediaViewPage extends HookWidget {
final String url;
const MediaViewPage(this.url);
@override
Widget build(BuildContext context) {
final showButtons = useState(true);
final isZoomedOut = useState(true);
useEffect(() {
if (showButtons.value) {
SystemChrome.setEnabledSystemUIOverlays([
SystemUiOverlay.bottom,
SystemUiOverlay.top,
]);
} else {
SystemChrome.setEnabledSystemUIOverlays([]);
}
return null;
}, [showButtons.value]);
useEffect(
() => () => SystemChrome.setEnabledSystemUIOverlays([
SystemUiOverlay.bottom,
SystemUiOverlay.top,
]),
[]);
share() {
showModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
builder: (context) => BottomModal(
child: Column(
children: [
ListTile(
leading: Icon(Icons.link),
title: Text('Share link'),
onTap: () {
Navigator.of(context).pop();
Share.text('Share image url', url, 'text/plain');
},
),
ListTile(
leading: Icon(Icons.image),
title: Text('Share file'),
onTap: () {
Navigator.of(context).pop();
// TODO: share file
},
),
],
),
),
);
}
return Scaffold(
extendBodyBehindAppBar: true,
extendBody: true,
appBar: showButtons.value
? AppBar(
backgroundColor: Colors.black38,
shadowColor: Colors.transparent,
leading: CloseButton(),
actions: [
IconButton(
icon: Icon(Icons.share),
tooltip: 'share',
onPressed: share,
),
IconButton(
icon: Icon(Icons.file_download),
tooltip: 'download',
onPressed: () {},
),
],
)
: null,
body: Dismissible(
direction: DismissDirection.vertical,
onDismissed: (_) => Navigator.of(context).pop(),
key: const Key('media view'),
background: Container(color: Colors.black),
dismissThresholds: {
DismissDirection.vertical: 0,
},
confirmDismiss: (direction) => Future.value(isZoomedOut.value),
resizeDuration: null,
child: GestureDetector(
onTapUp: (details) => showButtons.value = !showButtons.value,
child: PhotoView(
scaleStateChangedCallback: (value) {
isZoomedOut.value = value == PhotoViewScaleState.zoomedOut ||
value == PhotoViewScaleState.initial;
},
minScale: PhotoViewComputedScale.contained,
initialScale: PhotoViewComputedScale.contained,
imageProvider: CachedNetworkImageProvider(url),
heroAttributes: PhotoViewHeroAttributes(tag: url),
loadingBuilder: (context, event) =>
Center(child: CircularProgressIndicator()),
),
),
),
);
}
}

View File

@ -48,6 +48,8 @@ class UsersListPage extends StatelessWidget {
height: 50, height: 50,
width: 50, width: 50,
imageUrl: users[i].avatar, imageUrl: users[i].avatar,
errorWidget: (_, __, ___) =>
SizedBox(height: 50, width: 50),
imageBuilder: (context, imageProvider) => Container( imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,

View File

@ -200,6 +200,7 @@ class Comment extends StatelessWidget {
), ),
), ),
), ),
errorWidget: (_, __, ___) => Container(),
), ),
), ),
), ),

View File

@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import '../pages/media_view.dart';
class FullscreenableImage extends StatelessWidget {
final String url;
final Widget child;
const FullscreenableImage({
Key key,
@required this.url,
@required this.child,
}) : super(key: key);
_onTap(BuildContext c) {
Navigator.of(c).push(MaterialPageRoute(
builder: (context) => MediaViewPage(url),
));
}
@override
Widget build(BuildContext context) => InkWell(
onTap: () => _onTap(context),
child: Hero(
tag: url,
child: child,
),
);
}

View File

@ -4,6 +4,7 @@ import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:markdown/markdown.dart' as md; import 'package:markdown/markdown.dart' as md;
import '../url_launcher.dart'; import '../url_launcher.dart';
import 'fullscreenable_image.dart';
class MarkdownText extends StatelessWidget { class MarkdownText extends StatelessWidget {
final String instanceUrl; final String instanceUrl;
@ -26,13 +27,16 @@ class MarkdownText extends StatelessWidget {
), ),
))); )));
}, },
imageBuilder: (uri, title, alt) => CachedNetworkImage( imageBuilder: (uri, title, alt) => FullscreenableImage(
imageUrl: uri.toString(), url: uri.toString(),
errorWidget: (context, url, error) => Row( child: CachedNetworkImage(
children: [ imageUrl: uri.toString(),
Icon(Icons.warning), errorWidget: (context, url, error) => Row(
Text("couldn't load image, ${error.toString()}") children: [
], Icon(Icons.warning),
Text("couldn't load image, ${error.toString()}")
],
),
), ),
), ),
); );

View File

@ -12,6 +12,7 @@ import '../url_launcher.dart';
import '../util/api_extensions.dart'; import '../util/api_extensions.dart';
import '../util/goto.dart'; import '../util/goto.dart';
import 'bottom_modal.dart'; import 'bottom_modal.dart';
import 'fullscreenable_image.dart';
import 'markdown_text.dart'; import 'markdown_text.dart';
enum MediaType { enum MediaType {
@ -25,7 +26,13 @@ MediaType whatType(String url) {
if (url == null) return MediaType.other; if (url == null) return MediaType.other;
// TODO: make detection more nuanced // TODO: make detection more nuanced
if (url.endsWith('.jpg') || url.endsWith('.png') || url.endsWith('.gif')) { if (url.endsWith('.jpg') ||
url.endsWith('.jpeg') ||
url.endsWith('.png') ||
url.endsWith('.gif') ||
url.endsWith('.webp') ||
url.endsWith('.bmp') ||
url.endsWith('.wbpm')) {
return MediaType.image; return MediaType.image;
} }
return MediaType.other; return MediaType.other;
@ -40,13 +47,6 @@ class Post extends StatelessWidget {
// == ACTIONS == // == ACTIONS ==
void _openFullImage() {
// TODO: fullscreen media view
print('OPEN FULL IMAGE');
}
// POST ACTIONS
void _savePost() { void _savePost() {
print('SAVE POST'); print('SAVE POST');
} }
@ -370,10 +370,11 @@ class Post extends StatelessWidget {
Widget postImage() { Widget postImage() {
assert(post.url != null); assert(post.url != null);
return InkWell( return FullscreenableImage(
onTap: _openFullImage, url: post.url,
child: CachedNetworkImage( child: CachedNetworkImage(
imageUrl: post.url, imageUrl: post.url,
errorWidget: (_, __, ___) => Icon(Icons.warning),
progressIndicatorBuilder: (context, url, progress) => progressIndicatorBuilder: (context, url, progress) =>
CircularProgressIndicator(value: progress.progress), CircularProgressIndicator(value: progress.progress),
), ),

View File

@ -96,6 +96,7 @@ class UserProfile extends HookWidget {
if (userViewSnap.data?.banner != null) if (userViewSnap.data?.banner != null)
CachedNetworkImage( CachedNetworkImage(
imageUrl: userViewSnap.data.banner, imageUrl: userViewSnap.data.banner,
errorWidget: (_, __, ___) => Container(),
) )
else else
Container( Container(
@ -154,6 +155,7 @@ class UserProfile extends HookWidget {
borderRadius: BorderRadius.all(Radius.circular(12)), borderRadius: BorderRadius.all(Radius.circular(12)),
child: CachedNetworkImage( child: CachedNetworkImage(
imageUrl: userViewSnap.data.avatar, imageUrl: userViewSnap.data.avatar,
errorWidget: (_, __, ___) => Container(),
), ),
), ),
), ),

View File

@ -471,6 +471,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.9.0" version: "1.9.0"
photo_view:
dependency: "direct main"
description:
name: photo_view
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.2"
platform: platform:
dependency: transitive dependency: transitive
description: description:

View File

@ -21,6 +21,7 @@ environment:
sdk: '>=2.7.0 <3.0.0' sdk: '>=2.7.0 <3.0.0'
dependencies: dependencies:
photo_view: ^0.10.2
url_launcher: ^5.5.1 url_launcher: ^5.5.1
markdown: ^2.1.8 markdown: ^2.1.8
flutter_markdown: ^0.4.3 flutter_markdown: ^0.4.3
@ -51,7 +52,6 @@ dev_dependencies:
mobx_codegen: ^1.1.0 mobx_codegen: ^1.1.0
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter. # The following section is specific to Flutter.
flutter: flutter:
# The following line ensures that the Material Icons font is # The following line ensures that the Material Icons font is