blob: 86058ca5556c8b0a0028db8c5b506e8b5a8d21cd [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:animations/animations.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../layout/adaptive.dart';
import 'colors.dart';
import 'mail_view_page.dart';
import 'model/email_model.dart';
import 'model/email_store.dart';
import 'profile_avatar.dart';
const String _assetsPackage = 'flutter_gallery_assets';
const String _iconAssetLocation = 'reply/icons';
class MailPreviewCard extends StatelessWidget {
const MailPreviewCard({
super.key,
required this.id,
required this.email,
required this.onDelete,
required this.onStar,
required this.isStarred,
required this.onStarredMailbox,
});
final int id;
final Email email;
final VoidCallback onDelete;
final VoidCallback onStar;
final bool isStarred;
final bool onStarredMailbox;
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
// TODO(x): State restoration of mail view page is blocked because OpenContainer does not support restorablePush, https://github.com/flutter/gallery/issues/570.
return OpenContainer(
openBuilder: (BuildContext context, void Function() closedContainer) {
return MailViewPage(id: id, email: email);
},
openColor: theme.cardColor,
closedShape: const RoundedRectangleBorder(
),
closedElevation: 0,
closedColor: theme.cardColor,
closedBuilder: (BuildContext context, void Function() openContainer) {
final bool isDesktop = isDisplayDesktop(context);
final ColorScheme colorScheme = theme.colorScheme;
final _MailPreview mailPreview = _MailPreview(
id: id,
email: email,
onTap: openContainer,
onStar: onStar,
onDelete: onDelete,
);
if (isDesktop) {
return mailPreview;
} else {
return Dismissible(
key: ObjectKey(email),
dismissThresholds: const <DismissDirection, double>{
DismissDirection.startToEnd: 0.8,
DismissDirection.endToStart: 0.4,
},
onDismissed: (DismissDirection direction) {
switch (direction) {
case DismissDirection.endToStart:
if (onStarredMailbox) {
onStar();
}
case DismissDirection.startToEnd:
onDelete();
case DismissDirection.vertical:
case DismissDirection.horizontal:
case DismissDirection.up:
case DismissDirection.down:
case DismissDirection.none:
break;
}
},
background: _DismissibleContainer(
icon: 'twotone_delete',
backgroundColor: colorScheme.primary,
iconColor: ReplyColors.blue50,
alignment: Alignment.centerLeft,
padding: const EdgeInsetsDirectional.only(start: 20),
),
confirmDismiss: (DismissDirection direction) async {
if (direction == DismissDirection.endToStart) {
if (onStarredMailbox) {
return true;
}
onStar();
return false;
} else {
return true;
}
},
secondaryBackground: _DismissibleContainer(
icon: 'twotone_star',
backgroundColor: isStarred
? colorScheme.secondary
: theme.scaffoldBackgroundColor,
iconColor: isStarred
? colorScheme.onSecondary
: colorScheme.onBackground,
alignment: Alignment.centerRight,
padding: const EdgeInsetsDirectional.only(end: 20),
),
child: mailPreview,
);
}
},
);
}
}
class _DismissibleContainer extends StatelessWidget {
const _DismissibleContainer({
required this.icon,
required this.backgroundColor,
required this.iconColor,
required this.alignment,
required this.padding,
});
final String icon;
final Color backgroundColor;
final Color iconColor;
final Alignment alignment;
final EdgeInsetsDirectional padding;
@override
Widget build(BuildContext context) {
return AnimatedContainer(
alignment: alignment,
curve: Easing.legacy,
color: backgroundColor,
duration: kThemeAnimationDuration,
padding: padding,
child: Material(
color: Colors.transparent,
child: ImageIcon(
AssetImage(
'reply/icons/$icon.png',
package: 'flutter_gallery_assets',
),
size: 36,
color: iconColor,
),
),
);
}
}
class _MailPreview extends StatelessWidget {
const _MailPreview({
required this.id,
required this.email,
required this.onTap,
this.onStar,
this.onDelete,
});
final int id;
final Email email;
final VoidCallback onTap;
final VoidCallback? onStar;
final VoidCallback? onDelete;
@override
Widget build(BuildContext context) {
final TextTheme textTheme = Theme.of(context).textTheme;
final EmailStore emailStore = Provider.of<EmailStore>(
context,
listen: false,
);
return InkWell(
onTap: () {
Provider.of<EmailStore>(
context,
listen: false,
).selectedEmailId = id;
onTap();
},
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return ConstrainedBox(
constraints: BoxConstraints(maxHeight: constraints.maxHeight),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(
'${email.sender} - ${email.time}',
style: textTheme.bodySmall,
),
const SizedBox(height: 4),
Text(email.subject, style: textTheme.headlineSmall),
const SizedBox(height: 16),
],
),
),
_MailPreviewActionBar(
avatar: email.avatar,
isStarred: emailStore.isEmailStarred(email.id),
onStar: onStar,
onDelete: onDelete,
),
],
),
Padding(
padding: const EdgeInsetsDirectional.only(
end: 20,
),
child: Text(
email.message,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: textTheme.bodyMedium,
),
),
if (email.containsPictures) ...<Widget>[
const Flexible(
child: Column(
children: <Widget>[
SizedBox(height: 20),
_PicturePreview(),
],
),
),
],
],
),
),
);
},
),
);
}
}
class _PicturePreview extends StatelessWidget {
const _PicturePreview();
bool _shouldShrinkImage() {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
case TargetPlatform.android:
return true;
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.macOS:
case TargetPlatform.windows:
return false;
}
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: 96,
child: ListView.builder(
itemCount: 4,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsetsDirectional.only(end: 4),
child: Image.asset(
'reply/attachments/paris_${index + 1}.jpg',
gaplessPlayback: true,
package: 'flutter_gallery_assets',
cacheWidth: _shouldShrinkImage() ? 200 : null,
),
);
},
),
);
}
}
class _MailPreviewActionBar extends StatelessWidget {
const _MailPreviewActionBar({
required this.avatar,
required this.isStarred,
this.onStar,
this.onDelete,
});
final String avatar;
final bool isStarred;
final VoidCallback? onStar;
final VoidCallback? onDelete;
@override
Widget build(BuildContext context) {
final bool isDark = Theme.of(context).brightness == Brightness.dark;
final Color color = isDark ? ReplyColors.white50 : ReplyColors.blue600;
final bool isDesktop = isDisplayDesktop(context);
final Color starredIconColor =
isStarred ? Theme.of(context).colorScheme.secondary : color;
return Row(
children: <Widget>[
if (isDesktop) ...<Widget>[
IconButton(
icon: ImageIcon(
const AssetImage(
'$_iconAssetLocation/twotone_star.png',
package: _assetsPackage,
),
color: starredIconColor,
),
onPressed: onStar,
),
IconButton(
icon: ImageIcon(
const AssetImage(
'$_iconAssetLocation/twotone_delete.png',
package: _assetsPackage,
),
color: color,
),
onPressed: onDelete,
),
IconButton(
icon: Icon(
Icons.more_vert,
color: color,
),
onPressed: () {},
),
const SizedBox(width: 12),
],
ProfileAvatar(avatar: avatar),
],
);
}
}