| // 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), |
| ], |
| ); |
| } |
| } |