TF-105 [BUG] Fix loading is blinking when reading email
This commit is contained in:
@@ -10,7 +10,6 @@ import 'package:flutter/cupertino.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email_body_part.dart';
|
||||
@@ -30,9 +29,7 @@ import 'package:tmail_ui_user/features/composer/domain/usecases/send_email_inter
|
||||
import 'package:tmail_ui_user/features/composer/domain/usecases/upload_mutiple_attachment_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/constants/email_constants.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/extensions/email_content_extension.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/message_content.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
|
||||
import 'package:tmail_ui_user/features/upload/domain/state/local_file_picker_state.dart';
|
||||
import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_interactor.dart';
|
||||
@@ -157,19 +154,6 @@ class ComposerController extends BaseController {
|
||||
return null;
|
||||
}
|
||||
|
||||
Tuple3<List<MessageContent>, List<Attachment>, Session>? getContentEmailQuoted() {
|
||||
if (composerArguments.value != null) {
|
||||
final emailContent = composerArguments.value!.emailContent;
|
||||
final session = composerArguments.value!.session;
|
||||
if (emailContent != null) {
|
||||
final messageContents = emailContent.getListMessageContent();
|
||||
final attachmentsInline = emailContent.getListAttachmentInline();
|
||||
return Tuple3(messageContents, attachmentsInline, session);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void expandEmailAddressAction() {
|
||||
final newExpandMode = expandMode.value == ExpandMode.COLLAPSE ? ExpandMode.EXPAND : ExpandMode.COLLAPSE;
|
||||
expandMode.value = newExpandMode;
|
||||
@@ -229,7 +213,6 @@ class ComposerController extends BaseController {
|
||||
|
||||
String _getBodyEmailQuotedAsHtml(BuildContext context, HtmlMessagePurifier htmlMessagePurifier) {
|
||||
final headerEmailQuoted = getHeaderEmailQuoted(Localizations.localeOf(context).toLanguageTag());
|
||||
final contentEmail = getContentEmailQuoted();
|
||||
|
||||
final headerEmailQuotedAsHtml = headerEmailQuoted != null
|
||||
? AppLocalizations.of(context).header_email_quoted(headerEmailQuoted.value1, headerEmailQuoted.value2)
|
||||
@@ -237,9 +220,12 @@ class ComposerController extends BaseController {
|
||||
: '';
|
||||
|
||||
var trustAsHtml = '';
|
||||
if (contentEmail != null && contentEmail.value1.isNotEmpty) {
|
||||
final messageContent = contentEmail.value1.first;
|
||||
trustAsHtml = messageContent.content;
|
||||
|
||||
if (composerArguments.value != null) {
|
||||
final emailContents = composerArguments.value!.emailContents;
|
||||
if (emailContents != null && emailContents.isNotEmpty) {
|
||||
trustAsHtml = emailContents.first.content;
|
||||
}
|
||||
}
|
||||
|
||||
final emailQuotedHtml = '</br></br></br>$headerEmailQuotedAsHtml${trustAsHtml.addBlockQuoteTag()}</br>';
|
||||
@@ -281,7 +267,7 @@ class ComposerController extends BaseController {
|
||||
|
||||
Set<EmailBodyPart> _generateAttachments() {
|
||||
return attachments.map((attachment) =>
|
||||
attachment.toEmailBodyPart(Attachment.dispositionAttachment)).toSet();
|
||||
attachment.toEmailBodyPart(ContentDisposition.attachment.value)).toSet();
|
||||
}
|
||||
|
||||
void sendEmailAction(BuildContext context) async {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import 'package:core/core.dart';
|
||||
import 'package:model/email/email_content.dart';
|
||||
import 'package:model/model.dart';
|
||||
|
||||
class GetEmailContentSuccess extends UIState {
|
||||
final EmailContent? emailContent;
|
||||
final List<EmailContent> emailContents;
|
||||
final List<Attachment> attachments;
|
||||
|
||||
GetEmailContentSuccess(this.emailContent);
|
||||
GetEmailContentSuccess(this.emailContents, this.attachments);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [emailContent];
|
||||
List<Object?> get props => [emailContents, attachments];
|
||||
}
|
||||
|
||||
class GetEmailContentFailure extends FeatureFailure {
|
||||
|
||||
@@ -15,7 +15,9 @@ class GetEmailContentInteractor {
|
||||
try {
|
||||
yield Right<Failure, Success>(LoadingState());
|
||||
final email = await emailRepository.getEmailContent(accountId, emailId);
|
||||
yield Right<Failure, Success>(GetEmailContentSuccess(email.toEmailContent()));
|
||||
yield Right<Failure, Success>(GetEmailContentSuccess(
|
||||
email.emailContentList,
|
||||
email.allAttachments));
|
||||
} catch (e) {
|
||||
yield Left(GetEmailContentFailure(e));
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_interactor.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/web_view_state.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:tmail_ui_user/main/routes/app_routes.dart';
|
||||
@@ -48,7 +49,8 @@ class EmailController extends BaseController {
|
||||
|
||||
final emailAddressExpandMode = ExpandMode.COLLAPSE.obs;
|
||||
final attachmentsExpandMode = ExpandMode.COLLAPSE.obs;
|
||||
final emailContent = Rxn<EmailContent>();
|
||||
final emailContents = <EmailContent>[].obs;
|
||||
final attachments = <Attachment>[].obs;
|
||||
EmailId? _currentEmailId;
|
||||
|
||||
EmailController(
|
||||
@@ -109,7 +111,7 @@ class EmailController extends BaseController {
|
||||
},
|
||||
(success) {
|
||||
if (success is GetEmailContentSuccess) {
|
||||
emailContent.value = success.emailContent;
|
||||
_getEmailContentSuccess(success);
|
||||
} else if (success is MarkAsEmailReadSuccess) {
|
||||
_markAsEmailReadSuccess(success);
|
||||
} else if (success is ExportAttachmentSuccess) {
|
||||
@@ -126,10 +128,19 @@ class EmailController extends BaseController {
|
||||
void onError(error) {
|
||||
}
|
||||
|
||||
void _getEmailContentSuccess(GetEmailContentSuccess success) {
|
||||
emailContents.value = success.emailContents;
|
||||
attachments.value = success.attachments;
|
||||
if (emailContents.isNotEmpty) {
|
||||
dispatchState(Right(WebViewLoadingState()));
|
||||
}
|
||||
}
|
||||
|
||||
void _clearEmailContent() {
|
||||
toggleDisplayEmailAddressAction(expandMode: ExpandMode.COLLAPSE);
|
||||
attachmentsExpandMode.value = ExpandMode.COLLAPSE;
|
||||
emailContent.value = null;
|
||||
emailContents.clear();
|
||||
attachments.clear();
|
||||
}
|
||||
|
||||
void toggleDisplayEmailAddressAction({required ExpandMode expandMode}) {
|
||||
@@ -150,13 +161,19 @@ class EmailController extends BaseController {
|
||||
}
|
||||
mailboxDashBoardController.dispatchState(Right(success));
|
||||
|
||||
if (success is MarkAsEmailReadSuccess && success.readActions == ReadActions.markAsUnread) {
|
||||
if (success is MarkAsEmailReadSuccess
|
||||
&& success.readActions == ReadActions.markAsUnread
|
||||
&& Get.context != null
|
||||
&& !responsiveUtils.isDesktop(Get.context!)) {
|
||||
backToThreadView();
|
||||
}
|
||||
}
|
||||
|
||||
void _markAsEmailReadFailure(Failure failure) {
|
||||
if (failure is MarkAsEmailReadFailure && failure.readActions == ReadActions.markAsUnread) {
|
||||
if (failure is MarkAsEmailReadFailure
|
||||
&& failure.readActions == ReadActions.markAsUnread
|
||||
&& Get.context != null
|
||||
&& !responsiveUtils.isDesktop(Get.context!)) {
|
||||
backToThreadView();
|
||||
}
|
||||
}
|
||||
@@ -330,17 +347,25 @@ class EmailController extends BaseController {
|
||||
&& mailboxDashBoardController.selectedEmail.value != null;
|
||||
|
||||
void backToThreadView() {
|
||||
if (emailContents.isNotEmpty) {
|
||||
dispatchState(Right(WebViewLoadCompletedState()));
|
||||
}
|
||||
popBack();
|
||||
}
|
||||
|
||||
void pressEmailAction(EmailActionType emailActionType) {
|
||||
if (canComposeEmail()) {
|
||||
if (emailContents.isNotEmpty) {
|
||||
clearState();
|
||||
}
|
||||
|
||||
push(
|
||||
AppRoutes.COMPOSER,
|
||||
arguments: ComposerArguments(
|
||||
emailActionType: emailActionType,
|
||||
presentationEmail: mailboxDashBoardController.selectedEmail.value!,
|
||||
emailContent: emailContent.value,
|
||||
emailContents: emailContents,
|
||||
attachments: attachments,
|
||||
mailboxRole: mailboxDashBoardController.selectedMailbox.value?.role,
|
||||
session: mailboxDashBoardController.sessionCurrent!,
|
||||
userProfile: mailboxDashBoardController.userProfile.value!,
|
||||
|
||||
@@ -8,13 +8,12 @@ import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:model/model.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/email_controller.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/text_format.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/web_view_state.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/widgets/app_bar_mail_widget_builder.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/widgets/attachment_file_tile_builder.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/widgets/bottom_bar_mail_widget_builder.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/extensions/email_content_extension.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/extensions/list_attachment_extension.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/widgets/email_content_place_holder_loading_widget.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/widgets/sender_and_receiver_information_tile_builder.dart';
|
||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||
import 'package:filesize/filesize.dart';
|
||||
@@ -28,6 +27,11 @@ class EmailView extends GetView {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
emailController.viewState.value.map((success) {
|
||||
if (success is WebViewLoadCompletedState && emailController.emailContents.isNotEmpty) {
|
||||
emailController.dispatchState(Right(WebViewLoadingState()));
|
||||
}
|
||||
});
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
backgroundColor: AppColor.primaryLightColor,
|
||||
@@ -41,7 +45,7 @@ class EmailView extends GetView {
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_buildAppBar(context),
|
||||
Expanded(child: _buildEmailContent(context)),
|
||||
Expanded(child: _buildEmailBody(context)),
|
||||
_buildBottomBar(context),
|
||||
])
|
||||
)
|
||||
@@ -74,7 +78,7 @@ class EmailView extends GetView {
|
||||
.build());
|
||||
}
|
||||
|
||||
Widget _buildEmailContent(BuildContext context) {
|
||||
Widget _buildEmailBody(BuildContext context) {
|
||||
return Container(
|
||||
color: AppColor.bgMessenger,
|
||||
margin: EdgeInsets.zero,
|
||||
@@ -122,12 +126,7 @@ class EmailView extends GetView {
|
||||
return Obx(() => emailController.viewState.value.fold(
|
||||
(failure) => SizedBox.shrink(),
|
||||
(success) => success is LoadingState || success is WebViewLoadingState
|
||||
? Center(child: Padding(
|
||||
padding: EdgeInsets.only(top: 16, bottom: 16),
|
||||
child: SizedBox(
|
||||
width: 30,
|
||||
height: 30,
|
||||
child: CircularProgressIndicator(color: AppColor.primaryColor))))
|
||||
? EmailContentPlaceHolderLoading(responsiveUtils: responsiveUtils)
|
||||
: SizedBox.shrink()));
|
||||
}
|
||||
|
||||
@@ -150,29 +149,27 @@ class EmailView extends GetView {
|
||||
emailController.emailAddressExpandMode.value)
|
||||
.onOpenExpandAddressReceiverActionClick(() => emailController.toggleDisplayEmailAddressAction(expandMode: ExpandMode.EXPAND))
|
||||
.build()),
|
||||
_buildListAttachments(context),
|
||||
_buildLoadingView(),
|
||||
_buildListMessageContent(),
|
||||
_buildAttachments(context),
|
||||
_buildEmailContent(),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildListAttachments(BuildContext context) {
|
||||
if (emailController.mailboxDashBoardController.selectedEmail.value?.hasAttachment == true) {
|
||||
return Obx(() {
|
||||
if (emailController.emailContent.value != null) {
|
||||
final attachments = emailController.emailContent.value!.getListAttachment();
|
||||
return attachments.isNotEmpty
|
||||
? _buildAttachmentsBody(context, attachments)
|
||||
: SizedBox.shrink();
|
||||
} else {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
Widget _buildAttachments(BuildContext context) {
|
||||
return Obx(() => emailController.viewState.value.fold(
|
||||
(failure) => SizedBox.shrink(),
|
||||
(success) {
|
||||
if (success is LoadingState || success is WebViewLoadingState) {
|
||||
return SizedBox.shrink();
|
||||
} else {
|
||||
final attachments = emailController.attachments.attachmentsWithDispositionAttachment;
|
||||
return attachments.isNotEmpty
|
||||
? _buildAttachmentsBody(context, attachments)
|
||||
: SizedBox.shrink();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
int _getAttachmentLimitDisplayed(BuildContext context) {
|
||||
@@ -281,31 +278,12 @@ class EmailView extends GetView {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildListMessageContent() {
|
||||
return Obx(() {
|
||||
final messageContents = emailController.emailContent.value?.getListMessageContent();
|
||||
|
||||
if (messageContents != null && messageContents.isNotEmpty) {
|
||||
return messageContents.first.textFormat == TextFormat.PLAIN
|
||||
? Padding(
|
||||
padding: EdgeInsets.only(top: 16),
|
||||
child: Text(
|
||||
'${messageContents.first.content}',
|
||||
style: TextStyle(fontSize: 12,
|
||||
color: AppColor.mailboxTextColor)))
|
||||
: HtmlContentViewer(
|
||||
message: messageContents.first.content,
|
||||
onWebViewLoadStart: (webController, uri) {
|
||||
emailController.dispatchState(Right(WebViewLoadingState()));
|
||||
},
|
||||
onWebViewLoadStop: (webController, uri) {
|
||||
emailController.clearState();
|
||||
},
|
||||
mailtoDelegate: (uri) async {},
|
||||
);
|
||||
} else {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
});
|
||||
Widget _buildEmailContent() {
|
||||
return Obx(() => emailController.emailContents.isNotEmpty
|
||||
? HtmlContentViewer(
|
||||
message: emailController.emailContents.first.content,
|
||||
onWebViewLoadStop: (webController, uri) => emailController.dispatchState(Right(WebViewLoadCompletedState())),
|
||||
mailtoDelegate: (uri) async {})
|
||||
: SizedBox.shrink());
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email_body_part.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/constants/email_constants.dart';
|
||||
import 'package:model/model.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/message_content.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/text_format.dart';
|
||||
|
||||
extension EmailContentExtension on EmailContent {
|
||||
|
||||
List<MessageContent> getListMessageContent() {
|
||||
Map<PartId, TextFormat> mapHtmlBody = Map();
|
||||
List<MessageContent> listMessageContent = [];
|
||||
|
||||
htmlBody?.forEach((element) {
|
||||
if (element.partId != null && element.type != null) {
|
||||
mapHtmlBody[element.partId!] = element.type!.mimeType == EmailConstants.HTML_TEXT
|
||||
? TextFormat.HTML
|
||||
: TextFormat.PLAIN;
|
||||
}
|
||||
});
|
||||
|
||||
bodyValues?.forEach((key, value) {
|
||||
listMessageContent.add(MessageContent(mapHtmlBody[key] ?? TextFormat.PLAIN, value.value));
|
||||
});
|
||||
|
||||
return listMessageContent;
|
||||
}
|
||||
|
||||
List<Attachment> getListAttachment() {
|
||||
if (attachments != null) {
|
||||
return attachments!
|
||||
.where((element) => (element.disposition != null && element.disposition != 'inline'))
|
||||
.map((item) => item.toAttachment())
|
||||
.toList();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
List<Attachment> getListAttachmentInline() {
|
||||
if (attachments != null) {
|
||||
return attachments!
|
||||
.where((element) => element.disposition == 'inline')
|
||||
.map((item) => item.toAttachment())
|
||||
.toList();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -11,4 +11,12 @@ extension ListAttachmentExtension on List<Attachment> {
|
||||
});
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
List<Attachment> get attachmentsWithDispositionAttachment {
|
||||
return where((element) => element.disposition == ContentDisposition.attachment).toList();
|
||||
}
|
||||
|
||||
List<Attachment> get attachmentWithDispositionInlines {
|
||||
return where((element) => element.disposition == ContentDisposition.inline).toList();
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,8 @@ import 'package:model/model.dart';
|
||||
class ComposerArguments with EquatableMixin {
|
||||
final EmailActionType emailActionType;
|
||||
final PresentationEmail? presentationEmail;
|
||||
final EmailContent? emailContent;
|
||||
final List<EmailContent>? emailContents;
|
||||
final List<Attachment>? attachments;
|
||||
final Session session;
|
||||
final UserProfile userProfile;
|
||||
final Map<Role, MailboxId> mapMailboxId;
|
||||
@@ -16,7 +17,8 @@ class ComposerArguments with EquatableMixin {
|
||||
ComposerArguments({
|
||||
this.emailActionType = EmailActionType.compose,
|
||||
this.presentationEmail,
|
||||
this.emailContent,
|
||||
this.emailContents,
|
||||
this.attachments,
|
||||
this.mailboxRole,
|
||||
required this.session,
|
||||
required this.userProfile,
|
||||
@@ -27,7 +29,8 @@ class ComposerArguments with EquatableMixin {
|
||||
List<Object?> get props => [
|
||||
emailActionType,
|
||||
presentationEmail,
|
||||
emailContent,
|
||||
emailContents,
|
||||
attachments,
|
||||
mailboxRole,
|
||||
session,
|
||||
userProfile,
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/text_format.dart';
|
||||
import 'package:model/model.dart';
|
||||
|
||||
class MessageContent with EquatableMixin {
|
||||
|
||||
final TextFormat textFormat;
|
||||
final String content;
|
||||
|
||||
MessageContent(this.textFormat, this.content);
|
||||
|
||||
bool hasImageInlineWithCid() => content.contains('cid:');
|
||||
|
||||
String getContentHasInlineAttachment(String baseDownloadUrl, AccountId accountId, List<Attachment> attachments) {
|
||||
var contentValid = content;
|
||||
attachments.forEach((attachment) {
|
||||
if(attachment.cid != null) {
|
||||
final urlDownloadImage = attachment.getDownloadUrl(baseDownloadUrl, accountId);
|
||||
contentValid = content.replaceAll('cid:${attachment.cid}', '$urlDownloadImage');
|
||||
}
|
||||
});
|
||||
return contentValid;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [textFormat, content];
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
enum TextFormat {
|
||||
HTML,
|
||||
PLAIN
|
||||
}
|
||||
@@ -4,6 +4,13 @@ import 'package:core/core.dart';
|
||||
class WebViewLoadingState extends UIState {
|
||||
WebViewLoadingState();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class WebViewLoadCompletedState extends UIState {
|
||||
WebViewLoadCompletedState();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
|
||||
import 'package:core/core.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const List<Color> _defaultColors = [Color.fromRGBO(0, 0, 0, 0.1), Color(0x44CCCCCC), Color.fromRGBO(0, 0, 0, 0.1)];
|
||||
|
||||
Decoration _radiusBoxDecoration({
|
||||
required Animation animation,
|
||||
bool hasCustomColors = false,
|
||||
AlignmentGeometry beginAlign = Alignment.topLeft,
|
||||
AlignmentGeometry endAlign = Alignment.bottomRight,
|
||||
List<Color> colors = _defaultColors,
|
||||
double radius = 10.0
|
||||
}) {
|
||||
return BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(radius),
|
||||
shape: BoxShape.rectangle,
|
||||
gradient: LinearGradient(
|
||||
begin: beginAlign,
|
||||
end: endAlign,
|
||||
colors: hasCustomColors
|
||||
? colors.map((color) {
|
||||
return color;
|
||||
}).toList()
|
||||
: [
|
||||
Color.fromRGBO(0, 0, 0, 0.1),
|
||||
Color(0x32E5E5E5),
|
||||
Color.fromRGBO(0, 0, 0, 0.1),
|
||||
],
|
||||
stops: [animation.value - 2, animation.value, animation.value + 1]));
|
||||
}
|
||||
|
||||
class EmailContentPlaceHolderLoading extends StatefulWidget {
|
||||
|
||||
final ResponsiveUtils responsiveUtils;
|
||||
|
||||
const EmailContentPlaceHolderLoading({
|
||||
Key? key,
|
||||
required this.responsiveUtils,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_EmailContentPlaceHolderLoadingState createState() => _EmailContentPlaceHolderLoadingState();
|
||||
}
|
||||
|
||||
class _EmailContentPlaceHolderLoadingState extends State<EmailContentPlaceHolderLoading> with SingleTickerProviderStateMixin {
|
||||
late AnimationController _animationController;
|
||||
late Animation<double> _animation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_animationController = AnimationController(duration: Duration(seconds: 1), vsync: this)..repeat();
|
||||
_animation = Tween<double>(begin: -2, end: 2)
|
||||
.animate(CurvedAnimation(curve: Curves.easeInOutSine, parent: _animationController));
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animationController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedBuilder(
|
||||
animation: _animation,
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
return Container(
|
||||
margin: EdgeInsets.only(top: 20),
|
||||
padding: EdgeInsets.zero,
|
||||
color: Colors.transparent,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(children: [
|
||||
_placeHolderAttachment(context),
|
||||
SizedBox(width: 16),
|
||||
_placeHolderAttachment(context),
|
||||
if (!widget.responsiveUtils.isMobile(context))
|
||||
SizedBox(width: 16),
|
||||
if (!widget.responsiveUtils.isMobile(context))
|
||||
_placeHolderAttachment(context),
|
||||
if (widget.responsiveUtils.isTablet(context))
|
||||
SizedBox(width: 16),
|
||||
if (widget.responsiveUtils.isTablet(context))
|
||||
_placeHolderAttachment(context),
|
||||
]),
|
||||
SizedBox(height: 16),
|
||||
Container(
|
||||
margin: EdgeInsets.only(right: 64),
|
||||
height: 10,
|
||||
decoration: _radiusBoxDecoration(animation: _animation, radius: 5)),
|
||||
SizedBox(height: 16),
|
||||
Container(
|
||||
height: 10,
|
||||
decoration: _radiusBoxDecoration(animation: _animation, radius: 5)),
|
||||
SizedBox(height: 16),
|
||||
Container(
|
||||
margin: EdgeInsets.only(right: 128),
|
||||
height: 10,
|
||||
decoration: _radiusBoxDecoration(animation: _animation, radius: 5)),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _placeHolderAttachment(BuildContext context) {
|
||||
double width = MediaQuery.of(context).size.width;
|
||||
final percentAttachment = widget.responsiveUtils.isMobile(context)
|
||||
? 0.4
|
||||
: widget.responsiveUtils.isTablet(context) ? 0.22 : 0.14;
|
||||
return Container(
|
||||
height: 55,
|
||||
width: width * percentAttachment,
|
||||
margin: EdgeInsets.zero,
|
||||
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 16),
|
||||
decoration: _radiusBoxDecoration(animation: _animation),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
height: 24,
|
||||
width: 24,
|
||||
decoration: _radiusBoxDecoration(animation: _animation, radius: 12)),
|
||||
SizedBox(width: 8),
|
||||
Expanded(child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
height: 5,
|
||||
decoration: _radiusBoxDecoration(animation: _animation)),
|
||||
SizedBox(height: 10),
|
||||
Container(
|
||||
height: 5,
|
||||
decoration: _radiusBoxDecoration(animation: _animation))
|
||||
],
|
||||
))
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user