Toggle navigation
Toggle navigation
This project
Loading...
Sign in
flutter_package
/
fluttertpc_get
Go to a project
Toggle navigation
Projects
Groups
Snippets
Help
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
Jonny Borges
2021-11-27 16:10:48 -0300
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
8d0552e059bf1f23bac22fb897172d39b47d6f15
8d0552e0
1 parent
20d103f7
add snackbars tests and fix bugs
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
852 additions
and
471 deletions
example/lib/pages/home/presentation/views/home_view.dart
lib/get_navigation/src/extension_navigation.dart
lib/get_navigation/src/routes/get_transition_mixin.dart
lib/get_navigation/src/snackbar/snackbar.dart
lib/get_navigation/src/snackbar/snackbar_controller.dart
lib/get_rx/src/rx_types/rx_core/rx_interface.dart
lib/get_state_manager/src/rx_flutter/rx_ticket_provider_mixin.dart
test/navigation/get_main_test.dart
test/navigation/parse_route_test.dart
test/navigation/routes_test.dart
test/navigation/snackbar_test.dart
example/lib/pages/home/presentation/views/home_view.dart
View file @
8d0552e
...
...
@@ -21,6 +21,12 @@ class HomeView extends GetView<HomeController> {
child:
Scaffold
(
backgroundColor:
Colors
.
transparent
,
appBar:
AppBar
(
leading:
IconButton
(
icon:
Icon
(
Icons
.
add
),
onPressed:
()
{
Get
.
snackbar
(
'title'
,
'message'
);
},
),
title:
Text
(
'covid'
.
tr
),
backgroundColor:
Colors
.
white10
,
elevation:
0
,
...
...
lib/get_navigation/src/extension_navigation.dart
View file @
8d0552e
import
'dart:ui'
as
ui
;
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/scheduler.dart'
;
...
...
@@ -276,7 +277,7 @@ extension ExtensionDialog on GetInterface {
}
extension
ExtensionSnackbar
on
GetInterface
{
void
rawSnackbar
({
SnackbarController
rawSnackbar
({
String
?
title
,
String
?
message
,
Widget
?
titleText
,
...
...
@@ -296,7 +297,7 @@ extension ExtensionSnackbar on GetInterface {
Gradient
?
backgroundGradient
,
Widget
?
mainButton
,
OnTap
?
onTap
,
Duration
duration
=
const
Duration
(
seconds:
3
),
Duration
?
duration
=
const
Duration
(
seconds:
3
),
bool
isDismissible
=
true
,
DismissDirection
?
dismissDirection
,
bool
showProgressIndicator
=
false
,
...
...
@@ -309,12 +310,12 @@ extension ExtensionSnackbar on GetInterface {
Curve
reverseAnimationCurve
=
Curves
.
easeOutCirc
,
Duration
animationDuration
=
const
Duration
(
seconds:
1
),
SnackbarStatusCallback
?
snackbarStatus
,
double
?
barBlur
=
0.0
,
double
barBlur
=
0.0
,
double
overlayBlur
=
0.0
,
Color
?
overlayColor
,
Form
?
userInputForm
,
})
async
{
final
getBar
=
GetSnackBar
(
})
{
final
getSnackBar
=
GetSnackBar
(
snackbarStatus:
snackbarStatus
,
title:
title
,
message:
message
,
...
...
@@ -352,13 +353,16 @@ extension ExtensionSnackbar on GetInterface {
userInputForm:
userInputForm
,
);
final
controller
=
SnackbarController
(
getSnackBar
);
if
(
instantInit
)
{
getBa
r
.
show
();
controlle
r
.
show
();
}
else
{
SchedulerBinding
.
instance
!.
addPostFrameCallback
((
_
)
{
getBa
r
.
show
();
controlle
r
.
show
();
});
}
return
controller
;
}
SnackbarController
showSnackbar
(
GetSnackBar
snackbar
)
{
...
...
@@ -371,7 +375,7 @@ extension ExtensionSnackbar on GetInterface {
String
title
,
String
message
,
{
Color
?
colorText
,
Duration
?
duration
,
Duration
?
duration
=
const
Duration
(
seconds:
3
)
,
/// with instantInit = false you can put snackbar on initState
bool
instantInit
=
true
,
...
...
@@ -431,7 +435,7 @@ extension ExtensionSnackbar on GetInterface {
snackPosition:
snackPosition
??
SnackPosition
.
TOP
,
borderRadius:
borderRadius
??
15
,
margin:
margin
??
EdgeInsets
.
symmetric
(
horizontal:
10
),
duration:
duration
??
Duration
(
seconds:
3
)
,
duration:
duration
,
barBlur:
barBlur
??
7.0
,
backgroundColor:
backgroundColor
??
Colors
.
grey
.
withOpacity
(
0.2
),
icon:
icon
,
...
...
@@ -517,6 +521,7 @@ extension GetNavigation on GetInterface {
routeName
??=
"/
${page.runtimeType}
"
;
routeName
=
_cleanRouteName
(
routeName
);
if
(
preventDuplicates
&&
routeName
==
currentRoute
)
{
CupertinoPageRoute
ds
;
return
null
;
}
return
global
(
id
).
currentState
?.
push
<
T
>(
...
...
lib/get_navigation/src/routes/get_transition_mixin.dart
View file @
8d0552e
...
...
@@ -5,111 +5,247 @@ import 'package:flutter/cupertino.dart';
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/material.dart'
;
import
'../../../get.dart'
;
import
'../../../get.dart'
;
import
'default_transitions.dart'
;
import
'transitions_type.dart'
;
const
double
_kBackGestureWidth
=
20.0
;
const
double
_kMinFlingVelocity
=
1.0
;
// Screen widths per second.
const
int
_kMaxDroppedSwipePageForwardAnimationTime
=
800
;
// Screen widths per second.
// An eyeballed value for the maximum time it takes
//for a page to animate forward
// if the user releases a page mid swipe.
const
int
_kMax
DroppedSwipePageForwardAnimationTime
=
8
00
;
// Milliseconds.
const
int
_kMax
PageBackAnimationTime
=
3
00
;
// Milliseconds.
// The maximum time for a page to get reset to it's original position if the
// user releases a page mid swipe.
const
int
_kMaxPageBackAnimationTime
=
30
0
;
// Milliseconds.
const
double
_kMinFlingVelocity
=
1.
0
;
// Milliseconds.
mixin
GetPageRouteTransitionMixin
<
T
>
on
PageRoute
<
T
>
{
/// Builds the primary contents of the route.
@protected
Widget
buildContent
(
BuildContext
context
);
class
CupertinoBackGestureController
<
T
>
{
final
AnimationController
controller
;
/// {@template flutter.cupertino.CupertinoRouteTransitionMixin.title}
/// A title string for this route.
final
NavigatorState
navigator
;
/// Creates a controller for an iOS-style back gesture.
///
/// Used to auto-populate [CupertinoNavigationBar] and
/// [CupertinoSliverNavigationBar]'s `middle`/`largeTitle` widgets when
/// one is not manually supplied.
/// {@endtemplate}
String
?
get
title
;
/// The [navigator] and [controller] arguments must not be null.
CupertinoBackGestureController
({
required
this
.
navigator
,
required
this
.
controller
,
})
{
navigator
.
didStartUserGesture
();
}
double
Function
(
BuildContext
context
)?
get
gestureWidth
;
/// The drag gesture has ended with a horizontal motion of
/// [fractionalVelocity] as a fraction of screen width per second.
void
dragEnd
(
double
velocity
)
{
// Fling in the appropriate direction.
// AnimationController.fling is guaranteed to
// take at least one frame.
//
// This curve has been determined through rigorously eyeballing native iOS
// animations.
const
Curve
animationCurve
=
Curves
.
fastLinearToSlowEaseIn
;
final
bool
animateForward
;
ValueNotifier
<
String
?>?
_previousTitle
;
// If the user releases the page before mid screen with sufficient velocity,
// or after mid screen, we should animate the page out. Otherwise, the page
// should be animated back in.
if
(
velocity
.
abs
()
>=
_kMinFlingVelocity
)
{
animateForward
=
velocity
<=
0
;
}
else
{
animateForward
=
controller
.
value
>
0.5
;
}
/// The title string of the previous [CupertinoPageRoute].
///
/// The [ValueListenable]'s value is readable after the route is installed
/// onto a [Navigator]. The [ValueListenable] will also notify its listeners
/// if the value changes (such as by replacing the previous route).
///
/// The [ValueListenable] itself will be null before the route is installed.
/// Its content value will be null if the previous route has no title or
/// is not a [CupertinoPageRoute].
///
/// See also:
///
/// * [ValueListenableBuilder], which can be used to listen and rebuild
/// widgets based on a ValueListenable.
ValueListenable
<
String
?>
get
previousTitle
{
assert
(
_previousTitle
!=
null
,
'''
Cannot read the previousTitle for a route that has not yet been installed'''
,
);
return
_previousTitle
!;
}
if
(
animateForward
)
{
// The closer the panel is to dismissing, the shorter the animation is.
// We want to cap the animation time, but we want to use a linear curve
// to determine it.
final
droppedPageForwardAnimationTime
=
min
(
lerpDouble
(
_kMaxDroppedSwipePageForwardAnimationTime
,
0
,
controller
.
value
)!
.
floor
(),
_kMaxPageBackAnimationTime
,
);
controller
.
animateTo
(
1.0
,
duration:
Duration
(
milliseconds:
droppedPageForwardAnimationTime
),
curve:
animationCurve
);
}
else
{
// This route is destined to pop at this point. Reuse navigator's pop.
navigator
.
pop
();
@override
void
didChangePrevious
(
Route
<
dynamic
>?
previousRoute
)
{
final
previousTitleString
=
previousRoute
is
CupertinoRouteTransitionMixin
?
previousRoute
.
title
:
null
;
if
(
_previousTitle
==
null
)
{
_previousTitle
=
ValueNotifier
<
String
?>(
previousTitleString
);
// The popping may have finished inline if already at the
// target destination.
if
(
controller
.
isAnimating
)
{
// Otherwise, use a custom popping animation duration and curve.
final
droppedPageBackAnimationTime
=
lerpDouble
(
0
,
_kMaxDroppedSwipePageForwardAnimationTime
,
controller
.
value
)!
.
floor
();
controller
.
animateBack
(
0.0
,
duration:
Duration
(
milliseconds:
droppedPageBackAnimationTime
),
curve:
animationCurve
);
}
}
if
(
controller
.
isAnimating
)
{
// Keep the userGestureInProgress in true state so we don't change the
// curve of the page transition mid-flight since CupertinoPageTransition
// depends on userGestureInProgress.
late
AnimationStatusListener
animationStatusCallback
;
animationStatusCallback
=
(
status
)
{
navigator
.
didStopUserGesture
();
controller
.
removeStatusListener
(
animationStatusCallback
);
};
controller
.
addStatusListener
(
animationStatusCallback
);
}
else
{
_previousTitle
!.
value
=
previousTitleString
;
navigator
.
didStopUserGesture
()
;
}
super
.
didChangePrevious
(
previousRoute
);
}
@override
// A relatively rigorous eyeball estimation.
Duration
get
transitionDuration
=>
const
Duration
(
milliseconds:
400
);
/// The drag gesture has changed by [fractionalDelta]. The total range of the
/// drag should be 0.0 to 1.0.
void
dragUpdate
(
double
delta
)
{
controller
.
value
-=
delta
;
}
}
class
CupertinoBackGestureDetector
<
T
>
extends
StatefulWidget
{
final
Widget
child
;
final
double
gestureWidth
;
final
ValueGetter
<
bool
>
enabledCallback
;
final
ValueGetter
<
CupertinoBackGestureController
<
T
>>
onStartPopGesture
;
const
CupertinoBackGestureDetector
({
Key
?
key
,
required
this
.
enabledCallback
,
required
this
.
onStartPopGesture
,
required
this
.
child
,
required
this
.
gestureWidth
,
})
:
super
(
key:
key
);
@override
Color
?
get
barrierColor
=>
null
;
CupertinoBackGestureDetectorState
<
T
>
createState
()
=>
CupertinoBackGestureDetectorState
<
T
>();
}
class
CupertinoBackGestureDetectorState
<
T
>
extends
State
<
CupertinoBackGestureDetector
<
T
>>
{
CupertinoBackGestureController
<
T
>?
_backGestureController
;
late
HorizontalDragGestureRecognizer
_recognizer
;
@override
String
?
get
barrierLabel
=>
null
;
Widget
build
(
BuildContext
context
)
{
assert
(
debugCheckHasDirectionality
(
context
));
// For devices with notches, the drag area needs to be larger on the side
// that has the notch.
var
dragAreaWidth
=
Directionality
.
of
(
context
)
==
TextDirection
.
ltr
?
MediaQuery
.
of
(
context
).
padding
.
left
:
MediaQuery
.
of
(
context
).
padding
.
right
;
dragAreaWidth
=
max
(
dragAreaWidth
,
widget
.
gestureWidth
);
return
Stack
(
fit:
StackFit
.
passthrough
,
children:
<
Widget
>[
widget
.
child
,
PositionedDirectional
(
start:
0.0
,
width:
dragAreaWidth
,
top:
0.0
,
bottom:
0.0
,
child:
Listener
(
onPointerDown:
_handlePointerDown
,
behavior:
HitTestBehavior
.
translucent
,
),
),
],
);
}
bool
get
showCupertinoParallax
;
@override
void
dispose
()
{
_recognizer
.
dispose
();
super
.
dispose
();
}
@override
bool
canTransitionTo
(
TransitionRoute
<
dynamic
>
nextRoute
)
{
// Don't perform outgoing animation if the next route is a
// fullscreen dialog.
void
initState
()
{
super
.
initState
();
_recognizer
=
HorizontalDragGestureRecognizer
(
debugOwner:
this
)
..
onStart
=
_handleDragStart
..
onUpdate
=
_handleDragUpdate
..
onEnd
=
_handleDragEnd
..
onCancel
=
_handleDragCancel
;
}
return
nextRoute
is
GetPageRouteTransitionMixin
&&
!
nextRoute
.
fullscreenDialog
&&
nextRoute
.
showCupertinoParallax
;
double
_convertToLogical
(
double
value
)
{
switch
(
Directionality
.
of
(
context
))
{
case
TextDirection
.
rtl
:
return
-
value
;
case
TextDirection
.
ltr
:
return
value
;
}
}
/// True if an iOS-style back swipe pop gesture is currently
/// underway for [route].
void
_handleDragCancel
()
{
assert
(
mounted
);
// This can be called even if start is not called, paired with
// the "down" event
// that we don't consider here.
_backGestureController
?.
dragEnd
(
0.0
);
_backGestureController
=
null
;
}
void
_handleDragEnd
(
DragEndDetails
details
)
{
assert
(
mounted
);
assert
(
_backGestureController
!=
null
);
_backGestureController
!.
dragEnd
(
_convertToLogical
(
details
.
velocity
.
pixelsPerSecond
.
dx
/
context
.
size
!.
width
));
_backGestureController
=
null
;
}
void
_handleDragStart
(
DragStartDetails
details
)
{
assert
(
mounted
);
assert
(
_backGestureController
==
null
);
_backGestureController
=
widget
.
onStartPopGesture
();
}
void
_handleDragUpdate
(
DragUpdateDetails
details
)
{
assert
(
mounted
);
assert
(
_backGestureController
!=
null
);
_backGestureController
!.
dragUpdate
(
_convertToLogical
(
details
.
primaryDelta
!
/
context
.
size
!.
width
));
}
void
_handlePointerDown
(
PointerDownEvent
event
)
{
if
(
widget
.
enabledCallback
())
_recognizer
.
addPointer
(
event
);
}
}
mixin
GetPageRouteTransitionMixin
<
T
>
on
PageRoute
<
T
>
{
ValueNotifier
<
String
?>?
_previousTitle
;
@override
Color
?
get
barrierColor
=>
null
;
@override
String
?
get
barrierLabel
=>
null
;
double
Function
(
BuildContext
context
)?
get
gestureWidth
;
/// Whether a pop gesture can be started by the user.
///
///
This just check the route's [NavigatorState.userGestureInProgress]
.
///
Returns true if the user can edge-swipe to a previous route
.
///
/// See also:
/// Returns false once [isPopGestureInProgress] is true, but
/// [isPopGestureInProgress] can only become true if [popGestureEnabled] was
/// true first.
///
/// * [popGestureEnabled], which returns true if a user-triggered pop gesture
/// would be allowed.
static
bool
isPopGestureInProgress
(
PageRoute
<
dynamic
>
route
)
{
return
route
.
navigator
!.
userGestureInProgress
;
}
/// This should only be used between frames, not during build.
bool
get
popGestureEnabled
=>
_isPopGestureEnabled
(
this
);
/// True if an iOS-style back swipe pop gesture is currently
/// underway for this route.
...
...
@@ -122,44 +258,47 @@ Cannot read the previousTitle for a route that has not yet been installed''',
/// would be allowed.
bool
get
popGestureInProgress
=>
isPopGestureInProgress
(
this
);
///
Whether a pop gesture can be started by the user
.
///
The title string of the previous [CupertinoPageRoute]
.
///
/// Returns true if the user can edge-swipe to a previous route.
/// The [ValueListenable]'s value is readable after the route is installed
/// onto a [Navigator]. The [ValueListenable] will also notify its listeners
/// if the value changes (such as by replacing the previous route).
///
/// Returns false once [isPopGestureInProgress] is true, but
/// [isPopGestureInProgress] can only become true if [popGestureEnabled] was
/// true first.
/// The [ValueListenable] itself will be null before the route is installed.
/// Its content value will be null if the previous route has no title or
/// is not a [CupertinoPageRoute].
///
/// This should only be used between frames, not during build.
bool
get
popGestureEnabled
=>
_isPopGestureEnabled
(
this
);
/// See also:
///
/// * [ValueListenableBuilder], which can be used to listen and rebuild
/// widgets based on a ValueListenable.
ValueListenable
<
String
?>
get
previousTitle
{
assert
(
_previousTitle
!=
null
,
'''
Cannot read the previousTitle for a route that has not yet been installed'''
,
);
return
_previousTitle
!;
}
static
bool
_isPopGestureEnabled
<
T
>(
PageRoute
<
T
>
route
)
{
// If there's nothing to go back to, then obviously we don't support
// the back gesture.
if
(
route
.
isFirst
)
return
false
;
// If the route wouldn't actually pop if we popped it, then the gesture
// would be really confusing (or would skip internal routes),
//so disallow it.
if
(
route
.
willHandlePopInternally
)
return
false
;
// If attempts to dismiss this route might be vetoed such as in a page
// with forms, then do not allow the user to dismiss the route with a swipe.
if
(
route
.
hasScopedWillPopCallback
)
return
false
;
// Fullscreen dialogs aren't dismissible by back swipe.
if
(
route
.
fullscreenDialog
)
return
false
;
// If we're in an animation already, we cannot be manually swiped.
if
(
route
.
animation
!.
status
!=
AnimationStatus
.
completed
)
return
false
;
// If we're being popped into, we also cannot be swiped until the pop above
// it completes. This translates to our secondary animation being
// dismissed.
if
(
route
.
secondaryAnimation
!.
status
!=
AnimationStatus
.
dismissed
)
{
return
false
;
}
// If we're in a gesture already, we cannot start another.
if
(
isPopGestureInProgress
(
route
))
return
false
;
bool
get
showCupertinoParallax
;
// Looks like a back gesture would be welcome!
return
true
;
}
/// {@template flutter.cupertino.CupertinoRouteTransitionMixin.title}
/// A title string for this route.
///
/// Used to auto-populate [CupertinoNavigationBar] and
/// [CupertinoSliverNavigationBar]'s `middle`/`largeTitle` widgets when
/// one is not manually supplied.
/// {@endtemplate}
String
?
get
title
;
@override
// A relatively rigorous eyeball estimation.
Duration
get
transitionDuration
=>
const
Duration
(
milliseconds:
400
);
/// Builds the primary contents of the route.
@protected
Widget
buildContent
(
BuildContext
context
);
@override
Widget
buildPage
(
BuildContext
context
,
Animation
<
double
>
animation
,
...
...
@@ -173,17 +312,36 @@ Cannot read the previousTitle for a route that has not yet been installed''',
return
result
;
}
// Called by CupertinoBackGestureDetector when a pop ("back") drag start
// gesture is detected. The returned controller handles all of the subsequent
// drag events.
static
CupertinoBackGestureController
<
T
>
_startPopGesture
<
T
>(
PageRoute
<
T
>
route
)
{
assert
(
_isPopGestureEnabled
(
route
));
@override
Widget
buildTransitions
(
BuildContext
context
,
Animation
<
double
>
animation
,
Animation
<
double
>
secondaryAnimation
,
Widget
child
)
{
return
buildPageTransitions
<
T
>(
this
,
context
,
animation
,
secondaryAnimation
,
child
);
}
return
CupertinoBackGestureController
<
T
>(
navigator:
route
.
navigator
!,
controller:
route
.
controller
!,
// protected access
);
@override
bool
canTransitionTo
(
TransitionRoute
<
dynamic
>
nextRoute
)
{
// Don't perform outgoing animation if the next route is a
// fullscreen dialog.
return
(
nextRoute
is
GetPageRouteTransitionMixin
&&
!
nextRoute
.
fullscreenDialog
&&
nextRoute
.
showCupertinoParallax
)
||
(
nextRoute
is
CupertinoRouteTransitionMixin
&&
!
nextRoute
.
fullscreenDialog
);
}
@override
void
didChangePrevious
(
Route
<
dynamic
>?
previousRoute
)
{
final
previousTitleString
=
previousRoute
is
CupertinoRouteTransitionMixin
?
previousRoute
.
title
:
null
;
if
(
_previousTitle
==
null
)
{
_previousTitle
=
ValueNotifier
<
String
?>(
previousTitleString
);
}
else
{
_previousTitle
!.
value
=
previousTitleString
;
}
super
.
didChangePrevious
(
previousRoute
);
}
/// Returns a [CupertinoFullscreenDialogTransition] if [route] is a full
...
...
@@ -485,211 +643,57 @@ Cannot read the previousTitle for a route that has not yet been installed''',
}
}
@override
Widget
buildTransitions
(
BuildContext
context
,
Animation
<
double
>
animation
,
Animation
<
double
>
secondaryAnimation
,
Widget
child
)
{
return
buildPageTransitions
<
T
>(
this
,
context
,
animation
,
secondaryAnimation
,
child
);
}
}
class
CupertinoBackGestureDetector
<
T
>
extends
StatefulWidget
{
const
CupertinoBackGestureDetector
({
Key
?
key
,
required
this
.
enabledCallback
,
required
this
.
onStartPopGesture
,
required
this
.
child
,
required
this
.
gestureWidth
,
})
:
super
(
key:
key
);
final
Widget
child
;
final
double
gestureWidth
;
final
ValueGetter
<
bool
>
enabledCallback
;
final
ValueGetter
<
CupertinoBackGestureController
<
T
>>
onStartPopGesture
;
@override
CupertinoBackGestureDetectorState
<
T
>
createState
()
=>
CupertinoBackGestureDetectorState
<
T
>();
}
class
CupertinoBackGestureDetectorState
<
T
>
extends
State
<
CupertinoBackGestureDetector
<
T
>>
{
CupertinoBackGestureController
<
T
>?
_backGestureController
;
late
HorizontalDragGestureRecognizer
_recognizer
;
@override
void
initState
()
{
super
.
initState
();
_recognizer
=
HorizontalDragGestureRecognizer
(
debugOwner:
this
)
..
onStart
=
_handleDragStart
..
onUpdate
=
_handleDragUpdate
..
onEnd
=
_handleDragEnd
..
onCancel
=
_handleDragCancel
;
}
@override
void
dispose
()
{
_recognizer
.
dispose
();
super
.
dispose
();
}
void
_handleDragStart
(
DragStartDetails
details
)
{
assert
(
mounted
);
assert
(
_backGestureController
==
null
);
_backGestureController
=
widget
.
onStartPopGesture
();
}
void
_handleDragUpdate
(
DragUpdateDetails
details
)
{
assert
(
mounted
);
assert
(
_backGestureController
!=
null
);
_backGestureController
!.
dragUpdate
(
_convertToLogical
(
details
.
primaryDelta
!
/
context
.
size
!.
width
));
}
void
_handleDragEnd
(
DragEndDetails
details
)
{
assert
(
mounted
);
assert
(
_backGestureController
!=
null
);
_backGestureController
!.
dragEnd
(
_convertToLogical
(
details
.
velocity
.
pixelsPerSecond
.
dx
/
context
.
size
!.
width
));
_backGestureController
=
null
;
}
void
_handleDragCancel
()
{
assert
(
mounted
);
// This can be called even if start is not called, paired with
// the "down" event
// that we don't consider here.
_backGestureController
?.
dragEnd
(
0.0
);
_backGestureController
=
null
;
}
void
_handlePointerDown
(
PointerDownEvent
event
)
{
if
(
widget
.
enabledCallback
())
_recognizer
.
addPointer
(
event
);
}
double
_convertToLogical
(
double
value
)
{
switch
(
Directionality
.
of
(
context
))
{
case
TextDirection
.
rtl
:
return
-
value
;
case
TextDirection
.
ltr
:
return
value
;
}
}
@override
Widget
build
(
BuildContext
context
)
{
assert
(
debugCheckHasDirectionality
(
context
));
// For devices with notches, the drag area needs to be larger on the side
// that has the notch.
var
dragAreaWidth
=
Directionality
.
of
(
context
)
==
TextDirection
.
ltr
?
MediaQuery
.
of
(
context
).
padding
.
left
:
MediaQuery
.
of
(
context
).
padding
.
right
;
dragAreaWidth
=
max
(
dragAreaWidth
,
widget
.
gestureWidth
);
return
Stack
(
fit:
StackFit
.
passthrough
,
children:
<
Widget
>[
widget
.
child
,
PositionedDirectional
(
start:
0.0
,
width:
dragAreaWidth
,
top:
0.0
,
bottom:
0.0
,
child:
Listener
(
onPointerDown:
_handlePointerDown
,
behavior:
HitTestBehavior
.
translucent
,
),
),
],
);
}
}
class
CupertinoBackGestureController
<
T
>
{
/// Creates a controller for an iOS-style back gesture.
// Called by CupertinoBackGestureDetector when a pop ("back") drag start
// gesture is detected. The returned controller handles all of the subsequent
// drag events.
/// True if an iOS-style back swipe pop gesture is currently
/// underway for [route].
///
/// The [navigator] and [controller] arguments must not be null.
CupertinoBackGestureController
({
required
this
.
navigator
,
required
this
.
controller
,
})
{
navigator
.
didStartUserGesture
();
}
final
AnimationController
controller
;
final
NavigatorState
navigator
;
/// The drag gesture has changed by [fractionalDelta]. The total range of the
/// drag should be 0.0 to 1.0.
void
dragUpdate
(
double
delta
)
{
controller
.
value
-=
delta
;
/// This just check the route's [NavigatorState.userGestureInProgress].
///
/// See also:
///
/// * [popGestureEnabled], which returns true if a user-triggered pop gesture
/// would be allowed.
static
bool
isPopGestureInProgress
(
PageRoute
<
dynamic
>
route
)
{
return
route
.
navigator
!.
userGestureInProgress
;
}
/// The drag gesture has ended with a horizontal motion of
/// [fractionalVelocity] as a fraction of screen width per second.
void
dragEnd
(
double
velocity
)
{
// Fling in the appropriate direction.
// AnimationController.fling is guaranteed to
// take at least one frame.
//
// This curve has been determined through rigorously eyeballing native iOS
// animations.
const
Curve
animationCurve
=
Curves
.
fastLinearToSlowEaseIn
;
final
bool
animateForward
;
// If the user releases the page before mid screen with sufficient velocity,
// or after mid screen, we should animate the page out. Otherwise, the page
// should be animated back in.
if
(
velocity
.
abs
()
>=
_kMinFlingVelocity
)
{
animateForward
=
velocity
<=
0
;
}
else
{
animateForward
=
controller
.
value
>
0.5
;
static
bool
_isPopGestureEnabled
<
T
>(
PageRoute
<
T
>
route
)
{
// If there's nothing to go back to, then obviously we don't support
// the back gesture.
if
(
route
.
isFirst
)
return
false
;
// If the route wouldn't actually pop if we popped it, then the gesture
// would be really confusing (or would skip internal routes),
//so disallow it.
if
(
route
.
willHandlePopInternally
)
return
false
;
// If attempts to dismiss this route might be vetoed such as in a page
// with forms, then do not allow the user to dismiss the route with a swipe.
if
(
route
.
hasScopedWillPopCallback
)
return
false
;
// Fullscreen dialogs aren't dismissible by back swipe.
if
(
route
.
fullscreenDialog
)
return
false
;
// If we're in an animation already, we cannot be manually swiped.
if
(
route
.
animation
!.
status
!=
AnimationStatus
.
completed
)
return
false
;
// If we're being popped into, we also cannot be swiped until the pop above
// it completes. This translates to our secondary animation being
// dismissed.
if
(
route
.
secondaryAnimation
!.
status
!=
AnimationStatus
.
dismissed
)
{
return
false
;
}
// If we're in a gesture already, we cannot start another.
if
(
isPopGestureInProgress
(
route
))
return
false
;
if
(
animateForward
)
{
// The closer the panel is to dismissing, the shorter the animation is.
// We want to cap the animation time, but we want to use a linear curve
// to determine it.
final
droppedPageForwardAnimationTime
=
min
(
lerpDouble
(
_kMaxDroppedSwipePageForwardAnimationTime
,
0
,
controller
.
value
)!
.
floor
(),
_kMaxPageBackAnimationTime
,
);
controller
.
animateTo
(
1.0
,
duration:
Duration
(
milliseconds:
droppedPageForwardAnimationTime
),
curve:
animationCurve
);
}
else
{
// This route is destined to pop at this point. Reuse navigator's pop.
navigator
.
pop
();
// Looks like a back gesture would be welcome!
return
true
;
}
// The popping may have finished inline if already at the
// target destination.
if
(
controller
.
isAnimating
)
{
// Otherwise, use a custom popping animation duration and curve.
final
droppedPageBackAnimationTime
=
lerpDouble
(
0
,
_kMaxDroppedSwipePageForwardAnimationTime
,
controller
.
value
)!
.
floor
();
controller
.
animateBack
(
0.0
,
duration:
Duration
(
milliseconds:
droppedPageBackAnimationTime
),
curve:
animationCurve
);
}
}
static
CupertinoBackGestureController
<
T
>
_startPopGesture
<
T
>(
PageRoute
<
T
>
route
)
{
assert
(
_isPopGestureEnabled
(
route
));
if
(
controller
.
isAnimating
)
{
// Keep the userGestureInProgress in true state so we don't change the
// curve of the page transition mid-flight since CupertinoPageTransition
// depends on userGestureInProgress.
late
AnimationStatusListener
animationStatusCallback
;
animationStatusCallback
=
(
status
)
{
navigator
.
didStopUserGesture
();
controller
.
removeStatusListener
(
animationStatusCallback
);
};
controller
.
addStatusListener
(
animationStatusCallback
);
}
else
{
navigator
.
didStopUserGesture
();
}
return
CupertinoBackGestureController
<
T
>(
navigator:
route
.
navigator
!,
controller:
route
.
controller
!,
// protected access
);
}
}
...
...
lib/get_navigation/src/snackbar/snackbar.dart
View file @
8d0552e
...
...
@@ -145,7 +145,7 @@ class GetSnackBar extends StatefulWidget {
/// Default is 0.0. If different than 0.0, blurs only Snack's background.
/// To take effect, make sure your [backgroundColor] has some opacity.
/// The greater the value, the greater the blur.
final
double
?
barBlur
;
final
double
barBlur
;
/// Default is 0.0. If different than 0.0, creates a blurred
/// overlay that prevents the user from interacting with the screen.
...
...
@@ -201,7 +201,7 @@ class GetSnackBar extends StatefulWidget {
})
:
super
(
key:
key
);
@override
State
createState
()
=>
_
GetSnackBarState
();
State
createState
()
=>
GetSnackBarState
();
/// Show the snack. It's call [SnackbarStatus.OPENING] state
/// followed by [SnackbarStatus.OPEN]
...
...
@@ -210,30 +210,7 @@ class GetSnackBar extends StatefulWidget {
}
}
enum
RowStyle
{
icon
,
action
,
all
,
none
,
}
/// Indicates Status of snackbar
/// [SnackbarStatus.OPEN] Snack is fully open, [SnackbarStatus.CLOSED] Snackbar
/// has closed,
/// [SnackbarStatus.OPENING] Starts with the opening animation and ends
/// with the full
/// snackbar display, [SnackbarStatus.CLOSING] Starts with the closing animation
/// and ends
/// with the full snackbar dispose
enum
SnackbarStatus
{
OPEN
,
CLOSED
,
OPENING
,
CLOSING
}
/// Indicates if snack is going to start at the [TOP] or at the [BOTTOM]
enum
SnackPosition
{
TOP
,
BOTTOM
}
/// Indicates if snack will be attached to the edge of the screen or not
enum
SnackStyle
{
FLOATING
,
GROUNDED
}
class
_GetSnackBarState
extends
State
<
GetSnackBar
>
class
GetSnackBarState
extends
State
<
GetSnackBar
>
with
TickerProviderStateMixin
{
AnimationController
?
_fadeController
;
late
Animation
<
double
>
_fadeAnimation
;
...
...
@@ -306,7 +283,7 @@ class _GetSnackBarState extends State<GetSnackBar>
borderRadius:
BorderRadius
.
circular
(
widget
.
borderRadius
),
child:
BackdropFilter
(
filter:
ImageFilter
.
blur
(
sigmaX:
widget
.
barBlur
!,
sigmaY:
widget
.
barBlur
!
),
sigmaX:
widget
.
barBlur
,
sigmaY:
widget
.
barBlur
),
child:
Container
(
height:
snapshot
.
data
!.
height
,
width:
snapshot
.
data
!.
width
,
...
...
@@ -579,3 +556,26 @@ You need to either use message[String], or messageText[Widget] or define a userI
void
_updateProgress
()
=>
setState
(()
{});
}
enum
RowStyle
{
icon
,
action
,
all
,
none
,
}
/// Indicates Status of snackbar
/// [SnackbarStatus.OPEN] Snack is fully open, [SnackbarStatus.CLOSED] Snackbar
/// has closed,
/// [SnackbarStatus.OPENING] Starts with the opening animation and ends
/// with the full
/// snackbar display, [SnackbarStatus.CLOSING] Starts with the closing animation
/// and ends
/// with the full snackbar dispose
enum
SnackbarStatus
{
OPEN
,
CLOSED
,
OPENING
,
CLOSING
}
/// Indicates if snack is going to start at the [TOP] or at the [BOTTOM]
enum
SnackPosition
{
TOP
,
BOTTOM
}
/// Indicates if snack will be attached to the edge of the screen or not
enum
SnackStyle
{
FLOATING
,
GROUNDED
}
...
...
lib/get_navigation/src/snackbar/snackbar_controller.dart
View file @
8d0552e
...
...
@@ -8,12 +8,13 @@ import '../../../get.dart';
class
SnackbarController
{
static
final
_snackBarQueue
=
_SnackBarQueue
();
static
bool
get
isSnackbarBeingShown
=>
_snackBarQueue
.
_isJobInProgress
;
final
key
=
GlobalKey
<
GetSnackBarState
>();
late
Animation
<
double
>
_filterBlurAnimation
;
late
Animation
<
Color
?>
_filterColorAnimation
;
final
GetSnackBar
snackbar
;
final
_transitionCompleter
=
Completer
<
SnackbarController
>
();
final
_transitionCompleter
=
Completer
();
late
SnackbarStatusCallback
?
_snackbarStatus
;
late
final
Alignment
?
_initialAlignment
;
...
...
@@ -42,10 +43,14 @@ class SnackbarController {
SnackbarController
(
this
.
snackbar
);
Future
<
SnackbarController
>
get
future
=>
_transitionCompleter
.
future
;
Future
<
void
>
get
future
=>
_transitionCompleter
.
future
;
/// Close the snackbar with animation
Future
<
void
>
close
()
async
{
Future
<
void
>
close
({
bool
withAnimations
=
true
})
async
{
if
(!
withAnimations
)
{
_removeOverlay
();
return
;
}
_removeEntry
();
await
future
;
}
...
...
@@ -141,7 +146,7 @@ class SnackbarController {
return
AnimationController
(
duration:
snackbar
.
animationDuration
,
debugLabel:
'
$runtimeType
'
,
vsync:
navigator
!,
vsync:
_overlayState
!,
);
}
...
...
@@ -181,7 +186,7 @@ class SnackbarController {
onTap:
()
{
if
(
snackbar
.
isDismissible
&&
!
_onTappedDismiss
)
{
_onTappedDismiss
=
true
;
Get
.
back
();
close
();
}
},
child:
AnimatedBuilder
(
...
...
@@ -252,7 +257,8 @@ class SnackbarController {
},
key:
const
Key
(
'dismissible'
),
onDismissed:
(
_
)
{
_onDismiss
();
_wasDismissedBySwipe
=
true
;
_removeEntry
();
},
child:
_getSnackbarContainer
(
child
),
);
...
...
@@ -291,12 +297,6 @@ class SnackbarController {
}
}
void
_onDismiss
()
{
_cancelTimer
();
_wasDismissedBySwipe
=
true
;
_removeEntry
();
}
void
_removeEntry
()
{
assert
(
!
_transitionCompleter
.
isCompleted
,
...
...
@@ -307,7 +307,6 @@ class SnackbarController {
if
(
_wasDismissedBySwipe
)
{
Timer
(
const
Duration
(
milliseconds:
200
),
_controller
.
reset
);
_wasDismissedBySwipe
=
false
;
}
else
{
_controller
.
reverse
();
...
...
@@ -323,7 +322,7 @@ class SnackbarController {
'Cannot remove overlay from a disposed snackbar'
);
_controller
.
dispose
();
_overlayEntries
.
clear
();
_transitionCompleter
.
complete
(
this
);
_transitionCompleter
.
complete
();
}
Future
<
void
>
_show
()
{
...
...
@@ -365,6 +364,7 @@ class _SnackBarQueue {
}
Future
<
void
>
_closeCurrentJob
()
async
{
await
_currentSnackbar
?.
close
();
if
(
_currentSnackbar
==
null
)
return
;
await
_currentSnackbar
!.
close
();
}
}
...
...
lib/get_rx/src/rx_types/rx_core/rx_interface.dart
View file @
8d0552e
...
...
@@ -5,6 +5,8 @@ part of rx_types;
/// This interface is the contract that _RxImpl]<T> uses in all it's
/// subclass.
abstract
class
RxInterface
<
T
>
{
static
RxInterface
?
proxy
;
bool
get
canUpdate
;
/// Adds a listener to stream
...
...
@@ -13,8 +15,6 @@ abstract class RxInterface<T> {
/// Close the Rx Variable
void
close
();
static
RxInterface
?
proxy
;
/// Calls `callback` with current value, when the value changes.
StreamSubscription
<
T
>
listen
(
void
Function
(
T
event
)
onData
,
{
Function
?
onError
,
void
Function
()?
onDone
,
bool
?
cancelOnError
});
...
...
lib/get_state_manager/src/rx_flutter/rx_ticket_provider_mixin.dart
View file @
8d0552e
// ignore_for_file: lines_longer_than_80_chars
import
'package:flutter/foundation.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/scheduler.dart'
;
import
'../../get_state_manager.dart'
;
/// Used like `SingleTickerProviderMixin` but only with Get Controllers.
...
...
@@ -7,6 +12,84 @@ import '../../get_state_manager.dart';
/// Example:
///```
///class SplashController extends GetxController with
/// GetSingleTickerProviderStateMixin {
/// AnimationController controller;
///
/// @override
/// void onInit() {
/// final duration = const Duration(seconds: 2);
/// controller =
/// AnimationController.unbounded(duration: duration, vsync: this);
/// controller.repeat();
/// controller.addListener(() =>
/// print("Animation Controller value: ${controller.value}"));
/// }
/// ...
/// ```
mixin
GetSingleTickerProviderStateMixin
on
GetxController
implements
TickerProvider
{
Ticker
?
_ticker
;
@override
Ticker
createTicker
(
TickerCallback
onTick
)
{
assert
(()
{
if
(
_ticker
==
null
)
return
true
;
throw
FlutterError
.
fromParts
(<
DiagnosticsNode
>[
ErrorSummary
(
'
$runtimeType
is a SingleTickerProviderStateMixin but multiple tickers were created.'
),
ErrorDescription
(
'A SingleTickerProviderStateMixin can only be used as a TickerProvider once.'
),
ErrorHint
(
'If a State is used for multiple AnimationController objects, or if it is passed to other '
'objects and those objects might use it more than one time in total, then instead of '
'mixing in a SingleTickerProviderStateMixin, use a regular TickerProviderStateMixin.'
,
),
]);
}());
_ticker
=
Ticker
(
onTick
,
debugLabel:
kDebugMode
?
'created by
$this
'
:
null
);
// We assume that this is called from initState, build, or some sort of
// event handler, and that thus TickerMode.of(context) would return true. We
// can't actually check that here because if we're in initState then we're
// not allowed to do inheritance checks yet.
return
_ticker
!;
}
void
didChangeDependencies
(
BuildContext
context
)
{
if
(
_ticker
!=
null
)
_ticker
!.
muted
=
!
TickerMode
.
of
(
context
);
}
@override
void
onClose
()
{
assert
(()
{
if
(
_ticker
==
null
||
!
_ticker
!.
isActive
)
return
true
;
throw
FlutterError
.
fromParts
(<
DiagnosticsNode
>[
ErrorSummary
(
'
$this
was disposed with an active Ticker.'
),
ErrorDescription
(
'
$runtimeType
created a Ticker via its SingleTickerProviderStateMixin, but at the time '
'dispose() was called on the mixin, that Ticker was still active. The Ticker must '
'be disposed before calling super.dispose().'
,
),
ErrorHint
(
'Tickers used by AnimationControllers '
'should be disposed by calling dispose() on the AnimationController itself. '
'Otherwise, the ticker will leak.'
,
),
_ticker
!.
describeForError
(
'The offending ticker was'
),
]);
}());
super
.
onClose
();
}
}
@Deprecated
(
'use GetSingleTickerProviderStateMixin'
)
/// Used like `SingleTickerProviderMixin` but only with Get Controllers.
/// Simplifies AnimationController creation inside GetxController.
///
/// Example:
///```
///class SplashController extends GetxController with
/// SingleGetTickerProviderMixin {
/// AnimationController _ac;
///
...
...
test/navigation/get_main_test.dart
View file @
8d0552e
...
...
@@ -481,58 +481,6 @@ void main() {
expect
(
find
.
byType
(
FirstScreen
),
findsOneWidget
);
});
testWidgets
(
"Get.snackbar test"
,
(
tester
)
async
{
await
tester
.
pumpWidget
(
GetMaterialApp
(
popGesture:
true
,
home:
ElevatedButton
(
child:
Text
(
'Open Snackbar'
),
onPressed:
()
{
Get
.
snackbar
(
'title'
,
"message"
,
duration:
Duration
(
seconds:
1
));
},
),
),
);
expect
(
Get
.
isSnackbarOpen
,
false
);
await
tester
.
tap
(
find
.
text
(
'Open Snackbar'
));
expect
(
Get
.
isSnackbarOpen
,
true
);
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
});
testWidgets
(
"Get.rawSnackbar test"
,
(
tester
)
async
{
await
tester
.
pumpWidget
(
Wrapper
(
child:
ElevatedButton
(
child:
Text
(
'Open Snackbar'
),
onPressed:
()
{
Get
.
rawSnackbar
(
title:
'title'
,
message:
"message"
,
onTap:
(
_
)
{
print
(
'snackbar tapped'
);
},
duration:
Duration
(
seconds:
1
),
shouldIconPulse:
true
,
icon:
Icon
(
Icons
.
alarm
),
showProgressIndicator:
true
,
barBlur:
null
,
isDismissible:
true
,
leftBarIndicatorColor:
Colors
.
amber
,
overlayBlur:
1.0
,
);
},
),
),
);
expect
(
Get
.
isSnackbarOpen
,
false
);
await
tester
.
tap
(
find
.
text
(
'Open Snackbar'
));
expect
(
Get
.
isSnackbarOpen
,
true
);
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
});
}
class
FirstScreen
extends
StatelessWidget
{
...
...
test/navigation/parse_route_test.dart
View file @
8d0552e
...
...
@@ -10,28 +10,49 @@ void main() {
name:
'/city'
,
page:
()
=>
Container
(),
children:
[
GetPage
(
name:
'/home'
,
page:
()
=>
Container
(),
children:
[
GetPage
(
name:
'/bed-room'
,
page:
()
=>
Container
()),
GetPage
(
name:
'/living-room'
,
page:
()
=>
Container
()),
]),
GetPage
(
name:
'/home'
,
page:
()
=>
Container
(),
transition:
Transition
.
rightToLeftWithFade
,
children:
[
GetPage
(
name:
'/bed-room'
,
transition:
Transition
.
size
,
page:
()
=>
Container
(),
),
GetPage
(
name:
'/living-room'
,
transition:
Transition
.
topLevel
,
page:
()
=>
Container
(),
),
],
),
GetPage
(
name:
'/work'
,
transition:
Transition
.
upToDown
,
page:
()
=>
Container
(),
children:
[
GetPage
(
name:
'/office'
,
transition:
Transition
.
zoom
,
page:
()
=>
Container
(),
children:
[
GetPage
(
name:
'/pen'
,
transition:
Transition
.
cupertino
,
page:
()
=>
Container
(),
parameters:
testParams
,
),
GetPage
(
name:
'/paper'
,
page:
()
=>
Container
()),
GetPage
(
name:
'/paper'
,
page:
()
=>
Container
(),
transition:
Transition
.
downToUp
,
),
],
),
GetPage
(
name:
'/meeting-room'
,
transition:
Transition
.
fade
,
page:
()
=>
Container
(),
),
],
...
...
@@ -56,15 +77,42 @@ void main() {
test
(
'Parse Page without children'
,
()
{
final
pageTree
=
[
GetPage
(
name:
'/city'
,
page:
()
=>
Container
()),
GetPage
(
name:
'/city/home'
,
page:
()
=>
Container
()),
GetPage
(
name:
'/city/home/bed-room'
,
page:
()
=>
Container
()),
GetPage
(
name:
'/city/home/living-room'
,
page:
()
=>
Container
()),
GetPage
(
name:
'/city/work'
,
page:
()
=>
Container
()),
GetPage
(
name:
'/city/work/office'
,
page:
()
=>
Container
()),
GetPage
(
name:
'/city/work/office/pen'
,
page:
()
=>
Container
()),
GetPage
(
name:
'/city/work/office/paper'
,
page:
()
=>
Container
()),
GetPage
(
name:
'/city/work/meeting-room'
,
page:
()
=>
Container
()),
GetPage
(
name:
'/city'
,
page:
()
=>
Container
(),
transition:
Transition
.
cupertino
),
GetPage
(
name:
'/city/home'
,
page:
()
=>
Container
(),
transition:
Transition
.
downToUp
),
GetPage
(
name:
'/city/home/bed-room'
,
page:
()
=>
Container
(),
transition:
Transition
.
fade
),
GetPage
(
name:
'/city/home/living-room'
,
page:
()
=>
Container
(),
transition:
Transition
.
fadeIn
),
GetPage
(
name:
'/city/work'
,
page:
()
=>
Container
(),
transition:
Transition
.
leftToRight
),
GetPage
(
name:
'/city/work/office'
,
page:
()
=>
Container
(),
transition:
Transition
.
leftToRightWithFade
),
GetPage
(
name:
'/city/work/office/pen'
,
page:
()
=>
Container
(),
transition:
Transition
.
native
),
GetPage
(
name:
'/city/work/office/paper'
,
page:
()
=>
Container
(),
transition:
Transition
.
noTransition
),
GetPage
(
name:
'/city/work/meeting-room'
,
page:
()
=>
Container
(),
transition:
Transition
.
rightToLeft
),
];
final
tree
=
ParseRouteTree
(
routes:
pageTree
);
...
...
test/navigation/routes_test.dart
View file @
8d0552e
// import 'package:flutter/material.dart';
// import 'package:flutter_test/flutter_test.dart';
// import 'package:get/get.dart';
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:get/get.dart'
;
void
main
(
)
{
// testWidgets(
// 'GetPage page null',
// (tester) async {
// expect(() => GetPage(page: null, name: null), throwsAssertionError);
// },
// );
// testWidgets(
// "GetPage maintainState null",
// (tester) async {
// expect(
// () => GetPage(page: () => Scaffold(), maintainState: null, name: '/'),
// throwsAssertionError,
// );
// },
// );
// testWidgets(
// "GetPage name null",
// (tester) async {
// expect(
// () => GetPage(page: () => Scaffold(),
// maintainState: null, name: null),
// throwsAssertionError,
// );
// },
// );
// testWidgets(
// "GetPage fullscreenDialog null",
// (tester) async {
// expect(
// () =>
// GetPage(page: () => Scaffold(), fullscreenDialog: null, name: '/'),
// throwsAssertionError,
// );
// },
// );
testWidgets
(
'Back swipe dismiss interrupted by route push'
,
(
tester
)
async
{
// final scaffoldKey = GlobalKey();
await
tester
.
pumpWidget
(
GetCupertinoApp
(
popGesture:
true
,
home:
CupertinoPageScaffold
(
// key: scaffoldKey,
child:
Center
(
child:
CupertinoButton
(
onPressed:
()
{
Get
.
to
(()
=>
CupertinoPageScaffold
(
child:
Center
(
child:
Text
(
'route'
)),
));
},
child:
const
Text
(
'push'
),
),
),
),
),
);
// Check the basic iOS back-swipe dismiss transition. Dragging the pushed
// route halfway across the screen will trigger the iOS dismiss animation
await
tester
.
tap
(
find
.
text
(
'push'
));
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'route'
),
findsOneWidget
);
expect
(
find
.
text
(
'push'
),
findsNothing
);
var
gesture
=
await
tester
.
startGesture
(
const
Offset
(
5
,
300
));
await
gesture
.
moveBy
(
const
Offset
(
400
,
0
));
await
gesture
.
up
();
await
tester
.
pump
();
expect
(
// The 'route' route has been dragged to the right, halfway across the screen
tester
.
getTopLeft
(
find
.
ancestor
(
of:
find
.
text
(
'route'
),
matching:
find
.
byType
(
CupertinoPageScaffold
))),
const
Offset
(
400
,
0
),
);
expect
(
// The 'push' route is sliding in from the left.
tester
.
getTopLeft
(
find
.
ancestor
(
of:
find
.
text
(
'push'
),
matching:
find
.
byType
(
CupertinoPageScaffold
)))
.
dx
,
0
,
);
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'push'
),
findsOneWidget
);
expect
(
tester
.
getTopLeft
(
find
.
ancestor
(
of:
find
.
text
(
'push'
),
matching:
find
.
byType
(
CupertinoPageScaffold
))),
Offset
.
zero
,
);
expect
(
find
.
text
(
'route'
),
findsNothing
);
// Run the dismiss animation 60%, which exposes the route "push" button,
// and then press the button.
await
tester
.
tap
(
find
.
text
(
'push'
));
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'route'
),
findsOneWidget
);
expect
(
find
.
text
(
'push'
),
findsNothing
);
gesture
=
await
tester
.
startGesture
(
const
Offset
(
5
,
300
));
await
gesture
.
moveBy
(
const
Offset
(
400
,
0
));
// Drag halfway.
await
gesture
.
up
();
// Trigger the snapping animation.
// Since the back swipe drag was brought to >=50% of the screen, it will
// self snap to finish the pop transition as the gesture is lifted.
//
// This drag drop animation is 400ms when dropped exactly halfway
// (800 / [pixel distance remaining], see
// _CupertinoBackGestureController.dragEnd). It follows a curve that is very
// steep initially.
await
tester
.
pump
();
expect
(
tester
.
getTopLeft
(
find
.
ancestor
(
of:
find
.
text
(
'route'
),
matching:
find
.
byType
(
CupertinoPageScaffold
))),
const
Offset
(
400
,
0
),
);
// Let the dismissing snapping animation go 60%.
await
tester
.
pump
(
const
Duration
(
milliseconds:
240
));
expect
(
tester
.
getTopLeft
(
find
.
ancestor
(
of:
find
.
text
(
'route'
),
matching:
find
.
byType
(
CupertinoPageScaffold
)))
.
dx
,
moreOrLessEquals
(
798
,
epsilon:
1
),
);
// Use the navigator to push a route instead of tapping the 'push' button.
// The topmost route (the one that's animating away), ignores input while
// the pop is underway because route.navigator.userGestureInProgress.
Get
.
to
(()
=>
const
CupertinoPageScaffold
(
child:
Center
(
child:
Text
(
'route'
)),
));
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'route'
),
findsOneWidget
);
expect
(
find
.
text
(
'push'
),
findsNothing
);
expect
(
tester
.
state
<
NavigatorState
>(
find
.
byType
(
Navigator
))
.
userGestureInProgress
,
false
,
);
});
}
...
...
test/navigation/snackbar_test.dart
0 → 100644
View file @
8d0552e
import
'package:flutter/material.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:get/get.dart'
;
void
main
(
)
{
testWidgets
(
"test if Get.isSnackbarOpen works with Get.snackbar"
,
(
tester
)
async
{
await
tester
.
pumpWidget
(
GetMaterialApp
(
popGesture:
true
,
home:
ElevatedButton
(
child:
Text
(
'Open Snackbar'
),
onPressed:
()
{
Get
.
snackbar
(
'title'
,
"message"
,
duration:
Duration
(
seconds:
1
),
isDismissible:
false
,
);
},
),
),
);
expect
(
Get
.
isSnackbarOpen
,
false
);
await
tester
.
tap
(
find
.
text
(
'Open Snackbar'
));
expect
(
Get
.
isSnackbarOpen
,
true
);
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
Get
.
isSnackbarOpen
,
false
);
});
testWidgets
(
"Get.rawSnackbar test"
,
(
tester
)
async
{
await
tester
.
pumpWidget
(
GetMaterialApp
(
popGesture:
true
,
home:
ElevatedButton
(
child:
Text
(
'Open Snackbar'
),
onPressed:
()
{
Get
.
rawSnackbar
(
title:
'title'
,
message:
"message"
,
onTap:
(
_
)
{
print
(
'snackbar tapped'
);
},
shouldIconPulse:
true
,
icon:
Icon
(
Icons
.
alarm
),
showProgressIndicator:
true
,
duration:
Duration
(
seconds:
1
),
isDismissible:
true
,
leftBarIndicatorColor:
Colors
.
amber
,
overlayBlur:
1.0
,
);
},
),
),
);
expect
(
Get
.
isSnackbarOpen
,
false
);
await
tester
.
tap
(
find
.
text
(
'Open Snackbar'
));
expect
(
Get
.
isSnackbarOpen
,
true
);
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
Get
.
isSnackbarOpen
,
false
);
});
testWidgets
(
"test snackbar queue"
,
(
tester
)
async
{
final
messageOne
=
Text
(
'title'
);
final
messageTwo
=
Text
(
'titleTwo'
);
await
tester
.
pumpWidget
(
GetMaterialApp
(
popGesture:
true
,
home:
ElevatedButton
(
child:
Text
(
'Open Snackbar'
),
onPressed:
()
{
Get
.
rawSnackbar
(
messageText:
messageOne
,
duration:
Duration
(
seconds:
1
));
Get
.
rawSnackbar
(
messageText:
messageTwo
,
duration:
Duration
(
seconds:
1
));
},
),
),
);
expect
(
Get
.
isSnackbarOpen
,
false
);
await
tester
.
tap
(
find
.
text
(
'Open Snackbar'
));
expect
(
Get
.
isSnackbarOpen
,
true
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
find
.
text
(
'title'
),
findsOneWidget
);
expect
(
find
.
text
(
'titleTwo'
),
findsNothing
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
find
.
text
(
'title'
),
findsNothing
);
expect
(
find
.
text
(
'titleTwo'
),
findsOneWidget
);
Get
.
closeAllSnackbars
();
});
testWidgets
(
"test snackbar dismissible"
,
(
tester
)
async
{
const
dismissDirection
=
DismissDirection
.
vertical
;
const
snackBarTapTarget
=
Key
(
'snackbar-tap-target'
);
late
final
GetSnackBar
getBar
;
await
tester
.
pumpWidget
(
GetMaterialApp
(
home:
Scaffold
(
body:
Builder
(
builder:
(
context
)
{
return
Column
(
children:
<
Widget
>[
GestureDetector
(
key:
snackBarTapTarget
,
onTap:
()
{
getBar
=
GetSnackBar
(
message:
'bar1'
,
duration:
const
Duration
(
seconds:
2
),
isDismissible:
true
,
dismissDirection:
dismissDirection
,
);
Get
.
showSnackbar
(
getBar
);
},
behavior:
HitTestBehavior
.
opaque
,
child:
const
SizedBox
(
height:
100.0
,
width:
100.0
,
),
),
],
);
},
),
),
));
expect
(
Get
.
isSnackbarOpen
,
false
);
expect
(
find
.
text
(
'bar1'
),
findsNothing
);
await
tester
.
tap
(
find
.
byKey
(
snackBarTapTarget
));
await
tester
.
pumpAndSettle
();
expect
(
Get
.
isSnackbarOpen
,
true
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
find
.
byWidget
(
getBar
),
findsOneWidget
);
await
tester
.
ensureVisible
(
find
.
byWidget
(
getBar
));
await
tester
.
drag
(
find
.
byWidget
(
getBar
),
Offset
(
0.0
,
50.0
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
Get
.
isSnackbarOpen
,
false
);
});
testWidgets
(
"test snackbar onTap"
,
(
tester
)
async
{
const
dismissDirection
=
DismissDirection
.
vertical
;
const
snackBarTapTarget
=
Key
(
'snackbar-tap-target'
);
var
counter
=
0
;
late
final
GetSnackBar
getBar
;
late
final
SnackbarController
getBarController
;
await
tester
.
pumpWidget
(
GetMaterialApp
(
home:
Scaffold
(
body:
Builder
(
builder:
(
context
)
{
return
Column
(
children:
<
Widget
>[
GestureDetector
(
key:
snackBarTapTarget
,
onTap:
()
{
getBar
=
GetSnackBar
(
message:
'bar1'
,
onTap:
(
_
)
{
counter
++;
},
duration:
const
Duration
(
seconds:
2
),
isDismissible:
true
,
dismissDirection:
dismissDirection
,
);
getBarController
=
Get
.
showSnackbar
(
getBar
);
},
behavior:
HitTestBehavior
.
opaque
,
child:
const
SizedBox
(
height:
100.0
,
width:
100.0
,
),
),
],
);
},
),
),
));
await
tester
.
pumpAndSettle
();
expect
(
Get
.
isSnackbarOpen
,
false
);
expect
(
find
.
text
(
'bar1'
),
findsNothing
);
await
tester
.
tap
(
find
.
byKey
(
snackBarTapTarget
));
await
tester
.
pumpAndSettle
();
expect
(
Get
.
isSnackbarOpen
,
true
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
find
.
byWidget
(
getBar
),
findsOneWidget
);
await
tester
.
ensureVisible
(
find
.
byWidget
(
getBar
));
await
tester
.
tap
(
find
.
byWidget
(
getBar
));
expect
(
counter
,
1
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
3000
));
await
getBarController
.
close
(
withAnimations:
false
);
});
}
...
...
Please
register
or
login
to post a comment