pop static method
Future<void>
pop({
- required Widget child,
- Duration duration = Duration.zero,
- double popUpAnimationDuration = 0.4,
- BuildContext? context,
- Color? popBackgroundColor,
- Color? dismissBarrierColor,
- Color? backButtonBackgroundColor,
- Color? backButtonIconColor,
- Color? backgroundOverlaySplashColor,
- Color? shadowColor,
- bool showTimer = false,
- bool hasShadow = true,
- bool shouldBackgroundOverlayHaveBorderRadius = true,
- bool shouldDismissWhenTappingBackgroundOverlay = true,
- bool shouldBeMarginned = true,
- bool shouldAnimatePopup = true,
- bool shouldSaveThisPop = true,
- bool shouldBlurBackgroundOverlayLayer = true,
- bool isSecondaryTemporarySinglePop = false,
- Gradient? backgroundOverlayGradient,
- Offset? popPositionOffset,
- Offset? overlayBackgroundLayerOffset,
- BoxShadow? popShadow,
- VoidCallback? onDismiss,
- EdgeInsetsGeometry? popupBorderPadding,
- BorderRadiusGeometry? overlayBackgroundBorderRadius,
- Curve newWidgetResizeAnimationCurve = Curves.easeInOutBack,
Popup a Widget in front of your screen.
child : the Widget you want to popup - popIt will show an empty small box if no child is given
duration : the duration to show a popup,
if no duration is given, then Duration.zero is set by default, which will make the toast to not autodismiss
if so, then in order to dismiss the toast, the user will then need to tap anywhere on the background overlay
popUpAnimationDuration : the duration (in double type) for the animation popup to last --> default value is 0.5 sec
Implementation
/// [duration] : the duration to show a popup,
/// if no duration is given, then Duration.zero is set by default, which will make the toast to not autodismiss
/// if so, then in order to dismiss the toast, the user will then need to tap anywhere on the background overlay
/// [popUpAnimationDuration] : the duration (in double type) for the animation popup to last --> default value is 0.5 sec
static Future<void> pop({
required final Widget child,
final Duration duration = Duration.zero,
final double popUpAnimationDuration = 0.4,
final BuildContext? context,
final Color? popBackgroundColor,
final Color? dismissBarrierColor,
final Color? backButtonBackgroundColor,
final Color? backButtonIconColor,
final Color? backgroundOverlaySplashColor,
final Color? shadowColor,
final bool showTimer = false,
final bool hasShadow = true,
final bool shouldBackgroundOverlayHaveBorderRadius = true,
final bool shouldDismissWhenTappingBackgroundOverlay = true,
final bool shouldBeMarginned = true,
final bool shouldAnimatePopup = true,
final bool shouldSaveThisPop = true,
final bool shouldBlurBackgroundOverlayLayer = true,
final bool isSecondaryTemporarySinglePop = false,
final Gradient? backgroundOverlayGradient,
final Offset? popPositionOffset,
final Offset? overlayBackgroundLayerOffset,
final BoxShadow? popShadow,
final VoidCallback? onDismiss,
final EdgeInsetsGeometry? popupBorderPadding,
final BorderRadiusGeometry? overlayBackgroundBorderRadius,
final Curve newWidgetResizeAnimationCurve = Curves.easeInOutBack,
}) async {
// Ensure the overlay system is installed before showing the popup
_PopThisBootstrapper.ensureInstalled(context: context);
//if the widget tree is built
if (WidgetsBinding.instance.isRootWidgetAttached) {
if (shouldSaveThisPop == false && PopThis.isPopThisActive()) {
PopThis.animatedDismissPopThis();
}
if (isSecondaryTemporarySinglePop == false) {
//this Future.delayed is a safetynet to ensure the PopThis is called to be built
//after any currently running build run of any other unrelated widget
// await Future.delayed(0.sec, () {
WidgetsBinding.instance.addPostFrameCallback((_) {
_updatePopPositionOffset(popPositionOffset);
//log("_popThisController: ${_popThisController.state}");
//if an original popIt Overlay already exists
//then replace the popped widget with this new one
if (_popThisController.state != null) {
//first, save the child widget in the Widget history cache list
if (shouldSaveThisPop == true) {
_shouldSavePreviousPop.state = true;
_savePopWidget(
popToSave: child,
popPositionOffset: popPositionOffset,
// Pass all styling parameters
popBackgroundColor: popBackgroundColor,
backgroundOverlayColor: dismissBarrierColor,
backButtonBackgroundColor: backButtonBackgroundColor,
backButtonIconColor: backButtonIconColor,
backgroundOverlaySplashColor: backgroundOverlaySplashColor,
shadowColor: shadowColor,
showTimer: showTimer,
hasShadow: hasShadow,
shouldBackgroundOverlayHaveBorderRadius:
shouldBackgroundOverlayHaveBorderRadius,
shouldDismissWhenTappingBackgroundOverlay:
shouldDismissWhenTappingBackgroundOverlay,
shouldBeMarginned: shouldBeMarginned,
shouldAnimatePopup: shouldAnimatePopup,
shouldBlurBackgroundOverlayLayer:
shouldBlurBackgroundOverlayLayer,
popUpAnimationDuration: popUpAnimationDuration,
backgroundOverlayGradient: backgroundOverlayGradient,
popShadow: popShadow,
popupBorderPadding: popupBorderPadding,
overlayBackgroundBorderRadius: overlayBackgroundBorderRadius,
animationCurve: newWidgetResizeAnimationCurve,
);
// Log for debugging purposes
// log("After saving pop widget: ${_poppedWidgets.state.length} widgets in list");
} else {
_shouldSavePreviousPop.state = false;
}
//then repop with all style parameters
_rePop(
child,
onDismiss,
duration,
popBackgroundColor: popBackgroundColor,
backgroundOverlayColor: dismissBarrierColor,
backButtonBackgroundColor: backButtonBackgroundColor,
backButtonIconColor: backButtonIconColor,
backgroundOverlaySplashColor: backgroundOverlaySplashColor,
shadowColor: shadowColor,
showTimer: showTimer,
hasShadow: hasShadow,
shouldBackgroundOverlayHaveBorderRadius:
shouldBackgroundOverlayHaveBorderRadius,
shouldDismissWhenTappingBackgroundOverlay:
shouldDismissWhenTappingBackgroundOverlay,
shouldBeMarginned: shouldBeMarginned,
shouldAnimatePopup: shouldAnimatePopup,
shouldBlurBackgroundOverlayLayer:
shouldBlurBackgroundOverlayLayer,
popUpAnimationDuration: popUpAnimationDuration,
backgroundOverlayGradient: backgroundOverlayGradient,
popShadow: popShadow,
popupBorderPadding: popupBorderPadding,
overlayBackgroundBorderRadius: overlayBackgroundBorderRadius,
animationCurve: newWidgetResizeAnimationCurve,
);
return;
} else {
//set the timer (if given a duration to show the pop for)
//if (duration != Duration.zero) {
//initialise the _onDismissTimerController
_onDismissTimerController.state = _OnDismissController()
..onDismissCallbacks.add(onDismiss)
..onDismissTimer = duration != Duration.zero
? PausableTimer(
duration,
() {
duration == Duration.zero
? null
: PopThis.animatedDismissPopThis(
shouldPopBackToPreviousWidget: false);
},
)
: PausableTimer(
Duration.zero,
() {},
);
//start the TimerController
_onDismissTimerController.state!.onDismissTimer.start();
// }
//first, save the child widget in the Widget history cache list
if (shouldSaveThisPop == true) {
_savePopWidget(
popToSave: child,
popPositionOffset: popPositionOffset,
// Pass all styling parameters
popBackgroundColor: popBackgroundColor,
backgroundOverlayColor: dismissBarrierColor,
backButtonBackgroundColor: backButtonBackgroundColor,
backButtonIconColor: backButtonIconColor,
backgroundOverlaySplashColor: backgroundOverlaySplashColor,
shadowColor: shadowColor,
showTimer: showTimer,
hasShadow: hasShadow,
shouldBackgroundOverlayHaveBorderRadius:
shouldBackgroundOverlayHaveBorderRadius,
shouldDismissWhenTappingBackgroundOverlay:
shouldDismissWhenTappingBackgroundOverlay,
shouldBeMarginned: shouldBeMarginned,
shouldAnimatePopup: shouldAnimatePopup,
shouldBlurBackgroundOverlayLayer:
shouldBlurBackgroundOverlayLayer,
popUpAnimationDuration: popUpAnimationDuration,
backgroundOverlayGradient: backgroundOverlayGradient,
popShadow: popShadow,
popupBorderPadding: popupBorderPadding,
overlayBackgroundBorderRadius: overlayBackgroundBorderRadius,
animationCurve: newWidgetResizeAnimationCurve,
);
} else {
//if the user does not want to save the widget, then set the _shouldSavePreviousPop to false
_shouldSavePreviousPop.state = false;
}
_popThisController.state = _showCustomOverlay(
(context, opacity) => OnBuilder(
listenTo: _poppedWidgets,
builder: () {
// Get all parameters from current pop widget's state or fallback to original ones
var lastWidget = _poppedWidgets.state.isNotEmpty
? _poppedWidgets.state.last
: null;
// Colors
Color? currentBackgroundOverlayColor =
lastWidget?.backgroundOverlayColor ??
dismissBarrierColor;
Color? currentBackgroundOverlaySplashColor =
lastWidget?.backgroundOverlaySplashColor ??
backgroundOverlaySplashColor;
Color? currentPopBackgroundColor =
lastWidget?.popBackgroundColor ?? popBackgroundColor;
Color? currentBackButtonBackgroundColor =
lastWidget?.backButtonBackgroundColor ??
backButtonBackgroundColor;
Color? currentBackButtonIconColor =
lastWidget?.backButtonIconColor ?? backButtonIconColor;
Color? currentShadowColor =
lastWidget?.shadowColor ?? shadowColor;
// Boolean flags
bool currentShowTimer = lastWidget?.showTimer ?? showTimer;
bool currentHasShadow = lastWidget?.hasShadow ?? hasShadow;
bool currentShouldBackgroundOverlayHaveBorderRadius =
lastWidget?.shouldBackgroundOverlayHaveBorderRadius ??
shouldBackgroundOverlayHaveBorderRadius;
bool currentShouldDismissWhenTappingBackgroundOverlay =
lastWidget?.shouldDismissWhenTappingBackgroundOverlay ??
shouldDismissWhenTappingBackgroundOverlay;
bool currentShouldBeMarginned =
lastWidget?.shouldBeMarginned ?? shouldBeMarginned;
bool currentShouldAnimatePopup =
lastWidget?.shouldAnimatePopup ?? shouldAnimatePopup;
// Numeric values
double currentPopUpAnimationDuration =
lastWidget?.popUpAnimationDuration ??
popUpAnimationDuration;
// Complex objects
BoxShadow? currentPopShadow =
lastWidget?.popShadow ?? popShadow;
EdgeInsetsGeometry? currentPopupBorderPadding =
lastWidget?.popupBorderPadding ?? popupBorderPadding;
BorderRadiusGeometry? currentOverlayBackgroundBorderRadius =
lastWidget?.overlayBackgroundBorderRadius ??
overlayBackgroundBorderRadius;
Curve currentAnimationCurve = lastWidget?.animationCurve ??
newWidgetResizeAnimationCurve;
return ClipRRect(
borderRadius:
currentShouldBackgroundOverlayHaveBorderRadius
? currentOverlayBackgroundBorderRadius ??
BorderRadius.circular(0)
: BorderRadius.zero,
child: Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.transparent,
body: SizedBox(
height: 100.h,
width: 100.w,
child: Material(
type: MaterialType.transparency,
child: Stack(
clipBehavior: Clip.none,
children: [
//Background Empty space widget
Positioned(
left: overlayBackgroundLayerOffset?.dx,
top: overlayBackgroundLayerOffset?.dy,
child: //creates a underneath overlay layer
//this animatedSwitcher controls
//the background overlay fade animation at the end of the pop
AnimatedSwitcher(
// key: const ValueKey('main_switcher'),
duration: 0.3.sec,
switchInCurve: Curves.easeIn,
switchOutCurve: Curves.easeInOut,
child: PopThis.isPopThisActive() == false
? const SizedBox(
// key: ValueKey('empty_box_main'),
)
: STweenAnimationBuilder<double>(
tween: Tween<double>(
begin: (lastWidget
?.shouldBlurBackgroundOverlayLayer ??
shouldBlurBackgroundOverlayLayer)
? 0.0
: 10.0,
end: (lastWidget
?.shouldBlurBackgroundOverlayLayer ??
shouldBlurBackgroundOverlayLayer)
? 10.0
: 0.0,
),
duration: 0.3.sec,
curve: Curves.easeOut,
builder:
(context, blurValue, child) =>
ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: blurValue,
sigmaY: blurValue,
),
child: child,
),
),
child: SButton(
// key: const ValueKey('button_main'),
onTap: (position) {
// Use the temporary variable we created
bool shouldDismiss =
currentShouldDismissWhenTappingBackgroundOverlay;
shouldDismiss == false
? null
: PopThis
.animatedDismissPopThis(
shouldPopBackToPreviousWidget:
false,
);
},
splashColor:
currentBackgroundOverlaySplashColor ??
Colors.transparent,
shouldBounce: false,
child: Box(
height: 100.h,
width: 100.w,
color:
currentBackgroundOverlayColor ??
Colors.black.withValues(
alpha: 0.3),
),
),
),
),
),
// the toast content widget
_popThisController.state == null
? const SizedBox().animate(
effects: [
FadeEffect(
duration:
currentPopUpAnimationDuration
.sec,
curve: currentAnimationCurve,
),
],
)
: Positioned(
left: _popPositionOffset.state?.dx,
top: _popPositionOffset.state?.dy,
child: _AnimatedPopContent(
animationDuration:
currentPopUpAnimationDuration,
animationCurve: currentAnimationCurve,
shouldAnimatePopup:
currentShouldAnimatePopup,
child: _PopThisUp(
isSecondaryPop: false,
backgroundColor:
currentPopBackgroundColor,
duration: duration,
showTimer: currentShowTimer,
// if an offset position is not given, then remove the margin,
// so the popup can be in the center
shouldBeMarginned:
popPositionOffset == null
? false
: currentShouldBeMarginned,
isCentered:
popPositionOffset == null
? true
: false,
hasShadow: currentHasShadow,
boxShadow: currentPopShadow,
shadowColor: currentShadowColor,
backButtonBackgroundColor:
currentBackButtonBackgroundColor,
backButtonIconColor:
currentBackButtonIconColor,
popupBorderPadding:
currentPopupBorderPadding,
child:
_poppedWidgets.state.isNotEmpty
? _poppedWidgets.state.last
?.savedPop ??
child
: child,
),
),
),
],
),
),
),
),
);
}),
duration: duration,
context: context,
);
}
});
} else {
//as precaution, dismiss any previous secondary temp pop
PopThis.dismissSecondaryTempPopThis();
//then show the Secondary Temp popup
_secondaryTempPopThisController.state = _showCustomOverlay(
(context, opacity) => Material(
type: MaterialType.transparency,
child: ClipRRect(
borderRadius: shouldBackgroundOverlayHaveBorderRadius
? overlayBackgroundBorderRadius ?? BorderRadius.circular(32)
: BorderRadius.zero,
child: Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.transparent,
body: SizedBox(
height: 100.h,
width: 100.w,
child: Stack(
clipBehavior: Clip.none,
children: [
//Background Empty space widget
Positioned(
left: overlayBackgroundLayerOffset?.dx,
top: overlayBackgroundLayerOffset?.dy,
child: //creates a underneath overlay layer
//this animatedSwitcher controls
//the background overlay fade animation at the end of the pop
AnimatedSwitcher(
// key: const ValueKey('secondary_switcher'),
duration: 0.3.sec,
switchInCurve: Curves.easeIn,
switchOutCurve: Curves.easeInOut,
child: STweenAnimationBuilder<double>(
tween: Tween<double>(
begin:
shouldBlurBackgroundOverlayLayer ? 0.0 : 10.0,
end:
shouldBlurBackgroundOverlayLayer ? 10.0 : 0.0,
),
duration: 0.3.sec,
curve: Curves.easeOut,
builder: (context, blurValue, child) => ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: blurValue,
sigmaY: blurValue,
),
child: child,
),
),
child: SButton(
// key: const ValueKey('button_secondary'),
onTap: (position) =>
shouldDismissWhenTappingBackgroundOverlay ==
false
? null
: PopThis
.animatedDismissSecondaryTempPopThis(
onDismissExtraCallback:
onDismiss),
splashColor: backgroundOverlaySplashColor ??
Colors.transparent,
shouldBounce: false,
child: Box(
height: 100.h,
width: 100.w,
color: dismissBarrierColor ??
Colors.black.withValues(alpha: 0.3),
),
),
),
),
),
// the toast content widget
Positioned(
left: popPositionOffset?.dx,
top: popPositionOffset?.dy,
child: _AnimatedPopContent(
animationDuration: popUpAnimationDuration,
animationCurve: newWidgetResizeAnimationCurve,
shouldAnimatePopup: shouldAnimatePopup,
child: _PopThisUp(
isSecondaryPop: true,
backgroundColor: popBackgroundColor,
duration: duration,
showTimer: showTimer,
// if an offset position is not given, then remove the margin,
// so the popup can be in the center
shouldBeMarginned: popPositionOffset == null
? false
: shouldBeMarginned,
isCentered:
popPositionOffset == null ? true : false,
hasShadow: hasShadow,
boxShadow: popShadow,
shadowColor: shadowColor,
backButtonBackgroundColor:
backButtonBackgroundColor,
backButtonIconColor: backButtonIconColor,
popupBorderPadding: popupBorderPadding,
child: _poppedWidgets.state.isNotEmpty
? _poppedWidgets.state.last?.savedPop ?? child
: child,
),
),
),
],
),
),
),
),
),
duration: duration,
context: context,
);
}
}
}