From 1f04061eb9e4b2b8ce9e0ff36655ff7250cd46fe Mon Sep 17 00:00:00 2001 From: Yuriy Trofimov Date: Mon, 14 Nov 2022 18:12:12 +0200 Subject: [PATCH 01/10] Fix snackbar stucking --- lib/top_snack_bar.dart | 151 ++++++++++++++++++++--------------------- 1 file changed, 74 insertions(+), 77 deletions(-) diff --git a/lib/top_snack_bar.dart b/lib/top_snack_bar.dart index d5d79dd..35ede67 100644 --- a/lib/top_snack_bar.dart +++ b/lib/top_snack_bar.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:top_snackbar_flutter/safe_area_values.dart'; import 'package:top_snackbar_flutter/tap_bounce_container.dart'; @@ -21,7 +23,7 @@ OverlayEntry? _previousEntry; /// /// The [displayDuration] argument is used to specify duration displaying /// -/// The [onTap] callback of [TopSnackBar] +/// The [onTap] callback of [_TopSnackBar] /// /// The [overlayState] argument is used to add specific overlay state. /// If you will not pass it, it will try to get the current overlay state from @@ -63,18 +65,16 @@ void showTopSnackBar( SafeAreaValues safeAreaValues = const SafeAreaValues(), DismissType dismissType = DismissType.onTap, DismissDirection dismissDirection = DismissDirection.up, -}) async { - overlayState ??= Overlay.of(context); +}) { + final overlay = overlayState ?? Overlay.of(context); + late OverlayEntry overlayEntry; + overlayEntry = OverlayEntry( - builder: (context) { - return TopSnackBar( - child: child, + builder: (_) { + return _TopSnackBar( onDismissed: () { - if (overlayEntry.mounted && _previousEntry == overlayEntry) { - overlayEntry.remove(); - _previousEntry = null; - } + overlayEntry.remove(); }, animationDuration: animationDuration, reverseAnimationDuration: reverseAnimationDuration, @@ -88,6 +88,7 @@ void showTopSnackBar( safeAreaValues: safeAreaValues, dismissType: dismissType, dismissDirection: dismissDirection, + child: child, ); }, ); @@ -95,12 +96,30 @@ void showTopSnackBar( if (_previousEntry != null && _previousEntry!.mounted) { _previousEntry?.remove(); } - overlayState?.insert(overlayEntry); + + overlay?.insert(overlayEntry); _previousEntry = overlayEntry; } /// Widget that controls all animations -class TopSnackBar extends StatefulWidget { +class _TopSnackBar extends StatefulWidget { + const _TopSnackBar({ + Key? key, + required this.child, + required this.onDismissed, + required this.animationDuration, + required this.reverseAnimationDuration, + required this.displayDuration, + required this.padding, + required this.curve, + required this.reverseCurve, + required this.safeAreaValues, + this.onTap, + this.persistent = false, + this.onAnimationControllerInit, + this.dismissType = DismissType.onTap, + this.dismissDirection = DismissDirection.up, + }) : super(key: key); final Widget child; final VoidCallback onDismissed; final Duration animationDuration; @@ -116,87 +135,62 @@ class TopSnackBar extends StatefulWidget { final DismissType dismissType; final DismissDirection dismissDirection; - TopSnackBar({ - Key? key, - required this.child, - required this.onDismissed, - required this.animationDuration, - required this.reverseAnimationDuration, - required this.displayDuration, - this.onTap, - this.persistent = false, - this.onAnimationControllerInit, - required this.padding, - required this.curve, - required this.reverseCurve, - required this.safeAreaValues, - this.dismissType = DismissType.onTap, - this.dismissDirection = DismissDirection.up, - }) : super(key: key); - @override _TopSnackBarState createState() => _TopSnackBarState(); } -class _TopSnackBarState extends State +class _TopSnackBarState extends State<_TopSnackBar> with SingleTickerProviderStateMixin { - late Animation offsetAnimation; - late AnimationController animationController; + late final Animation _offsetAnimation; + late final AnimationController _animationController; - @override - void initState() { - _setupAndStartAnimation(); - super.initState(); - } + Timer? _timer; - @override - void dispose() { - animationController.dispose(); - super.dispose(); - } + final offsetTween = Tween(begin: const Offset(0, -1), end: Offset.zero); - void _setupAndStartAnimation() async { - animationController = AnimationController( + @override + void initState() { + _animationController = AnimationController( vsync: this, duration: widget.animationDuration, reverseDuration: widget.reverseAnimationDuration, ); - - widget.onAnimationControllerInit?.call(animationController); - - Tween offsetTween = Tween( - begin: Offset(0.0, -1.0), - end: Offset(0.0, 0.0), + _animationController.addStatusListener( + (status) { + if (status == AnimationStatus.completed && !widget.persistent) { + _timer = Timer(widget.displayDuration, () { + if (mounted) { + _animationController.reverse(); + } + }); + } + if (status == AnimationStatus.dismissed) { + _timer?.cancel(); + widget.onDismissed.call(); + } + }, ); - offsetAnimation = offsetTween.animate( + widget.onAnimationControllerInit?.call(_animationController); + + _offsetAnimation = offsetTween.animate( CurvedAnimation( - parent: animationController, + parent: _animationController, curve: widget.curve, reverseCurve: widget.reverseCurve, ), - )..addStatusListener((status) async { - if (status == AnimationStatus.completed) { - await Future.delayed(widget.displayDuration); - _dismiss(); - } - - if (status == AnimationStatus.dismissed) { - if (mounted) { - widget.onDismissed.call(); - } - } - }); - + ); if (mounted) { - animationController.forward(); + _animationController.forward(); } + super.initState(); } - void _dismiss() { - if (!widget.persistent && mounted) { - animationController.reverse(); - } + @override + void dispose() { + _animationController.dispose(); + _timer?.cancel(); + super.dispose(); } @override @@ -206,7 +200,7 @@ class _TopSnackBarState extends State left: widget.padding.left, right: widget.padding.right, child: SlideTransition( - position: offsetAnimation as Animation, + position: _offsetAnimation, child: SafeArea( top: widget.safeAreaValues.top, bottom: widget.safeAreaValues.bottom, @@ -227,9 +221,9 @@ class _TopSnackBarState extends State case DismissType.onTap: return TapBounceContainer( onTap: () { - if (mounted) { - widget.onTap?.call(); - _dismiss(); + widget.onTap?.call(); + if (!widget.persistent && mounted) { + _animationController.reverse(); } }, child: widget.child, @@ -237,8 +231,11 @@ class _TopSnackBarState extends State case DismissType.onSwipe: return Dismissible( direction: widget.dismissDirection, - key: UniqueKey(), - onDismissed: (direction) => _dismiss(), + key: const ValueKey(_TopSnackBar), + onDismissed: (direction) { + _timer?.cancel(); + widget.onDismissed.call(); + }, child: widget.child, ); case DismissType.none: From dfc67807fbfed278e2ec505f78263efc714f5431 Mon Sep 17 00:00:00 2001 From: Yuriy Trofimov Date: Mon, 14 Nov 2022 18:13:06 +0200 Subject: [PATCH 02/10] Code reformatting --- lib/custom_snack_bar.dart | 48 +++++++++++++++++------------------ lib/tap_bounce_container.dart | 28 ++++++++++---------- 2 files changed, 39 insertions(+), 37 deletions(-) diff --git a/lib/custom_snack_bar.dart b/lib/custom_snack_bar.dart index 09c0a08..d745f48 100644 --- a/lib/custom_snack_bar.dart +++ b/lib/custom_snack_bar.dart @@ -4,26 +4,13 @@ import 'package:flutter/material.dart'; /// Popup widget that you can use by default to show some information class CustomSnackBar extends StatefulWidget { - final String message; - final Widget icon; - final Color backgroundColor; - final TextStyle textStyle; - final int maxLines; - final int iconRotationAngle; - final List boxShadow; - final BorderRadius borderRadius; - final double iconPositionTop; - final double iconPositionLeft; - final EdgeInsetsGeometry messagePadding; - final double textScaleFactor; - const CustomSnackBar.success({ Key? key, required this.message, this.messagePadding = const EdgeInsets.symmetric(horizontal: 24), this.icon = const Icon( Icons.sentiment_very_satisfied, - color: const Color(0x15000000), + color: Color(0x15000000), size: 120, ), this.textStyle = const TextStyle( @@ -39,7 +26,7 @@ class CustomSnackBar extends StatefulWidget { this.boxShadow = kDefaultBoxShadow, this.borderRadius = kDefaultBorderRadius, this.textScaleFactor = 1.0, - }); + }) : super(key: key); const CustomSnackBar.info({ Key? key, @@ -47,7 +34,7 @@ class CustomSnackBar extends StatefulWidget { this.messagePadding = const EdgeInsets.symmetric(horizontal: 24), this.icon = const Icon( Icons.sentiment_neutral, - color: const Color(0x15000000), + color: Color(0x15000000), size: 120, ), this.textStyle = const TextStyle( @@ -63,7 +50,7 @@ class CustomSnackBar extends StatefulWidget { this.boxShadow = kDefaultBoxShadow, this.borderRadius = kDefaultBorderRadius, this.textScaleFactor = 1.0, - }); + }) : super(key: key); const CustomSnackBar.error({ Key? key, @@ -71,7 +58,7 @@ class CustomSnackBar extends StatefulWidget { this.messagePadding = const EdgeInsets.symmetric(horizontal: 24), this.icon = const Icon( Icons.error_outline, - color: const Color(0x15000000), + color: Color(0x15000000), size: 120, ), this.textStyle = const TextStyle( @@ -89,11 +76,24 @@ class CustomSnackBar extends StatefulWidget { this.textScaleFactor = 1.0, }); + final String message; + final Widget icon; + final Color backgroundColor; + final TextStyle textStyle; + final int maxLines; + final int iconRotationAngle; + final List boxShadow; + final BorderRadius borderRadius; + final double iconPositionTop; + final double iconPositionLeft; + final EdgeInsetsGeometry messagePadding; + final double textScaleFactor; + @override - _CustomSnackBarState createState() => _CustomSnackBarState(); + CustomSnackBarState createState() => CustomSnackBarState(); } -class _CustomSnackBarState extends State { +class CustomSnackBarState extends State { @override Widget build(BuildContext context) { final theme = Theme.of(context); @@ -111,7 +111,7 @@ class _CustomSnackBarState extends State { Positioned( top: widget.iconPositionTop, left: widget.iconPositionLeft, - child: Container( + child: SizedBox( height: 95, child: Transform.rotate( angle: widget.iconRotationAngle * pi / 180, @@ -140,13 +140,13 @@ class _CustomSnackBarState extends State { } } -const kDefaultBoxShadow = const [ +const kDefaultBoxShadow = [ BoxShadow( color: Colors.black26, - offset: Offset(0.0, 8.0), + offset: Offset(0, 8), spreadRadius: 1, blurRadius: 30, ), ]; -const kDefaultBorderRadius = const BorderRadius.all(Radius.circular(12)); +const kDefaultBorderRadius = BorderRadius.all(Radius.circular(12)); diff --git a/lib/tap_bounce_container.dart b/lib/tap_bounce_container.dart index 3005b0b..2eed271 100644 --- a/lib/tap_bounce_container.dart +++ b/lib/tap_bounce_container.dart @@ -1,32 +1,34 @@ +import 'dart:async'; + import 'package:flutter/cupertino.dart'; /// Widget for nice tap effect. It decrease widget scale while tapping class TapBounceContainer extends StatefulWidget { - final Widget child; - final VoidCallback? onTap; - - TapBounceContainer({ + const TapBounceContainer({ + Key? key, required this.child, this.onTap, - }); + }) : super(key: key); + + final Widget child; + final VoidCallback? onTap; @override - _TapBounceContainerState createState() => _TapBounceContainerState(); + TapBounceContainerState createState() => TapBounceContainerState(); } -class _TapBounceContainerState extends State +class TapBounceContainerState extends State with SingleTickerProviderStateMixin { late double _scale; late AnimationController _controller; - final animationDuration = Duration(milliseconds: 200); + final animationDuration = const Duration(milliseconds: 200); @override void initState() { _controller = AnimationController( vsync: this, duration: animationDuration, - lowerBound: 0.0, upperBound: 0.04, )..addListener(() { if (mounted) { @@ -63,17 +65,17 @@ class _TapBounceContainerState extends State } } - void _onTapUp(TapUpDetails details) async { + Future _onTapUp(TapUpDetails details) async { await _closeSnackBar(); } - void _onPanEnd(DragEndDetails details) async { + Future _onPanEnd(DragEndDetails details) async { await _closeSnackBar(); } - Future _closeSnackBar() async { + Future _closeSnackBar() async { if (mounted) { - _controller.reverse(); + unawaited(_controller.reverse()); await Future.delayed(animationDuration); widget.onTap?.call(); } From ac66f87c609961a66ad395f0b82850247c9bc94b Mon Sep 17 00:00:00 2001 From: Yuriy Trofimov Date: Tue, 15 Nov 2022 12:15:37 +0200 Subject: [PATCH 03/10] Implement multi direction swipe for dismissible --- lib/top_snack_bar.dart | 61 +++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/lib/top_snack_bar.dart b/lib/top_snack_bar.dart index 35ede67..ff1637c 100644 --- a/lib/top_snack_bar.dart +++ b/lib/top_snack_bar.dart @@ -46,9 +46,9 @@ OverlayEntry? _previousEntry; /// The [dismissType] argument specify which action to trigger to /// dismiss the snackbar. Defaults to `TopSnackBarDismissType.onTap` /// -/// The [dismissDirection] argument specify in which direction the snackbar +/// The [dismissDirections] argument specify in which direction the snackbar /// can be dismissed. This argument is only used when [dismissType] is equal -/// to `DismissType.onSwipe`. Defaults to `DismissDirection.up` +/// to `DismissType.onSwipe`. Defaults to `[DismissDirection.up]` void showTopSnackBar( BuildContext context, Widget child, { @@ -64,17 +64,18 @@ void showTopSnackBar( Curve reverseCurve = Curves.linearToEaseOut, SafeAreaValues safeAreaValues = const SafeAreaValues(), DismissType dismissType = DismissType.onTap, - DismissDirection dismissDirection = DismissDirection.up, + List dismissDirections = const [DismissDirection.up], }) { - final overlay = overlayState ?? Overlay.of(context); + final _overlay = overlayState ?? Overlay.of(context); - late OverlayEntry overlayEntry; + late OverlayEntry _overlayEntry; - overlayEntry = OverlayEntry( + _overlayEntry = OverlayEntry( builder: (_) { return _TopSnackBar( onDismissed: () { - overlayEntry.remove(); + _overlayEntry.remove(); + _previousEntry = null; }, animationDuration: animationDuration, reverseAnimationDuration: reverseAnimationDuration, @@ -87,7 +88,7 @@ void showTopSnackBar( reverseCurve: reverseCurve, safeAreaValues: safeAreaValues, dismissType: dismissType, - dismissDirection: dismissDirection, + dismissDirections: dismissDirections, child: child, ); }, @@ -97,8 +98,8 @@ void showTopSnackBar( _previousEntry?.remove(); } - overlay?.insert(overlayEntry); - _previousEntry = overlayEntry; + _overlay?.insert(_overlayEntry); + _previousEntry = _overlayEntry; } /// Widget that controls all animations @@ -114,12 +115,13 @@ class _TopSnackBar extends StatefulWidget { required this.curve, required this.reverseCurve, required this.safeAreaValues, + required this.dismissDirections, this.onTap, this.persistent = false, this.onAnimationControllerInit, this.dismissType = DismissType.onTap, - this.dismissDirection = DismissDirection.up, }) : super(key: key); + final Widget child; final VoidCallback onDismissed; final Duration animationDuration; @@ -133,7 +135,7 @@ class _TopSnackBar extends StatefulWidget { final Curve reverseCurve; final SafeAreaValues safeAreaValues; final DismissType dismissType; - final DismissDirection dismissDirection; + final List dismissDirections; @override _TopSnackBarState createState() => _TopSnackBarState(); @@ -141,12 +143,12 @@ class _TopSnackBar extends StatefulWidget { class _TopSnackBarState extends State<_TopSnackBar> with SingleTickerProviderStateMixin { - late final Animation _offsetAnimation; + late Animation _offsetAnimation; late final AnimationController _animationController; Timer? _timer; - final offsetTween = Tween(begin: const Offset(0, -1), end: Offset.zero); + final _offsetTween = Tween(begin: const Offset(0, -1), end: Offset.zero); @override void initState() { @@ -173,7 +175,7 @@ class _TopSnackBarState extends State<_TopSnackBar> widget.onAnimationControllerInit?.call(_animationController); - _offsetAnimation = offsetTween.animate( + _offsetAnimation = _offsetTween.animate( CurvedAnimation( parent: _animationController, curve: widget.curve, @@ -229,15 +231,26 @@ class _TopSnackBarState extends State<_TopSnackBar> child: widget.child, ); case DismissType.onSwipe: - return Dismissible( - direction: widget.dismissDirection, - key: const ValueKey(_TopSnackBar), - onDismissed: (direction) { - _timer?.cancel(); - widget.onDismissed.call(); - }, - child: widget.child, - ); + var childWidget = widget.child; + for (final direction in widget.dismissDirections) { + childWidget = Dismissible( + direction: direction, + key: UniqueKey(), + dismissThresholds: const {DismissDirection.up: 0.2}, + confirmDismiss: (direction) async { + if (!widget.persistent && mounted) { + if (direction == DismissDirection.down) { + await _animationController.reverse(); + } else { + _animationController.reset(); + } + } + return false; + }, + child: childWidget, + ); + } + return childWidget; case DismissType.none: return widget.child; } From 36be30f4c80cca5aa49d00e2895a7687af1635a4 Mon Sep 17 00:00:00 2001 From: Yuriy Trofimov Date: Tue, 15 Nov 2022 12:28:32 +0200 Subject: [PATCH 04/10] Add TextAlign parameter to CustomSnackBar widget --- lib/custom_snack_bar.dart | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/custom_snack_bar.dart b/lib/custom_snack_bar.dart index d745f48..747efe2 100644 --- a/lib/custom_snack_bar.dart +++ b/lib/custom_snack_bar.dart @@ -26,6 +26,7 @@ class CustomSnackBar extends StatefulWidget { this.boxShadow = kDefaultBoxShadow, this.borderRadius = kDefaultBorderRadius, this.textScaleFactor = 1.0, + this.textAlign = TextAlign.center, }) : super(key: key); const CustomSnackBar.info({ @@ -50,6 +51,7 @@ class CustomSnackBar extends StatefulWidget { this.boxShadow = kDefaultBoxShadow, this.borderRadius = kDefaultBorderRadius, this.textScaleFactor = 1.0, + this.textAlign = TextAlign.center, }) : super(key: key); const CustomSnackBar.error({ @@ -74,7 +76,8 @@ class CustomSnackBar extends StatefulWidget { this.boxShadow = kDefaultBoxShadow, this.borderRadius = kDefaultBorderRadius, this.textScaleFactor = 1.0, - }); + this.textAlign = TextAlign.center, + }) : super(key: key); final String message; final Widget icon; @@ -88,6 +91,7 @@ class CustomSnackBar extends StatefulWidget { final double iconPositionLeft; final EdgeInsetsGeometry messagePadding; final double textScaleFactor; + final TextAlign textAlign; @override CustomSnackBarState createState() => CustomSnackBarState(); @@ -124,10 +128,8 @@ class CustomSnackBarState extends State { padding: widget.messagePadding, child: Text( widget.message, - style: theme.textTheme.bodyText2?.merge( - widget.textStyle, - ), - textAlign: TextAlign.center, + style: theme.textTheme.bodyText2?.merge(widget.textStyle), + textAlign: widget.textAlign, overflow: TextOverflow.ellipsis, maxLines: widget.maxLines, textScaleFactor: widget.textScaleFactor, From e47c74be4dd3306022a6f4bc2692efe6b9af0d6f Mon Sep 17 00:00:00 2001 From: Yuriy Trofimov Date: Tue, 15 Nov 2022 12:46:39 +0200 Subject: [PATCH 05/10] Change required params for showTopSnackBar Remove BuildContext from params Make OverlayState required --- lib/top_snack_bar.dart | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/top_snack_bar.dart b/lib/top_snack_bar.dart index ff1637c..7252eab 100644 --- a/lib/top_snack_bar.dart +++ b/lib/top_snack_bar.dart @@ -25,9 +25,9 @@ OverlayEntry? _previousEntry; /// /// The [onTap] callback of [_TopSnackBar] /// -/// The [overlayState] argument is used to add specific overlay state. -/// If you will not pass it, it will try to get the current overlay state from -/// passed [BuildContext] +/// The [overlayState] required argument is used to add specific overlay state. +/// If you are sure that there is a overlay state in your [BuildContext], +/// You can get it [Overlay.of(BuildContext)] /// /// The [persistent] argument is used to make snack bar persistent, so /// [displayDuration] will be ignored. Default is false. @@ -50,13 +50,12 @@ OverlayEntry? _previousEntry; /// can be dismissed. This argument is only used when [dismissType] is equal /// to `DismissType.onSwipe`. Defaults to `[DismissDirection.up]` void showTopSnackBar( - BuildContext context, Widget child, { + required OverlayState overlayState, Duration animationDuration = const Duration(milliseconds: 1200), Duration reverseAnimationDuration = const Duration(milliseconds: 550), Duration displayDuration = const Duration(milliseconds: 3000), VoidCallback? onTap, - OverlayState? overlayState, bool persistent = false, ControllerCallback? onAnimationControllerInit, EdgeInsets padding = const EdgeInsets.all(16), @@ -66,10 +65,7 @@ void showTopSnackBar( DismissType dismissType = DismissType.onTap, List dismissDirections = const [DismissDirection.up], }) { - final _overlay = overlayState ?? Overlay.of(context); - late OverlayEntry _overlayEntry; - _overlayEntry = OverlayEntry( builder: (_) { return _TopSnackBar( @@ -98,7 +94,7 @@ void showTopSnackBar( _previousEntry?.remove(); } - _overlay?.insert(_overlayEntry); + overlayState.insert(_overlayEntry); _previousEntry = _overlayEntry; } From 0e9451325ad1e6632d1668d5a048aea8d604d38a Mon Sep 17 00:00:00 2001 From: Yuriy Trofimov Date: Tue, 15 Nov 2022 13:04:33 +0200 Subject: [PATCH 06/10] Rename showTopSnackBar parameter --- lib/top_snack_bar.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/top_snack_bar.dart b/lib/top_snack_bar.dart index 7252eab..bc16ea8 100644 --- a/lib/top_snack_bar.dart +++ b/lib/top_snack_bar.dart @@ -46,7 +46,7 @@ OverlayEntry? _previousEntry; /// The [dismissType] argument specify which action to trigger to /// dismiss the snackbar. Defaults to `TopSnackBarDismissType.onTap` /// -/// The [dismissDirections] argument specify in which direction the snackbar +/// The [dismissDirection] argument specify in which direction the snackbar /// can be dismissed. This argument is only used when [dismissType] is equal /// to `DismissType.onSwipe`. Defaults to `[DismissDirection.up]` void showTopSnackBar( @@ -63,7 +63,7 @@ void showTopSnackBar( Curve reverseCurve = Curves.linearToEaseOut, SafeAreaValues safeAreaValues = const SafeAreaValues(), DismissType dismissType = DismissType.onTap, - List dismissDirections = const [DismissDirection.up], + List dismissDirection = const [DismissDirection.up], }) { late OverlayEntry _overlayEntry; _overlayEntry = OverlayEntry( @@ -84,7 +84,7 @@ void showTopSnackBar( reverseCurve: reverseCurve, safeAreaValues: safeAreaValues, dismissType: dismissType, - dismissDirections: dismissDirections, + dismissDirections: dismissDirection, child: child, ); }, From 663dc3b208f9a96e2101cbcd33d1bcfd124dbcf1 Mon Sep 17 00:00:00 2001 From: Yuriy Trofimov Date: Tue, 15 Nov 2022 13:13:41 +0200 Subject: [PATCH 07/10] Update Example after package changes --- example/lib/main.dart | 75 ++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index bfc6927..cca2848 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -4,23 +4,23 @@ import 'package:top_snackbar_flutter/tap_bounce_container.dart'; import 'package:top_snackbar_flutter/top_snack_bar.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatefulWidget { + const MyApp({Key key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + MyAppState createState() => MyAppState(); } -class _MyAppState extends State { +class MyAppState extends State { AnimationController localAnimationController; @override Widget build(BuildContext context) { return MaterialApp( - theme: ThemeData( - primaryColor: Colors.orangeAccent, - ), + theme: ThemeData(primaryColor: Colors.orangeAccent), home: Builder( builder: (BuildContext context) { return Scaffold( @@ -32,74 +32,77 @@ class _MyAppState extends State { TapBounceContainer( onTap: () { showTopSnackBar( - context, - CustomSnackBar.info( + const CustomSnackBar.info( message: - "There is some information. You need to do something with that", + 'There is some information. You need to do something with that', ), + overlayState: Overlay.of(context), ); }, - child: buildButton(context, "Show info"), + child: buildButton(context, 'Show info'), ), - SizedBox(height: 24), + const SizedBox(height: 24), TapBounceContainer( onTap: () { showTopSnackBar( - context, - CustomSnackBar.success( + const CustomSnackBar.success( message: - "Good job, your release is successful. Have a nice day", + 'Good job, your release is successful. Have a nice day', ), + overlayState: Overlay.of(context), ); }, - child: buildButton(context, "Show success"), + child: buildButton(context, 'Show success'), ), - SizedBox(height: 24), + const SizedBox(height: 24), TapBounceContainer( onTap: () { showTopSnackBar( - context, - CustomSnackBar.error( + const CustomSnackBar.error( message: - "Something went wrong. Please check your credentials and try again", + 'Something went wrong. Please check your credentials and try again', ), + overlayState: Overlay.of(context), ); }, - child: buildButton(context, "Show error"), + child: buildButton(context, 'Show error'), ), - SizedBox(height: 48), + const SizedBox(height: 48), TapBounceContainer( onTap: () { showTopSnackBar( - context, - CustomSnackBar.info( - message: "Persistent SnackBar", + const CustomSnackBar.info( + message: 'Persistent SnackBar', ), persistent: true, - onAnimationControllerInit: (controller) => - localAnimationController = controller, + overlayState: Overlay.of(context), + onAnimationControllerInit: (controller) { + localAnimationController = controller; + }, ); }, - child: buildButton(context, "Show persistent SnackBar"), + child: buildButton(context, 'Show persistent SnackBar'), ), - SizedBox(height: 24), + const SizedBox(height: 24), TapBounceContainer( onTap: () => localAnimationController.reverse(), - child: buildButton(context, "Dismiss persistent SnackBar"), + child: buildButton(context, 'Dismiss persistent SnackBar'), ), - SizedBox(height: 24), + const SizedBox(height: 24), TapBounceContainer( onTap: () { showTopSnackBar( - context, - CustomSnackBar.info(message: "Try to swipe me left"), + const CustomSnackBar.info( + message: 'Try to swipe me left', + ), dismissType: DismissType.onSwipe, - dismissDirection: DismissDirection.endToStart, + dismissDirection: [DismissDirection.endToStart], + overlayState: Overlay.of(context), ); }, child: buildButton( context, - "Show swiped dismissible SnackBar", + 'Show swiped dismissible SnackBar', ), ), ], @@ -121,7 +124,7 @@ class _MyAppState extends State { color: Colors.grey.withOpacity(0.4), spreadRadius: 6, blurRadius: 10, - offset: Offset(0, 3), + offset: const Offset(0, 3), ), ], ), @@ -130,7 +133,7 @@ class _MyAppState extends State { child: Center( child: Text( text, - style: TextStyle( + style: const TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600, From fdd771329918a419d312aea5d454c81c2b258846 Mon Sep 17 00:00:00 2001 From: Yuriy Trofimov Date: Tue, 15 Nov 2022 13:18:05 +0200 Subject: [PATCH 08/10] Change package version to 3.0.0 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 5e51ade..a808498 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: top_snackbar_flutter description: Top snack bar package was created for nice and convenient way to inform users about what happened. -version: 2.1.1 +version: 3.0.0 homepage: https://github.com/LanarsInc/top-snackbar-flutter environment: From d36dbef66a1288454ab25b6646cf27a9711f145c Mon Sep 17 00:00:00 2001 From: Yuriy Trofimov Date: Tue, 15 Nov 2022 13:18:33 +0200 Subject: [PATCH 09/10] Update CHANGELOG.md file --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79b9c62..d414acb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ +## 3.0.0 - 15.11.2022 +* **Breaking change**. Remove BuildContext in favor of OverlayState +* **Breaking change**. Add multi direction swipe for DismissType.onSwipe by [mofadillah](https://github.com/mofadillah) 'dismissDirection' change type from DismissDirection to List +* Enhancement: Add TextAlign parameter to CustomSnackBar widget by [ayoubrajabi](https://github.com/ayoubrajabi) +* Bugfix [#35](https://github.com/LanarsInc/top-snackbar-flutter/issues/35) Snackbar stuck on status bar + ## 2.1.1 - 07.07.2022 * Fix dismissible widget dismissed error + ## 2.1.0 - 04.07.2022 * Enhancement: Add `safeAreaValues` parameter by [LeGoffMael](https://github.com/LeGoffMael) * Enhancement: Make `top_snackbar_flutter` dismissible by swipe with parameters `dismissType` and `dismissDirection` by [LeGoffMael](https://github.com/LeGoffMael) From ab8ec3a41b5fbc03f245367ad548e0bd5f3d1d40 Mon Sep 17 00:00:00 2001 From: Yuriy Trofimov Date: Tue, 15 Nov 2022 13:29:28 +0200 Subject: [PATCH 10/10] Make OverlayState parameter as positional --- example/lib/main.dart | 10 +++++----- lib/top_snack_bar.dart | 11 +++++------ 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index cca2848..6440a4b 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -32,11 +32,11 @@ class MyAppState extends State { TapBounceContainer( onTap: () { showTopSnackBar( + Overlay.of(context), const CustomSnackBar.info( message: 'There is some information. You need to do something with that', ), - overlayState: Overlay.of(context), ); }, child: buildButton(context, 'Show info'), @@ -45,11 +45,11 @@ class MyAppState extends State { TapBounceContainer( onTap: () { showTopSnackBar( + Overlay.of(context), const CustomSnackBar.success( message: 'Good job, your release is successful. Have a nice day', ), - overlayState: Overlay.of(context), ); }, child: buildButton(context, 'Show success'), @@ -58,11 +58,11 @@ class MyAppState extends State { TapBounceContainer( onTap: () { showTopSnackBar( + Overlay.of(context), const CustomSnackBar.error( message: 'Something went wrong. Please check your credentials and try again', ), - overlayState: Overlay.of(context), ); }, child: buildButton(context, 'Show error'), @@ -71,11 +71,11 @@ class MyAppState extends State { TapBounceContainer( onTap: () { showTopSnackBar( + Overlay.of(context), const CustomSnackBar.info( message: 'Persistent SnackBar', ), persistent: true, - overlayState: Overlay.of(context), onAnimationControllerInit: (controller) { localAnimationController = controller; }, @@ -92,12 +92,12 @@ class MyAppState extends State { TapBounceContainer( onTap: () { showTopSnackBar( + Overlay.of(context), const CustomSnackBar.info( message: 'Try to swipe me left', ), dismissType: DismissType.onSwipe, dismissDirection: [DismissDirection.endToStart], - overlayState: Overlay.of(context), ); }, child: buildButton( diff --git a/lib/top_snack_bar.dart b/lib/top_snack_bar.dart index bc16ea8..d9f93f2 100644 --- a/lib/top_snack_bar.dart +++ b/lib/top_snack_bar.dart @@ -10,6 +10,9 @@ enum DismissType { onTap, onSwipe, none } OverlayEntry? _previousEntry; +/// The [overlayState] argument is used to add specific overlay state. +/// If you are sure that there is a overlay state in your [BuildContext], +/// You can get it [Overlay.of(BuildContext)] /// Displays a widget that will be passed to [child] parameter above the current /// contents of the app, with transition animation /// @@ -25,10 +28,6 @@ OverlayEntry? _previousEntry; /// /// The [onTap] callback of [_TopSnackBar] /// -/// The [overlayState] required argument is used to add specific overlay state. -/// If you are sure that there is a overlay state in your [BuildContext], -/// You can get it [Overlay.of(BuildContext)] -/// /// The [persistent] argument is used to make snack bar persistent, so /// [displayDuration] will be ignored. Default is false. /// @@ -50,8 +49,8 @@ OverlayEntry? _previousEntry; /// can be dismissed. This argument is only used when [dismissType] is equal /// to `DismissType.onSwipe`. Defaults to `[DismissDirection.up]` void showTopSnackBar( + OverlayState overlayState, Widget child, { - required OverlayState overlayState, Duration animationDuration = const Duration(milliseconds: 1200), Duration reverseAnimationDuration = const Duration(milliseconds: 550), Duration displayDuration = const Duration(milliseconds: 3000), @@ -139,7 +138,7 @@ class _TopSnackBar extends StatefulWidget { class _TopSnackBarState extends State<_TopSnackBar> with SingleTickerProviderStateMixin { - late Animation _offsetAnimation; + late final Animation _offsetAnimation; late final AnimationController _animationController; Timer? _timer;