Merge pull request #36 from krawieck/media-view
This commit is contained in:
commit
abb107fbbf
|
@ -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),
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -200,6 +200,7 @@ class Comment extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
errorWidget: (_, __, ___) => Container(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
|
@ -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()}")
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -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),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue