feat: Added new image detail screen

This commit is contained in:
Deven Joshi
2020-11-12 17:04:01 +05:30
parent 264b11362e
commit 3ad6a25a82
6 changed files with 345 additions and 49 deletions
+94 -16
View File
@@ -1,34 +1,112 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:jiffy/jiffy.dart';
import 'package:photo_view/photo_view.dart';
import 'package:stream_chat_flutter/src/image_footer.dart';
import 'package:stream_chat_flutter/src/image_header.dart';
import '../stream_chat_flutter.dart';
/// A full screen image widget
class FullScreenImage extends StatelessWidget {
class FullScreenImage extends StatefulWidget {
/// The url of the image
final String url;
final List<String> urls;
final int startIndex;
final String userName;
final DateTime sentAt;
/// Instantiate a new FullScreenImage
const FullScreenImage({
Key key,
@required this.url,
@required this.urls,
this.startIndex = 0,
this.userName = '',
this.sentAt,
}) : super(key: key);
@override
_FullScreenImageState createState() => _FullScreenImageState();
}
class _FullScreenImageState extends State<FullScreenImage>
with SingleTickerProviderStateMixin {
bool _optionsShown = true;
AnimationController _controller;
PageController _pageController;
int _currentPage;
@override
void initState() {
super.initState();
_controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 300));
_pageController = PageController(initialPage: widget.startIndex);
_currentPage = widget.startIndex;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
iconTheme: IconThemeData(
color: Colors.white,
),
),
body: PhotoView(
imageProvider: CachedNetworkImageProvider(url),
maxScale: PhotoViewComputedScale.covered,
minScale: PhotoViewComputedScale.contained,
heroAttributes: PhotoViewHeroAttributes(
tag: url,
),
body: Stack(
children: [
AnimatedBuilder(
animation: _controller,
builder: (context, snapshot) {
return PageView.builder(
controller: _pageController,
onPageChanged: (val) {
setState(() {
_currentPage = val;
});
},
itemBuilder: (context, position) {
return PhotoView(
imageProvider: CachedNetworkImageProvider(widget.urls[position]),
maxScale: PhotoViewComputedScale.covered,
minScale: PhotoViewComputedScale.contained,
heroAttributes: PhotoViewHeroAttributes(
tag: widget.urls,
),
backgroundDecoration: BoxDecoration(
color: ColorTween(
begin: StreamChatTheme.of(context)
.channelTheme
.channelHeaderTheme
.color,
end: Colors.black).lerp(_controller.value),
),
onTapUp: (a, b, c) {
setState(() {
_optionsShown = !_optionsShown;
});
if(_controller.isCompleted) {
_controller.reverse();
} else {
_controller.forward();
}
},
);
},
itemCount: widget.urls.length,
);
}
),
AnimatedOpacity(
opacity: _optionsShown ? 1.0 : 0.0,
duration: Duration(milliseconds: 300),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ImageHeader(userName: widget.userName, sentAt: 'Sent at${Jiffy(widget.sentAt.toLocal())
.format(' HH:mm')}',),
ImageFooter(currentPage: _currentPage, totalPages: widget.urls.length,),
],
),
),
],
),
);
}
+8 -4
View File
@@ -67,9 +67,11 @@ class GiphyAttachment extends StatelessWidget {
Navigator.push(context,
MaterialPageRoute(builder: (_) {
return FullScreenImage(
url: attachment.imageUrl ??
urls: [attachment.imageUrl ??
attachment.assetUrl ??
attachment.thumbUrl,
attachment.thumbUrl],
userName: message.user.name,
sentAt: message.createdAt,
);
}));
},
@@ -299,9 +301,11 @@ class GiphyAttachment extends StatelessWidget {
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return FullScreenImage(
url: attachment.imageUrl ??
urls: [attachment.imageUrl ??
attachment.assetUrl ??
attachment.thumbUrl,
attachment.thumbUrl],
userName: message.user.name,
sentAt: message.createdAt,
);
}));
},
+4 -2
View File
@@ -44,9 +44,11 @@ class ImageAttachment extends StatelessWidget {
MaterialPageRoute(
builder: (_) {
return FullScreenImage(
url: attachment.imageUrl ??
urls: [attachment.imageUrl ??
attachment.assetUrl ??
attachment.thumbUrl,
attachment.thumbUrl],
userName: message.user.name,
sentAt: message.createdAt,
);
},
),
+133
View File
@@ -0,0 +1,133 @@
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/back_button.dart';
import 'package:stream_chat_flutter/src/channel_info.dart';
import 'package:stream_chat_flutter/src/channel_name.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/stream_icons.dart';
import './channel_name.dart';
import 'channel_image.dart';
import 'stream_channel.dart';
class ImageFooter extends StatelessWidget {
/// Callback to call when pressing the back button.
/// By default it calls [Navigator.pop]
final VoidCallback onBackPressed;
/// Callback to call when the header is tapped.
final VoidCallback onTitleTap;
/// Callback to call when the image is tapped.
final VoidCallback onImageTap;
final int currentPage;
final int totalPages;
/// Creates a channel header
ImageFooter({
Key key,
this.onBackPressed,
this.onTitleTap,
this.onImageTap,
this.currentPage = 0,
this.totalPages = 0,
}) : preferredSize = Size.fromHeight(kToolbarHeight),
super(key: key);
@override
Widget build(BuildContext context) {
return SafeArea(
child: Container(
color: StreamChatTheme.of(context).channelTheme.channelHeaderTheme.color,
padding: EdgeInsets.symmetric(vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: Icon(
StreamIcons.share,
size: 24.0,
color: Colors.black,
),
onPressed: onBackPressed,
),
InkWell(
onTap: onTitleTap,
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
'${currentPage + 1} of $totalPages',
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w700,
color: Colors.black,
),
),
],
),
),
),
IconButton(
icon: Icon(
StreamIcons.grid,
color: Colors.black,
),
onPressed: () {},
),
],
),
),
);
return AppBar(
brightness: Theme.of(context).brightness,
elevation: 1,
leading: IconButton(
icon: Icon(StreamIcons.share),
onPressed: onBackPressed,
),
backgroundColor:
StreamChatTheme.of(context).channelTheme.channelHeaderTheme.color,
actions: <Widget>[
IconButton(
icon: Icon(StreamIcons.grid),
onPressed: () {},
),
],
centerTitle: true,
title: InkWell(
onTap: onTitleTap,
child: Container(
height: preferredSize.height,
width: preferredSize.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
'Demo name',
style: StreamChatTheme.of(context)
.channelTheme
.channelHeaderTheme
.title,
),
Text(
'1 of 1',
style: StreamChatTheme.of(context).channelPreviewTheme.subtitle,
),
],
),
),
),
);
}
@override
final Size preferredSize;
}
+5 -27
View File
@@ -110,34 +110,12 @@ class ImageGroup extends StatelessWidget {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
iconTheme: IconThemeData(
color: Colors.white,
),
builder: (context) =>
FullScreenImage(urls: images.map((e) => e.imageUrl).toList(),
startIndex: index,
userName: message.user.name,
sentAt: message.createdAt,
),
backgroundColor: Colors.black,
body: SizedBox.expand(
child: CarouselSlider(
items: images
.map((image) => FullScreenImage(
url: image.imageUrl ??
image.thumbUrl ??
image.assetUrl,
))
.toList(),
options: CarouselOptions(
initialPage: index ?? 0,
enableInfiniteScroll: false,
viewportFraction: 0.95,
height: MediaQuery.of(context).size.height,
),
),
),
);
},
),
);
}
+101
View File
@@ -0,0 +1,101 @@
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/back_button.dart';
import 'package:stream_chat_flutter/src/channel_info.dart';
import 'package:stream_chat_flutter/src/channel_name.dart';
import 'package:stream_chat_flutter/src/stream_chat_theme.dart';
import 'package:stream_chat_flutter/src/stream_icons.dart';
import './channel_name.dart';
import 'channel_image.dart';
import 'stream_channel.dart';
class ImageHeader extends StatelessWidget implements PreferredSizeWidget {
/// True if this header shows the leading back button
final bool showBackButton;
/// Callback to call when pressing the back button.
/// By default it calls [Navigator.pop]
final VoidCallback onBackPressed;
/// Callback to call when the header is tapped.
final VoidCallback onTitleTap;
/// Callback to call when the image is tapped.
final VoidCallback onImageTap;
final String userName;
final String sentAt;
/// Creates a channel header
ImageHeader({
Key key,
this.showBackButton = true,
this.onBackPressed,
this.onTitleTap,
this.onImageTap,
this.userName = '',
this.sentAt = '',
}) : preferredSize = Size.fromHeight(kToolbarHeight),
super(key: key);
@override
Widget build(BuildContext context) {
return AppBar(
brightness: Theme.of(context).brightness,
elevation: 1,
leading: showBackButton
? IconButton(
icon: Icon(
StreamIcons.close,
color: Colors.black,
size: 24.0,
),
onPressed: onBackPressed,
)
: SizedBox(),
backgroundColor:
StreamChatTheme.of(context).channelTheme.channelHeaderTheme.color,
actions: <Widget>[
IconButton(
icon: Icon(
StreamIcons.menu_point_v,
color: Colors.black,
size: 24.0,
),
onPressed: () {},
),
],
centerTitle: true,
title: InkWell(
onTap: onTitleTap,
child: Container(
height: preferredSize.height,
width: preferredSize.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
userName,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w700,
color: Colors.black,
),
),
Text(
sentAt,
style: StreamChatTheme.of(context).channelPreviewTheme.subtitle,
),
],
),
),
),
);
}
@override
final Size preferredSize;
}