TF-475 Create custom toast
This commit is contained in:
@@ -63,6 +63,8 @@ export 'presentation/views/search/search_bar_view.dart';
|
|||||||
export 'presentation/views/popup_menu/popup_menu_item_widget.dart';
|
export 'presentation/views/popup_menu/popup_menu_item_widget.dart';
|
||||||
export 'presentation/views/tab_bar/custom_tab_indicator.dart';
|
export 'presentation/views/tab_bar/custom_tab_indicator.dart';
|
||||||
export 'presentation/views/quick_search/quick_search_input_form.dart';
|
export 'presentation/views/quick_search/quick_search_input_form.dart';
|
||||||
|
export 'presentation/views/toast/toast_position.dart';
|
||||||
|
export 'presentation/views/toast/tmail_toast.dart';
|
||||||
|
|
||||||
// Resources
|
// Resources
|
||||||
export 'presentation/resources/assets_paths.dart';
|
export 'presentation/resources/assets_paths.dart';
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import 'package:core/core.dart';
|
import 'package:core/core.dart';
|
||||||
import 'package:core/presentation/extensions/color_extension.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:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:getwidget/getwidget.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
|
|
||||||
@@ -40,24 +38,49 @@ class AppToast {
|
|||||||
gravity: ToastGravity.BOTTOM);
|
gravity: ToastGravity.BOTTOM);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showToastWithAction(BuildContext context, String message, String actionName, Function onActionClick) {
|
void showToastWithAction(
|
||||||
GFToast.showToast(
|
BuildContext context,
|
||||||
message,
|
String message,
|
||||||
context,
|
String actionName,
|
||||||
toastPosition: GFToastPosition.BOTTOM,
|
Function onActionClick, {double? maxWidth}) {
|
||||||
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
showToastMessage(
|
||||||
backgroundColor: AppColor.toastWithActionBackgroundColor,
|
context,
|
||||||
trailing: PointerInterceptor(child: GFButton(
|
message,
|
||||||
onPressed: () {
|
maxWidth: maxWidth,
|
||||||
ToastView.dismiss();
|
trailing: TextButton(
|
||||||
onActionClick();
|
onPressed: () {
|
||||||
},
|
ToastView.dismiss();
|
||||||
text: actionName,
|
onActionClick.call();
|
||||||
type: GFButtonType.transparent,
|
},
|
||||||
color: AppColor.buttonActionToastWithActionColor,
|
child: Text(
|
||||||
)),
|
actionName,
|
||||||
toastBorderRadius: 5.0,
|
style: const TextStyle(fontSize: 16, color: AppColor.buttonActionToastWithActionColor),
|
||||||
toastDuration: 3
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
void showToastMessage(BuildContext context, String message, {
|
||||||
|
Widget? leading, Widget? trailing, double? maxWidth
|
||||||
|
}) {
|
||||||
|
TMailToast.showToast(
|
||||||
|
message,
|
||||||
|
context,
|
||||||
|
width: maxWidth,
|
||||||
|
toastPosition: ToastPosition.BOTTOM,
|
||||||
|
textStyle: TextStyle(fontSize: 16, color: Colors.white),
|
||||||
|
backgroundColor: AppColor.toastWithActionBackgroundColor,
|
||||||
|
trailing: trailing != null
|
||||||
|
? Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8),
|
||||||
|
child: PointerInterceptor(child: trailing))
|
||||||
|
: null,
|
||||||
|
leading: leading != null
|
||||||
|
? Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 8),
|
||||||
|
child: PointerInterceptor(child: leading))
|
||||||
|
: null,
|
||||||
|
toastBorderRadius: 5.0,
|
||||||
|
toastDuration: 3
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,4 +57,13 @@ class ResponsiveUtils {
|
|||||||
bool isHeightShortest(BuildContext context) {
|
bool isHeightShortest(BuildContext context) {
|
||||||
return MediaQuery.of(context).size.shortestSide < heightShortest;
|
return MediaQuery.of(context).size.shortestSide < heightShortest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double getMaxWidthToast(BuildContext context) {
|
||||||
|
final widthScreen = getSizeScreenWidth(context);
|
||||||
|
if (isPortraitMobile(context)) {
|
||||||
|
return widthScreen;
|
||||||
|
} else {
|
||||||
|
return widthScreen < 444 ? widthScreen : 444;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:core/presentation/views/toast/toast_position.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class TMailToast {
|
||||||
|
/// text of type [String] display on toast
|
||||||
|
String? text;
|
||||||
|
|
||||||
|
/// defines the duration of time toast display over screen
|
||||||
|
int? toastDuration;
|
||||||
|
|
||||||
|
/// defines the position of toast over the screen
|
||||||
|
ToastPosition? toastPosition;
|
||||||
|
|
||||||
|
/// defines the background color of the toast
|
||||||
|
Color? backgroundColor;
|
||||||
|
|
||||||
|
/// defines the test style of the toast text
|
||||||
|
TextStyle? textStyle;
|
||||||
|
|
||||||
|
/// defines the border radius of the toast
|
||||||
|
double? toastBorderRadius;
|
||||||
|
|
||||||
|
/// defines the border of the toast
|
||||||
|
Border? border;
|
||||||
|
|
||||||
|
/// defines the trailing widget of the toast
|
||||||
|
Widget? trailing;
|
||||||
|
|
||||||
|
/// defines the leading widget of the toast
|
||||||
|
Widget? leading;
|
||||||
|
|
||||||
|
/// defines the size width widget of the toast
|
||||||
|
double? width;
|
||||||
|
|
||||||
|
// ignore: type_annotate_public_apis, always_declare_return_types
|
||||||
|
static showToast(
|
||||||
|
text,
|
||||||
|
BuildContext context, {
|
||||||
|
toastDuration,
|
||||||
|
toastPosition,
|
||||||
|
backgroundColor = const Color(0xAA000000),
|
||||||
|
textStyle = const TextStyle(fontSize: 15, color: Colors.white),
|
||||||
|
toastBorderRadius = 20.0,
|
||||||
|
border,
|
||||||
|
trailing,
|
||||||
|
leading,
|
||||||
|
width,
|
||||||
|
}) {
|
||||||
|
assert(text != null);
|
||||||
|
ToastView.dismiss();
|
||||||
|
ToastView.createView(text, context, toastDuration, toastPosition,
|
||||||
|
backgroundColor, textStyle, toastBorderRadius, border, trailing,
|
||||||
|
leading, width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ToastView {
|
||||||
|
static final ToastView _instance = ToastView._internal();
|
||||||
|
// ignore: sort_constructors_first
|
||||||
|
factory ToastView() => _instance;
|
||||||
|
// ignore: sort_constructors_first
|
||||||
|
ToastView._internal();
|
||||||
|
|
||||||
|
static OverlayState? overlayState;
|
||||||
|
static OverlayEntry? _overlayEntry;
|
||||||
|
static bool _isVisible = false;
|
||||||
|
|
||||||
|
// ignore: avoid_void_async
|
||||||
|
static void createView(
|
||||||
|
String text,
|
||||||
|
BuildContext context,
|
||||||
|
int? toastDuration,
|
||||||
|
ToastPosition? toastPosition,
|
||||||
|
Color backgroundColor,
|
||||||
|
TextStyle textStyle,
|
||||||
|
double toastBorderRadius,
|
||||||
|
Border? border,
|
||||||
|
Widget? trailing,
|
||||||
|
Widget? leading,
|
||||||
|
double? width) async {
|
||||||
|
overlayState = Overlay.of(context, rootOverlay: false);
|
||||||
|
|
||||||
|
final Widget toastChild = ToastCard(
|
||||||
|
Container(
|
||||||
|
width: width,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: backgroundColor,
|
||||||
|
borderRadius: BorderRadius.circular(toastBorderRadius),
|
||||||
|
border: border,
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
|
||||||
|
child: trailing == null && leading == null
|
||||||
|
? Text(text, softWrap: true, style: textStyle)
|
||||||
|
: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
if (leading != null) leading,
|
||||||
|
Expanded(child: Text(text, style: textStyle)),
|
||||||
|
if (trailing != null) trailing
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Duration(seconds: toastDuration ?? 2), fadeDuration: 500);
|
||||||
|
|
||||||
|
_overlayEntry = OverlayEntry(
|
||||||
|
builder: (BuildContext context) =>
|
||||||
|
_showWidgetBasedOnPosition(toastChild, toastPosition));
|
||||||
|
|
||||||
|
_isVisible = true;
|
||||||
|
overlayState!.insert(_overlayEntry!);
|
||||||
|
await Future.delayed(Duration(seconds: toastDuration ?? 2));
|
||||||
|
await dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Positioned _showWidgetBasedOnPosition(
|
||||||
|
Widget child, ToastPosition? toastPosition) {
|
||||||
|
switch (toastPosition) {
|
||||||
|
case ToastPosition.BOTTOM:
|
||||||
|
return Positioned(bottom: 60, left: 18, right: 18, child: child);
|
||||||
|
case ToastPosition.BOTTOM_LEFT:
|
||||||
|
return Positioned(bottom: 60, left: 18, child: child);
|
||||||
|
case ToastPosition.BOTTOM_RIGHT:
|
||||||
|
return Positioned(bottom: 60, right: 18, child: child);
|
||||||
|
case ToastPosition.CENTER:
|
||||||
|
return Positioned(
|
||||||
|
top: 60, bottom: 60, left: 18, right: 18, child: child);
|
||||||
|
case ToastPosition.CENTER_LEFT:
|
||||||
|
return Positioned(top: 60, bottom: 60, left: 18, child: child);
|
||||||
|
case ToastPosition.CENTER_RIGHT:
|
||||||
|
return Positioned(top: 60, bottom: 60, right: 18, child: child);
|
||||||
|
case ToastPosition.TOP_LEFT:
|
||||||
|
return Positioned(top: 110, left: 18, child: child);
|
||||||
|
case ToastPosition.TOP_RIGHT:
|
||||||
|
return Positioned(top: 110, right: 18, child: child);
|
||||||
|
default:
|
||||||
|
return Positioned(top: 110, left: 18, right: 18, child: child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> dismiss() async {
|
||||||
|
if (!_isVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_isVisible = false;
|
||||||
|
_overlayEntry?.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ToastCard extends StatefulWidget {
|
||||||
|
const ToastCard(this.child, this.duration,
|
||||||
|
{Key? key, this.fadeDuration = 500})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final Widget child;
|
||||||
|
final Duration duration;
|
||||||
|
final int fadeDuration;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ToastStateFulState createState() => ToastStateFulState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ToastStateFulState extends State<ToastCard>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
void showAnimation() {
|
||||||
|
_animationController!.forward();
|
||||||
|
}
|
||||||
|
|
||||||
|
void hideAnimation() {
|
||||||
|
_animationController!.reverse();
|
||||||
|
_timer?.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimationController? _animationController;
|
||||||
|
late Animation _fadeAnimation;
|
||||||
|
|
||||||
|
Timer? _timer;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_animationController = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: Duration(milliseconds: widget.fadeDuration),
|
||||||
|
);
|
||||||
|
_fadeAnimation =
|
||||||
|
CurvedAnimation(parent: _animationController!, curve: Curves.easeIn);
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
showAnimation();
|
||||||
|
_timer = Timer(widget.duration, hideAnimation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void deactivate() {
|
||||||
|
_timer?.cancel();
|
||||||
|
_animationController!.stop();
|
||||||
|
super.deactivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_timer?.cancel();
|
||||||
|
_animationController?.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => FadeTransition(
|
||||||
|
opacity: _fadeAnimation as Animation<double>,
|
||||||
|
child: Center(
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: widget.child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
enum ToastPosition {
|
||||||
|
/// [ToastPosition.TOP] is used to show toast top of the screen
|
||||||
|
TOP,
|
||||||
|
|
||||||
|
/// [ToastPosition.BOTTOM] is used to show toast bottom of the screen
|
||||||
|
BOTTOM,
|
||||||
|
|
||||||
|
/// [ToastPosition.CENTER] is used to show toast center of the screen
|
||||||
|
CENTER,
|
||||||
|
|
||||||
|
/// [ToastPosition.TOP_LEFT] is used to show toast top left of the screen
|
||||||
|
TOP_LEFT,
|
||||||
|
|
||||||
|
/// [ToastPosition.TOP_RIGHT] is used to show toast top right of the screen
|
||||||
|
TOP_RIGHT,
|
||||||
|
|
||||||
|
/// [ToastPosition.BOTTOM_LEFT] is used to show toast bottom left of the screen
|
||||||
|
BOTTOM_LEFT,
|
||||||
|
|
||||||
|
/// [ToastPosition.BOTTOM_RIGHT] is used to show toast bottom right of the screen
|
||||||
|
BOTTOM_RIGHT,
|
||||||
|
|
||||||
|
/// [ToastPosition.CENTER_LEFT] is used to show toast center left of the screen
|
||||||
|
CENTER_LEFT,
|
||||||
|
|
||||||
|
/// [ToastPosition.CENTER_RIGHT] is used to show toast center right of the screen
|
||||||
|
CENTER_RIGHT,
|
||||||
|
}
|
||||||
@@ -58,9 +58,6 @@ dependencies:
|
|||||||
# device_info_plus
|
# device_info_plus
|
||||||
device_info_plus: 3.2.2
|
device_info_plus: 3.2.2
|
||||||
|
|
||||||
# getwidget
|
|
||||||
getwidget: 2.0.4
|
|
||||||
|
|
||||||
# webview_flutter
|
# webview_flutter
|
||||||
webview_flutter: 3.0.0
|
webview_flutter: 3.0.0
|
||||||
|
|
||||||
|
|||||||
@@ -392,7 +392,8 @@ class EmailController extends BaseController {
|
|||||||
success.currentMailboxId,
|
success.currentMailboxId,
|
||||||
MoveAction.undo,
|
MoveAction.undo,
|
||||||
success.emailActionType));
|
success.emailActionType));
|
||||||
}
|
},
|
||||||
|
maxWidth: responsiveUtils.getMaxWidthToast(currentContext!)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -328,7 +328,8 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
currentOverlayContext!,
|
currentOverlayContext!,
|
||||||
AppLocalizations.of(currentContext!).drafts_saved,
|
AppLocalizations.of(currentContext!).drafts_saved,
|
||||||
AppLocalizations.of(currentContext!).discard,
|
AppLocalizations.of(currentContext!).discard,
|
||||||
() => _discardEmail(success.emailAsDrafts)
|
() => _discardEmail(success.emailAsDrafts),
|
||||||
|
maxWidth: _responsiveUtils.getMaxWidthToast(currentContext!)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -349,7 +350,8 @@ class MailboxDashBoardController extends ReloadableController {
|
|||||||
success.currentMailboxId,
|
success.currentMailboxId,
|
||||||
MoveAction.undo,
|
MoveAction.undo,
|
||||||
success.emailActionType));
|
success.emailActionType));
|
||||||
}
|
},
|
||||||
|
maxWidth: _responsiveUtils.getMaxWidthToast(currentContext!)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -669,7 +669,8 @@ class ThreadController extends BaseController {
|
|||||||
emailActionType!,
|
emailActionType!,
|
||||||
destinationPath: destinationPath));
|
destinationPath: destinationPath));
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
maxWidth: _responsiveUtils.getMaxWidthToast(currentContext!)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1043,7 +1044,8 @@ class ThreadController extends BaseController {
|
|||||||
success.currentMailboxId,
|
success.currentMailboxId,
|
||||||
MoveAction.undo,
|
MoveAction.undo,
|
||||||
success.emailActionType));
|
success.emailActionType));
|
||||||
}
|
},
|
||||||
|
maxWidth: _responsiveUtils.getMaxWidthToast(currentContext!)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user