TF-2919 Fix memory leak when attach files in composer
This commit is contained in:
@@ -2,7 +2,6 @@ import 'package:core/presentation/resources/image_paths.dart';
|
|||||||
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||||
import 'package:core/utils/platform_info.dart';
|
import 'package:core/utils/platform_info.dart';
|
||||||
import 'package:extended_text/extended_text.dart';
|
import 'package:extended_text/extended_text.dart';
|
||||||
import 'package:filesize/filesize.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@@ -10,7 +9,7 @@ import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart';
|
|||||||
import 'package:tmail_ui_user/features/composer/presentation/styles/attachment_item_composer_widget_style.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/styles/attachment_item_composer_widget_style.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_progress_loading_composer_widget.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_progress_loading_composer_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/domain/model/upload_task_id.dart';
|
import 'package:tmail_ui_user/features/upload/domain/model/upload_task_id.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart';
|
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_status.dart';
|
||||||
|
|
||||||
typedef OnDeleteAttachmentAction = void Function(UploadTaskId uploadTaskId);
|
typedef OnDeleteAttachmentAction = void Function(UploadTaskId uploadTaskId);
|
||||||
|
|
||||||
@@ -18,7 +17,12 @@ class AttachmentItemComposerWidget extends StatelessWidget with AppLoaderMixin {
|
|||||||
|
|
||||||
final _imagePaths = Get.find<ImagePaths>();
|
final _imagePaths = Get.find<ImagePaths>();
|
||||||
|
|
||||||
final UploadFileState fileState;
|
final String fileIcon;
|
||||||
|
final String fileName;
|
||||||
|
final String fileSize;
|
||||||
|
final UploadFileStatus uploadStatus;
|
||||||
|
final double percentUploading;
|
||||||
|
final UploadTaskId uploadTaskId;
|
||||||
final double? maxWidth;
|
final double? maxWidth;
|
||||||
final EdgeInsetsGeometry? itemMargin;
|
final EdgeInsetsGeometry? itemMargin;
|
||||||
final EdgeInsetsGeometry? itemPadding;
|
final EdgeInsetsGeometry? itemPadding;
|
||||||
@@ -27,7 +31,12 @@ class AttachmentItemComposerWidget extends StatelessWidget with AppLoaderMixin {
|
|||||||
|
|
||||||
AttachmentItemComposerWidget({
|
AttachmentItemComposerWidget({
|
||||||
super.key,
|
super.key,
|
||||||
required this.fileState,
|
required this.fileIcon,
|
||||||
|
required this.fileName,
|
||||||
|
required this.fileSize,
|
||||||
|
required this.uploadStatus,
|
||||||
|
required this.percentUploading,
|
||||||
|
required this.uploadTaskId,
|
||||||
this.maxWidth,
|
this.maxWidth,
|
||||||
this.itemMargin,
|
this.itemMargin,
|
||||||
this.itemPadding,
|
this.itemPadding,
|
||||||
@@ -55,7 +64,7 @@ class AttachmentItemComposerWidget extends StatelessWidget with AppLoaderMixin {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
SvgPicture.asset(
|
SvgPicture.asset(
|
||||||
fileState.getIcon(_imagePaths),
|
fileIcon,
|
||||||
width: AttachmentItemComposerWidgetStyle.iconSize,
|
width: AttachmentItemComposerWidgetStyle.iconSize,
|
||||||
height: AttachmentItemComposerWidgetStyle.iconSize,
|
height: AttachmentItemComposerWidgetStyle.iconSize,
|
||||||
fit: BoxFit.fill
|
fit: BoxFit.fill
|
||||||
@@ -64,7 +73,7 @@ class AttachmentItemComposerWidget extends StatelessWidget with AppLoaderMixin {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: PlatformInfo.isCanvasKit
|
child: PlatformInfo.isCanvasKit
|
||||||
? ExtendedText(
|
? ExtendedText(
|
||||||
fileState.fileName,
|
fileName,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflowWidget: const TextOverflowWidget(
|
overflowWidget: const TextOverflowWidget(
|
||||||
position: TextOverflowPosition.middle,
|
position: TextOverflowPosition.middle,
|
||||||
@@ -76,7 +85,7 @@ class AttachmentItemComposerWidget extends StatelessWidget with AppLoaderMixin {
|
|||||||
style: AttachmentItemComposerWidgetStyle.labelTextStyle,
|
style: AttachmentItemComposerWidgetStyle.labelTextStyle,
|
||||||
)
|
)
|
||||||
: Text(
|
: Text(
|
||||||
fileState.fileName,
|
fileName,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: AttachmentItemComposerWidgetStyle.labelTextStyle,
|
style: AttachmentItemComposerWidgetStyle.labelTextStyle,
|
||||||
@@ -84,14 +93,14 @@ class AttachmentItemComposerWidget extends StatelessWidget with AppLoaderMixin {
|
|||||||
),
|
),
|
||||||
const SizedBox(width: AttachmentItemComposerWidgetStyle.space),
|
const SizedBox(width: AttachmentItemComposerWidgetStyle.space),
|
||||||
Text(
|
Text(
|
||||||
filesize(fileState.fileSize),
|
fileSize,
|
||||||
style: AttachmentItemComposerWidgetStyle.sizeLabelTextStyle
|
style: AttachmentItemComposerWidgetStyle.sizeLabelTextStyle
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
AttachmentProgressLoadingComposerWidget(
|
AttachmentProgressLoadingComposerWidget(
|
||||||
fileState: fileState,
|
uploadStatus: uploadStatus,
|
||||||
padding: AttachmentItemComposerWidgetStyle.progressLoadingPadding,
|
percentUploading: percentUploading,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -103,7 +112,7 @@ class AttachmentItemComposerWidget extends StatelessWidget with AppLoaderMixin {
|
|||||||
borderRadius: AttachmentItemComposerWidgetStyle.deleteIconRadius,
|
borderRadius: AttachmentItemComposerWidgetStyle.deleteIconRadius,
|
||||||
padding: AttachmentItemComposerWidgetStyle.deleteIconPadding,
|
padding: AttachmentItemComposerWidgetStyle.deleteIconPadding,
|
||||||
iconColor: AttachmentItemComposerWidgetStyle.deleteIconColor,
|
iconColor: AttachmentItemComposerWidgetStyle.deleteIconColor,
|
||||||
onTapActionCallback: () => onDeleteAttachmentAction?.call(fileState.uploadTaskId),
|
onTapActionCallback: () => onDeleteAttachmentAction?.call(uploadTaskId),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
+12
-12
@@ -1,27 +1,27 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:percent_indicator/linear_percent_indicator.dart';
|
import 'package:percent_indicator/linear_percent_indicator.dart';
|
||||||
|
import 'package:tmail_ui_user/features/composer/presentation/styles/attachment_item_composer_widget_style.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/styles/attachment_progress_loading_composer_widget_style.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/styles/attachment_progress_loading_composer_widget_style.dart';
|
||||||
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart';
|
|
||||||
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_status.dart';
|
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_status.dart';
|
||||||
|
|
||||||
class AttachmentProgressLoadingComposerWidget extends StatelessWidget {
|
class AttachmentProgressLoadingComposerWidget extends StatelessWidget {
|
||||||
|
|
||||||
final UploadFileState fileState;
|
final UploadFileStatus uploadStatus;
|
||||||
final EdgeInsetsGeometry? padding;
|
final double percentUploading;
|
||||||
|
|
||||||
const AttachmentProgressLoadingComposerWidget({
|
const AttachmentProgressLoadingComposerWidget({
|
||||||
super.key,
|
super.key,
|
||||||
required this.fileState,
|
required this.uploadStatus,
|
||||||
this.padding,
|
required this.percentUploading,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
switch (fileState.uploadStatus) {
|
switch (uploadStatus) {
|
||||||
case UploadFileStatus.waiting:
|
case UploadFileStatus.waiting:
|
||||||
return Padding(
|
return const Padding(
|
||||||
padding: padding ?? EdgeInsets.zero,
|
padding: AttachmentItemComposerWidgetStyle.progressLoadingPadding,
|
||||||
child: const LinearProgressIndicator(
|
child: LinearProgressIndicator(
|
||||||
color: AttachmentProgressLoadingComposerWidgetStyle.progressColor,
|
color: AttachmentProgressLoadingComposerWidgetStyle.progressColor,
|
||||||
minHeight: AttachmentProgressLoadingComposerWidgetStyle.height,
|
minHeight: AttachmentProgressLoadingComposerWidgetStyle.height,
|
||||||
backgroundColor: AttachmentProgressLoadingComposerWidgetStyle.backgroundColor,
|
backgroundColor: AttachmentProgressLoadingComposerWidgetStyle.backgroundColor,
|
||||||
@@ -29,13 +29,13 @@ class AttachmentProgressLoadingComposerWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
case UploadFileStatus.uploading:
|
case UploadFileStatus.uploading:
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: padding ?? EdgeInsets.zero,
|
padding: AttachmentItemComposerWidgetStyle.progressLoadingPadding,
|
||||||
child: LinearPercentIndicator(
|
child: LinearPercentIndicator(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
lineHeight:AttachmentProgressLoadingComposerWidgetStyle.height,
|
lineHeight:AttachmentProgressLoadingComposerWidgetStyle.height,
|
||||||
percent: fileState.percentUploading > 1.0
|
percent: percentUploading > 1.0
|
||||||
? 1.0
|
? 1.0
|
||||||
: fileState.percentUploading,
|
: percentUploading,
|
||||||
barRadius: const Radius.circular(AttachmentProgressLoadingComposerWidgetStyle.radius),
|
barRadius: const Radius.circular(AttachmentProgressLoadingComposerWidgetStyle.radius),
|
||||||
backgroundColor: AttachmentProgressLoadingComposerWidgetStyle.backgroundColor,
|
backgroundColor: AttachmentProgressLoadingComposerWidgetStyle.backgroundColor,
|
||||||
progressColor: AttachmentProgressLoadingComposerWidgetStyle.progressColor,
|
progressColor: AttachmentProgressLoadingComposerWidgetStyle.progressColor,
|
||||||
|
|||||||
+15
-2
@@ -3,6 +3,7 @@ import 'package:core/presentation/resources/image_paths.dart';
|
|||||||
import 'package:core/presentation/utils/responsive_utils.dart';
|
import 'package:core/presentation/utils/responsive_utils.dart';
|
||||||
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
import 'package:core/presentation/views/button/tmail_button_widget.dart';
|
||||||
import 'package:core/presentation/views/list/sliver_grid_delegate_fixed_height.dart';
|
import 'package:core/presentation/views/list/sliver_grid_delegate_fixed_height.dart';
|
||||||
|
import 'package:filesize/filesize.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/styles/attachment_item_composer_widget_style.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/styles/attachment_item_composer_widget_style.dart';
|
||||||
@@ -73,8 +74,14 @@ class _MobileAttachmentComposerWidgetState extends State<MobileAttachmentCompose
|
|||||||
primary: false,
|
primary: false,
|
||||||
itemCount: _listFileDisplayed.length,
|
itemCount: _listFileDisplayed.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
|
final file = _listFileDisplayed[index];
|
||||||
return AttachmentItemComposerWidget(
|
return AttachmentItemComposerWidget(
|
||||||
fileState: _listFileDisplayed[index],
|
fileIcon: file.getIcon(_imagePaths),
|
||||||
|
fileName: file.fileName,
|
||||||
|
fileSize: filesize(file.fileSize),
|
||||||
|
uploadStatus: file.uploadStatus,
|
||||||
|
percentUploading: file.percentUploading,
|
||||||
|
uploadTaskId: file.uploadTaskId,
|
||||||
itemMargin: MobileAttachmentComposerWidgetStyle.itemMargin,
|
itemMargin: MobileAttachmentComposerWidgetStyle.itemMargin,
|
||||||
onDeleteAttachmentAction: widget.onDeleteAttachmentAction
|
onDeleteAttachmentAction: widget.onDeleteAttachmentAction
|
||||||
);
|
);
|
||||||
@@ -130,8 +137,14 @@ class _MobileAttachmentComposerWidgetState extends State<MobileAttachmentCompose
|
|||||||
crossAxisSpacing: MobileAttachmentComposerWidgetStyle.listItemSpace,
|
crossAxisSpacing: MobileAttachmentComposerWidgetStyle.listItemSpace,
|
||||||
),
|
),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
|
final file = _listFileDisplayed[index];
|
||||||
return AttachmentItemComposerWidget(
|
return AttachmentItemComposerWidget(
|
||||||
fileState: _listFileDisplayed[index],
|
fileIcon: file.getIcon(_imagePaths),
|
||||||
|
fileName: file.fileName,
|
||||||
|
fileSize: filesize(file.fileSize),
|
||||||
|
uploadStatus: file.uploadStatus,
|
||||||
|
percentUploading: file.percentUploading,
|
||||||
|
uploadTaskId: file.uploadTaskId,
|
||||||
itemMargin: MobileAttachmentComposerWidgetStyle.itemMargin,
|
itemMargin: MobileAttachmentComposerWidgetStyle.itemMargin,
|
||||||
itemPadding: const EdgeInsets.symmetric(horizontal: 8),
|
itemPadding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
onDeleteAttachmentAction: widget.onDeleteAttachmentAction
|
onDeleteAttachmentAction: widget.onDeleteAttachmentAction
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
|
|
||||||
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:filesize/filesize.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/styles/web/attachment_composer_widget_style.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/styles/web/attachment_composer_widget_style.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_item_composer_widget.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_item_composer_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_header_composer_widget.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_header_composer_widget.dart';
|
||||||
@@ -28,6 +31,7 @@ class AttachmentComposerWidget extends StatefulWidget {
|
|||||||
|
|
||||||
class _AttachmentComposerWidgetState extends State<AttachmentComposerWidget> {
|
class _AttachmentComposerWidgetState extends State<AttachmentComposerWidget> {
|
||||||
|
|
||||||
|
final ImagePaths _imagePaths = Get.find<ImagePaths>();
|
||||||
bool _isCollapsed = false;
|
bool _isCollapsed = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -36,6 +40,12 @@ class _AttachmentComposerWidgetState extends State<AttachmentComposerWidget> {
|
|||||||
_isCollapsed = widget.isCollapsed;
|
_isCollapsed = widget.isCollapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_isCollapsed = false;
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
@@ -66,8 +76,13 @@ class _AttachmentComposerWidgetState extends State<AttachmentComposerWidget> {
|
|||||||
runSpacing: AttachmentComposerWidgetStyle.listItemSpace,
|
runSpacing: AttachmentComposerWidgetStyle.listItemSpace,
|
||||||
children: widget.listFileUploaded
|
children: widget.listFileUploaded
|
||||||
.map((file) => AttachmentItemComposerWidget(
|
.map((file) => AttachmentItemComposerWidget(
|
||||||
fileState: file,
|
fileIcon: file.getIcon(_imagePaths),
|
||||||
onDeleteAttachmentAction: widget.onDeleteAttachmentAction
|
fileName: file.fileName,
|
||||||
|
fileSize: filesize(file.fileSize),
|
||||||
|
uploadStatus: file.uploadStatus,
|
||||||
|
percentUploading: file.percentUploading,
|
||||||
|
uploadTaskId: file.uploadTaskId,
|
||||||
|
onDeleteAttachmentAction: widget.onDeleteAttachmentAction,
|
||||||
))
|
))
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:async/async.dart';
|
import 'package:async/async.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
@@ -45,6 +47,9 @@ class UploadController extends BaseController {
|
|||||||
final StreamGroup<Either<Failure, Success>> _progressUploadInlineImageStateStreamGroup
|
final StreamGroup<Either<Failure, Success>> _progressUploadInlineImageStateStreamGroup
|
||||||
= StreamGroup<Either<Failure, Success>>.broadcast();
|
= StreamGroup<Either<Failure, Success>>.broadcast();
|
||||||
|
|
||||||
|
StreamSubscription? _uploadAttachmentStreamSubscription;
|
||||||
|
StreamSubscription? _uploadInlineImageStreamSubscription;
|
||||||
|
|
||||||
final UploadFileStateList _uploadingStateFiles = UploadFileStateList();
|
final UploadFileStateList _uploadingStateFiles = UploadFileStateList();
|
||||||
final UploadFileStateList _uploadingStateInlineFiles = UploadFileStateList();
|
final UploadFileStateList _uploadingStateInlineFiles = UploadFileStateList();
|
||||||
|
|
||||||
@@ -58,17 +63,21 @@ class UploadController extends BaseController {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
|
listUploadAttachments.clear();
|
||||||
_uploadingStateFiles.clear();
|
_uploadingStateFiles.clear();
|
||||||
_progressUploadStateStreamGroup.close();
|
|
||||||
_uploadingStateInlineFiles.clear();
|
_uploadingStateInlineFiles.clear();
|
||||||
_progressUploadInlineImageStateStreamGroup.close();
|
uploadInlineViewState.value = Right(UIClosedState());
|
||||||
dispatchState(Right(UIClosedState()));
|
dispatchState(Right(UIClosedState()));
|
||||||
|
_progressUploadStateStreamGroup.close();
|
||||||
|
_progressUploadInlineImageStateStreamGroup.close();
|
||||||
|
_uploadAttachmentStreamSubscription?.cancel();
|
||||||
|
_uploadInlineImageStreamSubscription?.cancel();
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _registerProgressUploadStateStream() {
|
void _registerProgressUploadStateStream() {
|
||||||
_progressUploadStateStreamGroup.stream.listen(_handleProgressUploadStateStream);
|
_uploadAttachmentStreamSubscription = _progressUploadStateStreamGroup.stream.listen(_handleProgressUploadStateStream);
|
||||||
_progressUploadInlineImageStateStreamGroup.stream.listen(_handleProgressUploadInlineImageStateStream);
|
_uploadInlineImageStreamSubscription = _progressUploadInlineImageStateStreamGroup.stream.listen(_handleProgressUploadInlineImageStateStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleProgressUploadStateStream(Either<Failure, Success> uploadState) {
|
void _handleProgressUploadStateStream(Either<Failure, Success> uploadState) {
|
||||||
|
|||||||
Reference in New Issue
Block a user