Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
d8da0917
Unverified
Commit
d8da0917
authored
Aug 11, 2021
by
Kate Lovett
Committed by
GitHub
Aug 11, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Android 12 overscroll stretch effect (#87839)
parent
8d89632f
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
689 additions
and
17 deletions
+689
-17
fix_data.yaml
packages/flutter/lib/fix_data.yaml
+11
-0
app.dart
packages/flutter/lib/src/material/app.dart
+13
-1
notification_listener.dart
packages/flutter/lib/src/widgets/notification_listener.dart
+1
-1
overscroll_indicator.dart
packages/flutter/lib/src/widgets/overscroll_indicator.dart
+301
-14
scroll_configuration.dart
packages/flutter/lib/src/widgets/scroll_configuration.dart
+38
-1
app_test.dart
packages/flutter/test/material/app_test.dart
+36
-0
overscroll_stretch_indicator_test.dart
...utter/test/widgets/overscroll_stretch_indicator_test.dart
+223
-0
scroll_behavior_test.dart
packages/flutter/test/widgets/scroll_behavior_test.dart
+42
-0
cupertino.dart
packages/flutter/test_fixes/cupertino.dart
+4
-0
cupertino.dart.expect
packages/flutter/test_fixes/cupertino.dart.expect
+4
-0
material.dart
packages/flutter/test_fixes/material.dart
+4
-0
material.dart.expect
packages/flutter/test_fixes/material.dart.expect
+4
-0
widgets.dart
packages/flutter/test_fixes/widgets.dart
+4
-0
widgets.dart.expect
packages/flutter/test_fixes/widgets.dart.expect
+4
-0
No files found.
packages/flutter/lib/fix_data.yaml
View file @
d8da0917
...
...
@@ -15,6 +15,17 @@
version
:
1
transforms
:
# Changes made in https://github.com/flutter/flutter/pull/87839
-
title
:
"
Migrate
to
'disallowIndicator'"
date
:
2021-08-06
element
:
uris
:
[
'
material.dart'
,
'
widgets.dart'
,
'
cupertino.dart'
]
method
:
'
disallowGlow'
inClass
:
'
OverscrollIndicatorNotification'
changes
:
-
kind
:
'
rename'
newName
:
'
disallowIndicator'
# Changes made in https://github.com/flutter/flutter/pull/87281
-
title
:
"
Remove
'fixTextFieldOutlineLabel'"
date
:
2021-04-30
...
...
packages/flutter/lib/src/material/app.dart
View file @
d8da0917
...
...
@@ -703,7 +703,9 @@ class MaterialScrollBehavior extends ScrollBehavior {
/// Creates a MaterialScrollBehavior that decorates [Scrollable]s with
/// [GlowingOverscrollIndicator]s and [Scrollbar]s based on the current
/// platform and provided [ScrollableDetails].
const
MaterialScrollBehavior
();
const
MaterialScrollBehavior
({
AndroidOverscrollIndicator
?
androidOverscrollIndicator
,
})
:
super
(
androidOverscrollIndicator:
androidOverscrollIndicator
);
@override
TargetPlatform
getPlatform
(
BuildContext
context
)
=>
Theme
.
of
(
context
).
platform
;
...
...
@@ -743,6 +745,16 @@ class MaterialScrollBehavior extends ScrollBehavior {
case
TargetPlatform
.
windows
:
return
child
;
case
TargetPlatform
.
android
:
switch
(
androidOverscrollIndicator
)
{
case
AndroidOverscrollIndicator
.
stretch
:
return
StretchingOverscrollIndicator
(
axisDirection:
details
.
direction
,
child:
child
,
);
case
AndroidOverscrollIndicator
.
glow
:
continue
glow
;
}
glow:
case
TargetPlatform
.
fuchsia
:
return
GlowingOverscrollIndicator
(
axisDirection:
details
.
direction
,
...
...
packages/flutter/lib/src/widgets/notification_listener.dart
View file @
d8da0917
...
...
@@ -185,7 +185,7 @@ class NotificationListener<T extends Notification> extends StatelessWidget {
/// Called when a notification of the appropriate type arrives at this
/// location in the tree.
///
/// Return true to cancel the notification bubbling. Return false
(or null)
to
/// Return true to cancel the notification bubbling. Return false to
/// allow the notification to continue to be dispatched to further ancestors.
///
/// The notification's [Notification.visitAncestor] method is called for each
...
...
packages/flutter/lib/src/widgets/overscroll_indicator.dart
View file @
d8da0917
...
...
@@ -6,7 +6,7 @@ import 'dart:async' show Timer;
import
'dart:math'
as
math
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/physics.dart'
;
import
'package:flutter/physics.dart'
show
nearEqual
,
Tolerance
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/scheduler.dart'
;
...
...
@@ -15,6 +15,7 @@ import 'framework.dart';
import
'notification_listener.dart'
;
import
'scroll_notification.dart'
;
import
'ticker_provider.dart'
;
import
'transitions.dart'
;
/// A visual indication that a scroll view has overscrolled.
///
...
...
@@ -116,9 +117,10 @@ import 'ticker_provider.dart';
/// See also:
///
/// * [OverscrollIndicatorNotification], which can be used to manipulate the
/// glow position or prevent the glow from being painted at all
/// glow position or prevent the glow from being painted at all
.
/// * [NotificationListener], to listen for the
/// [OverscrollIndicatorNotification]
/// [OverscrollIndicatorNotification].
/// * [StretchingOverscrollIndicator], a Material Design overscroll indicator.
class
GlowingOverscrollIndicator
extends
StatefulWidget
{
/// Creates a visual indication that a scroll view has overscrolled.
///
...
...
@@ -165,22 +167,28 @@ class GlowingOverscrollIndicator extends StatefulWidget {
/// viewport.
final
bool
showTrailing
;
/// {@template flutter.overscroll.axisDirection}
/// The direction of positive scroll offsets in the [Scrollable] whose
/// overscrolls are to be visualized.
/// {@endtemplate}
final
AxisDirection
axisDirection
;
/// {@template flutter.overscroll.axis}
/// The axis along which scrolling occurs in the [Scrollable] whose
/// overscrolls are to be visualized.
/// {@endtemplate}
Axis
get
axis
=>
axisDirectionToAxis
(
axisDirection
);
/// The color of the glow. The alpha channel is ignored.
final
Color
color
;
/// {@template flutter.overscroll.notificationPredicate}
/// A check that specifies whether a [ScrollNotification] should be
/// handled by this widget.
///
/// By default, checks whether `notification.depth == 0`. Set it to something
/// else for more complicated layouts.
/// else for more complicated layouts, such as nested [ScrollView]s.
/// {@endtemplate}
final
ScrollNotificationPredicate
notificationPredicate
;
/// The widget below this widget in the tree.
...
...
@@ -271,7 +279,7 @@ class _GlowingOverscrollIndicatorState extends State<GlowingOverscrollIndicator>
assert
(
false
);
}
final
bool
isLeading
=
controller
==
_leadingController
;
if
(
_lastNotificationType
!=
OverscrollNotification
)
{
if
(
_lastNotificationType
is
!
OverscrollNotification
)
{
final
OverscrollIndicatorNotification
confirmationNotification
=
OverscrollIndicatorNotification
(
leading:
isLeading
);
confirmationNotification
.
dispatch
(
context
);
_accepted
[
isLeading
]
=
confirmationNotification
.
_accepted
;
...
...
@@ -637,18 +645,285 @@ class _GlowingOverscrollIndicatorPainter extends CustomPainter {
}
}
/// A notification that an [GlowingOverscrollIndicator] will start showing an
/// overscroll indication.
/// A Material Design visual indication that a scroll view has overscrolled.
///
/// To prevent the indicator from showing the indication, call [disallowGlow] on
/// the notification.
/// A [StretchingOverscrollIndicator] listens for [ScrollNotification]s in order
/// to stretch the content of the [Scrollable]. These notifications are typically
/// generated by a [ScrollView], such as a [ListView] or a [GridView].
///
/// When triggered, the [StretchingOverscrollIndicator] generates an
/// [OverscrollIndicatorNotification] before showing an overscroll indication.
/// To prevent the indicator from showing the indication, call
/// [OverscrollIndicatorNotification.disallowIndicator] on the notification.
///
/// Created by [ScrollBehavior.buildOverscrollIndicator] on platforms
/// (e.g., Android) that commonly use this type of overscroll indication when
/// [ScrollBehavior.androidOverscrollIndicator] is
/// [AndroidOverscrollIndicator.stretch]. Otherwise, the default
/// [GlowingOverscrollIndicator] is applied.
///
/// See also:
///
/// * [GlowingOverscrollIndicator], which generates this type of notification.
/// * [OverscrollIndicatorNotification], which can be used to prevent the stretch
/// effect from being applied at all.
/// * [NotificationListener], to listen for the
/// [OverscrollIndicatorNotification].
/// * [GlowingOverscrollIndicator], the default overscroll indicator for
/// [TargetPlatform.android] and [TargetPlatform.fuchsia].
class
StretchingOverscrollIndicator
extends
StatefulWidget
{
/// Creates a visual indication that a scroll view has overscrolled by
/// applying a stretch transformation to the content.
///
/// In order for this widget to display an overscroll indication, the [child]
/// widget must contain a widget that generates a [ScrollNotification], such
/// as a [ListView] or a [GridView].
///
/// The [axisDirection] and [notificationPredicate] arguments must not be null.
const
StretchingOverscrollIndicator
({
Key
?
key
,
required
this
.
axisDirection
,
this
.
notificationPredicate
=
defaultScrollNotificationPredicate
,
this
.
child
,
})
:
assert
(
axisDirection
!=
null
),
assert
(
notificationPredicate
!=
null
),
super
(
key:
key
);
/// {@macro flutter.overscroll.axisDirection}
final
AxisDirection
axisDirection
;
/// {@macro flutter.overscroll.axis}
Axis
get
axis
=>
axisDirectionToAxis
(
axisDirection
);
/// {@macro flutter.overscroll.notificationPredicate}
final
ScrollNotificationPredicate
notificationPredicate
;
/// The widget below this widget in the tree.
///
/// The overscroll indicator will apply a stretch effect to this child. This
/// child (and its subtree) should include a source of [ScrollNotification]
/// notifications.
///
/// Typically a [StretchingOverscrollIndicator] is created by a
/// [ScrollBehavior.buildOverscrollIndicator] method when opted-in using the
/// [ScrollBehavior.androidOverscrollIndicator] flag. In this case
/// the child is usually the one provided as an argument to that method.
final
Widget
?
child
;
@override
State
<
StretchingOverscrollIndicator
>
createState
()
=>
_StretchingOverscrollIndicatorState
();
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
super
.
debugFillProperties
(
properties
);
properties
.
add
(
EnumProperty
<
AxisDirection
>(
'axisDirection'
,
axisDirection
));
}
}
class
_StretchingOverscrollIndicatorState
extends
State
<
StretchingOverscrollIndicator
>
with
TickerProviderStateMixin
{
late
final
_StretchController
_stretchController
=
_StretchController
(
vsync:
this
);
ScrollNotification
?
_lastNotification
;
OverscrollNotification
?
_lastOverscrollNotification
;
bool
_accepted
=
true
;
bool
_handleScrollNotification
(
ScrollNotification
notification
)
{
if
(!
widget
.
notificationPredicate
(
notification
))
return
false
;
if
(
notification
is
OverscrollNotification
)
{
_lastOverscrollNotification
=
notification
;
if
(
_lastNotification
.
runtimeType
is
!
OverscrollNotification
)
{
final
OverscrollIndicatorNotification
confirmationNotification
=
OverscrollIndicatorNotification
(
leading:
notification
.
overscroll
<
0.0
);
confirmationNotification
.
dispatch
(
context
);
_accepted
=
confirmationNotification
.
_accepted
;
}
assert
(
notification
.
metrics
.
axis
==
widget
.
axis
);
if
(
_accepted
)
{
if
(
notification
.
velocity
!=
0.0
)
{
assert
(
notification
.
dragDetails
==
null
);
_stretchController
.
absorbImpact
(
notification
.
velocity
.
abs
());
}
else
{
assert
(
notification
.
overscroll
!=
0.0
);
if
(
notification
.
dragDetails
!=
null
)
{
_stretchController
.
pull
(
notification
.
overscroll
.
abs
()
/
notification
.
metrics
.
viewportDimension
);
}
}
}
}
else
if
(
notification
is
ScrollEndNotification
&&
notification
.
dragDetails
!=
null
||
notification
is
ScrollUpdateNotification
&&
notification
.
dragDetails
!=
null
)
{
_stretchController
.
scrollEnd
();
}
_lastNotification
=
notification
;
return
false
;
}
@override
void
dispose
()
{
_stretchController
.
dispose
();
super
.
dispose
();
}
@override
Widget
build
(
BuildContext
context
)
{
return
NotificationListener
<
ScrollNotification
>(
onNotification:
_handleScrollNotification
,
child:
AnimatedBuilder
(
animation:
_stretchController
,
builder:
(
BuildContext
context
,
Widget
?
child
)
{
final
double
stretch
=
_stretchController
.
value
;
double
x
=
1.0
;
double
y
=
1.0
;
final
AlignmentDirectional
alignment
;
switch
(
widget
.
axis
)
{
case
Axis
.
horizontal
:
x
+=
stretch
;
alignment
=
(
_lastOverscrollNotification
?.
overscroll
??
0
)
>
0
?
AlignmentDirectional
.
centerEnd
:
AlignmentDirectional
.
centerStart
;
break
;
case
Axis
.
vertical
:
y
+=
stretch
;
alignment
=
(
_lastOverscrollNotification
?.
overscroll
??
0
)
>
0
?
AlignmentDirectional
.
bottomCenter
:
AlignmentDirectional
.
topCenter
;
break
;
}
return
Transform
(
alignment:
alignment
,
transform:
Matrix4
.
diagonal3Values
(
x
,
y
,
1.0
),
child:
widget
.
child
,
);
},
),
);
}
}
enum
_StretchState
{
idle
,
absorb
,
pull
,
recede
,
}
class
_StretchController
extends
ChangeNotifier
{
_StretchController
({
required
TickerProvider
vsync
})
{
_stretchController
=
AnimationController
(
vsync:
vsync
)
..
addStatusListener
(
_changePhase
);
final
Animation
<
double
>
decelerator
=
CurvedAnimation
(
parent:
_stretchController
,
curve:
Curves
.
decelerate
,
)..
addListener
(
notifyListeners
);
_stretchSize
=
decelerator
.
drive
(
_stretchSizeTween
);
}
late
final
AnimationController
_stretchController
;
late
final
Animation
<
double
>
_stretchSize
;
final
Tween
<
double
>
_stretchSizeTween
=
Tween
<
double
>(
begin:
0.0
,
end:
0.0
);
_StretchState
_state
=
_StretchState
.
idle
;
double
_pullDistance
=
0.0
;
// Constants from Android.
static
const
double
_exponentialScalar
=
math
.
e
/
0.33
;
static
const
double
_stretchIntensity
=
0.016
;
static
const
double
_flingFriction
=
1.01
;
static
const
Duration
_stretchDuration
=
Duration
(
milliseconds:
400
);
double
get
value
=>
_stretchSize
.
value
;
/// Handle a fling to the edge of the viewport at a particular velocity.
///
/// The velocity must be positive.
void
absorbImpact
(
double
velocity
)
{
assert
(
velocity
>=
0.0
);
velocity
=
velocity
.
clamp
(
1
,
10000
);
_stretchSizeTween
.
begin
=
_stretchSize
.
value
;
_stretchSizeTween
.
end
=
math
.
min
(
_stretchIntensity
+
(
_flingFriction
/
velocity
),
1.0
);
_stretchController
.
duration
=
Duration
(
milliseconds:
(
velocity
*
0.02
).
round
());
_stretchController
.
forward
(
from:
0.0
);
_state
=
_StretchState
.
absorb
;
}
/// Handle a user-driven overscroll.
///
/// The `normalizedOverscroll` argument should be the absolute value of the
/// scroll distance in logical pixels, divided by the extent of the viewport
/// in the main axis.
void
pull
(
double
normalizedOverscroll
)
{
assert
(
normalizedOverscroll
>=
0.0
);
_pullDistance
=
normalizedOverscroll
+
_pullDistance
;
_stretchSizeTween
.
begin
=
_stretchSize
.
value
;
final
double
linearIntensity
=
_stretchIntensity
*
_pullDistance
;
final
double
exponentialIntensity
=
_stretchIntensity
*
(
1
-
math
.
exp
(-
_pullDistance
*
_exponentialScalar
));
_stretchSizeTween
.
end
=
linearIntensity
+
exponentialIntensity
;
_stretchController
.
duration
=
_stretchDuration
;
if
(
_state
!=
_StretchState
.
pull
)
{
_stretchController
.
forward
(
from:
0.0
);
_state
=
_StretchState
.
pull
;
}
else
{
if
(!
_stretchController
.
isAnimating
)
{
assert
(
_stretchController
.
value
==
1.0
);
notifyListeners
();
}
}
}
void
scrollEnd
()
{
if
(
_state
==
_StretchState
.
pull
)
_recede
(
_stretchDuration
);
}
void
_changePhase
(
AnimationStatus
status
)
{
if
(
status
!=
AnimationStatus
.
completed
)
return
;
switch
(
_state
)
{
case
_StretchState
.
absorb
:
_recede
(
_stretchDuration
);
break
;
case
_StretchState
.
recede
:
_state
=
_StretchState
.
idle
;
_pullDistance
=
0.0
;
break
;
case
_StretchState
.
pull
:
case
_StretchState
.
idle
:
break
;
}
}
void
_recede
(
Duration
duration
)
{
if
(
_state
==
_StretchState
.
recede
||
_state
==
_StretchState
.
idle
)
return
;
_stretchSizeTween
.
begin
=
_stretchSize
.
value
;
_stretchSizeTween
.
end
=
0.0
;
_stretchController
.
duration
=
duration
;
_stretchController
.
forward
(
from:
0.0
);
_state
=
_StretchState
.
recede
;
}
@override
void
dispose
()
{
_stretchController
.
dispose
();
super
.
dispose
();
}
}
/// A notification that either a [GlowingOverscrollIndicator] or a
/// [StretchingOverscrollIndicator] will start showing an overscroll indication.
///
/// To prevent the indicator from showing the indication, call
/// [disallowIndicator] on the notification.
///
/// See also:
///
/// * [GlowingOverscrollIndicator], which generates this type of notification
/// by painting an indicator over the child content.
/// * [StretchingOverscrollIndicator], which generates this type of
/// notification by applying a stretch transformation to the child content.
class
OverscrollIndicatorNotification
extends
Notification
with
ViewportNotificationMixin
{
/// Creates a notification that an [GlowingOverscrollIndicator]
will start
/// showing an overscroll indication.
/// Creates a notification that an [GlowingOverscrollIndicator]
or a
///
[StretchingOverscrollIndicator] will start
showing an overscroll indication.
///
/// The [leading] argument must not be null.
OverscrollIndicatorNotification
({
...
...
@@ -659,7 +934,7 @@ class OverscrollIndicatorNotification extends Notification with ViewportNotifica
/// view.
final
bool
leading
;
/// Controls at which offset
the glow should be drawn
.
/// Controls at which offset
a [GlowingOverscrollIndicator] draws
.
///
/// A positive offset will move the glow away from its edge,
/// i.e. for a vertical, [leading] indicator, a [paintOffset] of 100.0 will
...
...
@@ -669,15 +944,27 @@ class OverscrollIndicatorNotification extends Notification with ViewportNotifica
///
/// A negative [paintOffset] is generally not useful, since the glow will be
/// clipped.
///
/// This has no effect on a [StretchingOverscrollIndicator].
double
paintOffset
=
0.0
;
bool
_accepted
=
true
;
/// Call this method if the glow should be prevented.
/// Call this method if the glow should be prevented. This method is
/// deprecated in favor of [disallowIndicator].
@Deprecated
(
'Use disallowIndicator instead. '
'This feature was deprecated after v2.5.0-6.0.pre.'
,
)
void
disallowGlow
()
{
_accepted
=
false
;
}
/// Call this method if the overscroll indicator should be prevented.
void
disallowIndicator
()
{
_accepted
=
false
;
}
@override
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
...
...
packages/flutter/lib/src/widgets/scroll_configuration.dart
View file @
d8da0917
...
...
@@ -21,6 +21,21 @@ const Set<PointerDeviceKind> _kTouchLikeDeviceTypes = <PointerDeviceKind>{
PointerDeviceKind
.
invertedStylus
,
};
/// The default overscroll indicator applied on [TargetPlatform.android].
// TODO(Piinks): Complete migration to stretch by default.
const
AndroidOverscrollIndicator
_kDefaultAndroidOverscrollIndicator
=
AndroidOverscrollIndicator
.
glow
;
/// Types of overscroll indicators supported by [TargetPlatform.android].
enum
AndroidOverscrollIndicator
{
/// Utilizes a [StretchingOverscrollIndicator], which transforms the contents
/// of a [ScrollView] when overscrolled.
stretch
,
/// Utilizes a [GlowingOverscrollIndicator], painting a glowing semi circle on
/// top of the [ScrollView] in response to oversfcrolling.
glow
,
}
/// Describes how [Scrollable] widgets should behave.
///
/// {@template flutter.widgets.scrollBehavior}
...
...
@@ -46,7 +61,13 @@ const Set<PointerDeviceKind> _kTouchLikeDeviceTypes = <PointerDeviceKind>{
@immutable
class
ScrollBehavior
{
/// Creates a description of how [Scrollable] widgets should behave.
const
ScrollBehavior
();
const
ScrollBehavior
({
AndroidOverscrollIndicator
?
androidOverscrollIndicator
,
}):
_androidOverscrollIndicator
=
androidOverscrollIndicator
;
/// Specifies which overscroll indicatpr to use on [TargetPlatform.android].
AndroidOverscrollIndicator
get
androidOverscrollIndicator
=>
_androidOverscrollIndicator
??
_kDefaultAndroidOverscrollIndicator
;
final
AndroidOverscrollIndicator
?
_androidOverscrollIndicator
;
/// Creates a copy of this ScrollBehavior, making it possible to
/// easily toggle `scrollbar` and `overscrollIndicator` effects.
...
...
@@ -106,6 +127,16 @@ class ScrollBehavior {
case
TargetPlatform
.
windows
:
return
child
;
case
TargetPlatform
.
android
:
switch
(
androidOverscrollIndicator
)
{
case
AndroidOverscrollIndicator
.
stretch
:
return
StretchingOverscrollIndicator
(
axisDirection:
axisDirection
,
child:
child
,
);
case
AndroidOverscrollIndicator
.
glow
:
continue
glow
;
}
glow:
case
TargetPlatform
.
fuchsia
:
return
GlowingOverscrollIndicator
(
axisDirection:
axisDirection
,
...
...
@@ -230,6 +261,11 @@ class _WrappedScrollBehavior implements ScrollBehavior {
@override
Set
<
PointerDeviceKind
>
get
dragDevices
=>
_dragDevices
??
delegate
.
dragDevices
;
@override
AndroidOverscrollIndicator
get
androidOverscrollIndicator
=>
delegate
.
androidOverscrollIndicator
;
@override
AndroidOverscrollIndicator
?
get
_androidOverscrollIndicator
=>
throw
UnimplementedError
();
@override
Widget
buildOverscrollIndicator
(
BuildContext
context
,
Widget
child
,
ScrollableDetails
details
)
{
if
(
overscrollIndicator
)
...
...
@@ -256,6 +292,7 @@ class _WrappedScrollBehavior implements ScrollBehavior {
ScrollPhysics
?
physics
,
TargetPlatform
?
platform
,
Set
<
PointerDeviceKind
>?
dragDevices
,
AndroidOverscrollIndicator
?
androidOverscrollIndicator
})
{
return
delegate
.
copyWith
(
scrollbars:
scrollbars
,
...
...
packages/flutter/test/material/app_test.dart
View file @
d8da0917
...
...
@@ -1070,6 +1070,42 @@ void main() {
expect
(
scrollBehavior
.
getScrollPhysics
(
capturedContext
).
runtimeType
,
NeverScrollableScrollPhysics
);
});
testWidgets
(
'ScrollBehavior default android overscroll indicator'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
MaterialApp
(
scrollBehavior:
const
MaterialScrollBehavior
(),
home:
ListView
(
children:
const
<
Widget
>[
SizedBox
(
height:
1000.0
,
width:
1000.0
,
child:
Text
(
'Test'
),
)
]
)
));
expect
(
find
.
byType
(
StretchingOverscrollIndicator
),
findsNothing
);
expect
(
find
.
byType
(
GlowingOverscrollIndicator
),
findsOneWidget
);
},
variant:
TargetPlatformVariant
.
only
(
TargetPlatform
.
android
));
testWidgets
(
'ScrollBehavior stretch android overscroll indicator'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
MaterialApp
(
scrollBehavior:
const
MaterialScrollBehavior
(
androidOverscrollIndicator:
AndroidOverscrollIndicator
.
stretch
),
home:
ListView
(
children:
const
<
Widget
>[
SizedBox
(
height:
1000.0
,
width:
1000.0
,
child:
Text
(
'Test'
),
)
]
)
));
expect
(
find
.
byType
(
StretchingOverscrollIndicator
),
findsOneWidget
);
expect
(
find
.
byType
(
GlowingOverscrollIndicator
),
findsNothing
);
},
variant:
TargetPlatformVariant
.
only
(
TargetPlatform
.
android
));
testWidgets
(
'When `useInheritedMediaQuery` is true an existing MediaQuery is used if one is available'
,
(
WidgetTester
tester
)
async
{
late
BuildContext
capturedContext
;
final
UniqueKey
uniqueKey
=
UniqueKey
();
...
...
packages/flutter/test/widgets/overscroll_stretch_indicator_test.dart
0 → 100644
View file @
d8da0917
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
Widget
buildTest
(
Key
box1Key
,
Key
box2Key
,
Key
box3Key
,
ScrollController
controller
,
{
Axis
?
axis
,
})
{
return
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
ScrollConfiguration
(
behavior:
const
ScrollBehavior
().
copyWith
(
overscroll:
false
),
child:
StretchingOverscrollIndicator
(
axisDirection:
axis
==
null
?
AxisDirection
.
down
:
AxisDirection
.
right
,
child:
CustomScrollView
(
scrollDirection:
axis
??
Axis
.
vertical
,
controller:
controller
,
slivers:
<
Widget
>[
SliverToBoxAdapter
(
child:
Container
(
color:
const
Color
(
0xD0FF0000
),
key:
box1Key
,
height:
250.0
,
width:
300.0
,
)),
SliverToBoxAdapter
(
child:
Container
(
color:
const
Color
(
0xFFFFFF00
),
key:
box2Key
,
height:
250.0
,
width:
300.0
,
)),
SliverToBoxAdapter
(
child:
Container
(
color:
const
Color
(
0xFF6200EA
),
key:
box3Key
,
height:
250.0
,
width:
300.0
,
)),
],
),
),
),
);
}
testWidgets
(
'Stretch overscroll vertically'
,
(
WidgetTester
tester
)
async
{
final
Key
box1Key
=
UniqueKey
();
final
Key
box2Key
=
UniqueKey
();
final
Key
box3Key
=
UniqueKey
();
final
ScrollController
controller
=
ScrollController
();
await
tester
.
pumpWidget
(
buildTest
(
box1Key
,
box2Key
,
box3Key
,
controller
),
);
expect
(
find
.
byType
(
StretchingOverscrollIndicator
),
findsOneWidget
);
expect
(
find
.
byType
(
GlowingOverscrollIndicator
),
findsNothing
);
final
RenderBox
box1
=
tester
.
renderObject
(
find
.
byKey
(
box1Key
));
final
RenderBox
box2
=
tester
.
renderObject
(
find
.
byKey
(
box2Key
));
final
RenderBox
box3
=
tester
.
renderObject
(
find
.
byKey
(
box3Key
));
expect
(
controller
.
offset
,
0.0
);
expect
(
box1
.
localToGlobal
(
Offset
.
zero
),
Offset
.
zero
);
expect
(
box2
.
localToGlobal
(
Offset
.
zero
),
const
Offset
(
0.0
,
250.0
));
expect
(
box3
.
localToGlobal
(
Offset
.
zero
),
const
Offset
(
0.0
,
500.0
));
await
expectLater
(
find
.
byType
(
CustomScrollView
),
matchesGoldenFile
(
'overscroll_stretch.vertical.start.png'
),
);
TestGesture
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byType
(
CustomScrollView
)));
// Overscroll the start
await
gesture
.
moveBy
(
const
Offset
(
0.0
,
200.0
));
await
tester
.
pumpAndSettle
();
expect
(
box1
.
localToGlobal
(
Offset
.
zero
),
Offset
.
zero
);
expect
(
box2
.
localToGlobal
(
Offset
.
zero
).
dy
,
greaterThan
(
255.0
));
expect
(
box3
.
localToGlobal
(
Offset
.
zero
).
dy
,
greaterThan
(
510.0
));
await
expectLater
(
find
.
byType
(
CustomScrollView
),
matchesGoldenFile
(
'overscroll_stretch.vertical.top.png'
),
);
await
gesture
.
up
();
await
tester
.
pumpAndSettle
();
// Stretch released back to the start
expect
(
box1
.
localToGlobal
(
Offset
.
zero
),
Offset
.
zero
);
expect
(
box2
.
localToGlobal
(
Offset
.
zero
),
const
Offset
(
0.0
,
250.0
));
expect
(
box3
.
localToGlobal
(
Offset
.
zero
),
const
Offset
(
0.0
,
500.0
));
// Jump to end of the list
controller
.
jumpTo
(
controller
.
position
.
maxScrollExtent
);
expect
(
controller
.
offset
,
150.0
);
await
expectLater
(
find
.
byType
(
CustomScrollView
),
matchesGoldenFile
(
'overscroll_stretch.vertical.end.png'
),
);
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byType
(
CustomScrollView
)));
// Overscroll the end
await
gesture
.
moveBy
(
const
Offset
(
0.0
,
-
200.0
));
await
tester
.
pumpAndSettle
();
expect
(
box1
.
localToGlobal
(
Offset
.
zero
).
dy
,
lessThan
(-
165
));
expect
(
box2
.
localToGlobal
(
Offset
.
zero
).
dy
,
lessThan
(
90.0
));
expect
(
box3
.
localToGlobal
(
Offset
.
zero
).
dy
,
lessThan
(
350.0
));
await
expectLater
(
find
.
byType
(
CustomScrollView
),
matchesGoldenFile
(
'overscroll_stretch.vertical.bottom.png'
),
);
});
testWidgets
(
'Stretch overscroll horizontally'
,
(
WidgetTester
tester
)
async
{
final
Key
box1Key
=
UniqueKey
();
final
Key
box2Key
=
UniqueKey
();
final
Key
box3Key
=
UniqueKey
();
final
ScrollController
controller
=
ScrollController
();
await
tester
.
pumpWidget
(
buildTest
(
box1Key
,
box2Key
,
box3Key
,
controller
,
axis:
Axis
.
horizontal
)
);
expect
(
find
.
byType
(
StretchingOverscrollIndicator
),
findsOneWidget
);
expect
(
find
.
byType
(
GlowingOverscrollIndicator
),
findsNothing
);
final
RenderBox
box1
=
tester
.
renderObject
(
find
.
byKey
(
box1Key
));
final
RenderBox
box2
=
tester
.
renderObject
(
find
.
byKey
(
box2Key
));
final
RenderBox
box3
=
tester
.
renderObject
(
find
.
byKey
(
box3Key
));
expect
(
controller
.
offset
,
0.0
);
expect
(
box1
.
localToGlobal
(
Offset
.
zero
),
Offset
.
zero
);
expect
(
box2
.
localToGlobal
(
Offset
.
zero
),
const
Offset
(
300.0
,
0.0
));
expect
(
box3
.
localToGlobal
(
Offset
.
zero
),
const
Offset
(
600.0
,
0.0
));
await
expectLater
(
find
.
byType
(
CustomScrollView
),
matchesGoldenFile
(
'overscroll_stretch.horizontal.start.png'
),
);
TestGesture
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byType
(
CustomScrollView
)));
// Overscroll the start
await
gesture
.
moveBy
(
const
Offset
(
200.0
,
0.0
));
await
tester
.
pumpAndSettle
();
expect
(
box1
.
localToGlobal
(
Offset
.
zero
),
Offset
.
zero
);
expect
(
box2
.
localToGlobal
(
Offset
.
zero
).
dx
,
greaterThan
(
305.0
));
expect
(
box3
.
localToGlobal
(
Offset
.
zero
).
dx
,
greaterThan
(
610.0
));
await
expectLater
(
find
.
byType
(
CustomScrollView
),
matchesGoldenFile
(
'overscroll_stretch.horizontal.left.png'
),
);
await
gesture
.
up
();
await
tester
.
pumpAndSettle
();
// Stretch released back to the start
expect
(
box1
.
localToGlobal
(
Offset
.
zero
),
Offset
.
zero
);
expect
(
box2
.
localToGlobal
(
Offset
.
zero
),
const
Offset
(
300.0
,
0.0
));
expect
(
box3
.
localToGlobal
(
Offset
.
zero
),
const
Offset
(
600.0
,
0.0
));
// Jump to end of the list
controller
.
jumpTo
(
controller
.
position
.
maxScrollExtent
);
expect
(
controller
.
offset
,
100.0
);
await
expectLater
(
find
.
byType
(
CustomScrollView
),
matchesGoldenFile
(
'overscroll_stretch.horizontal.end.png'
),
);
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byType
(
CustomScrollView
)));
// Overscroll the end
await
gesture
.
moveBy
(
const
Offset
(-
200.0
,
0.0
));
await
tester
.
pumpAndSettle
();
expect
(
box1
.
localToGlobal
(
Offset
.
zero
).
dx
,
lessThan
(-
116.0
));
expect
(
box2
.
localToGlobal
(
Offset
.
zero
).
dx
,
lessThan
(
190.0
));
expect
(
box3
.
localToGlobal
(
Offset
.
zero
).
dx
,
lessThan
(
500.0
));
await
expectLater
(
find
.
byType
(
CustomScrollView
),
matchesGoldenFile
(
'overscroll_stretch.horizontal.right.png'
),
);
});
testWidgets
(
'Disallow stretching overscroll'
,
(
WidgetTester
tester
)
async
{
final
Key
box1Key
=
UniqueKey
();
final
Key
box2Key
=
UniqueKey
();
final
Key
box3Key
=
UniqueKey
();
final
ScrollController
controller
=
ScrollController
();
double
indicatorNotification
=
0
;
await
tester
.
pumpWidget
(
NotificationListener
<
OverscrollIndicatorNotification
>(
onNotification:
(
OverscrollIndicatorNotification
notification
)
{
notification
.
disallowIndicator
();
indicatorNotification
+=
1
;
return
false
;
},
child:
buildTest
(
box1Key
,
box2Key
,
box3Key
,
controller
),
)
);
expect
(
find
.
byType
(
StretchingOverscrollIndicator
),
findsOneWidget
);
expect
(
find
.
byType
(
GlowingOverscrollIndicator
),
findsNothing
);
final
RenderBox
box1
=
tester
.
renderObject
(
find
.
byKey
(
box1Key
));
final
RenderBox
box2
=
tester
.
renderObject
(
find
.
byKey
(
box2Key
));
final
RenderBox
box3
=
tester
.
renderObject
(
find
.
byKey
(
box3Key
));
expect
(
indicatorNotification
,
0.0
);
expect
(
controller
.
offset
,
0.0
);
expect
(
box1
.
localToGlobal
(
Offset
.
zero
),
Offset
.
zero
);
expect
(
box2
.
localToGlobal
(
Offset
.
zero
),
const
Offset
(
0.0
,
250.0
));
expect
(
box3
.
localToGlobal
(
Offset
.
zero
),
const
Offset
(
0.0
,
500.0
));
final
TestGesture
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byType
(
CustomScrollView
)));
// Overscroll the start, should not stretch
await
gesture
.
moveBy
(
const
Offset
(
0.0
,
200.0
));
await
tester
.
pumpAndSettle
();
expect
(
indicatorNotification
,
1.0
);
expect
(
box1
.
localToGlobal
(
Offset
.
zero
),
Offset
.
zero
);
expect
(
box2
.
localToGlobal
(
Offset
.
zero
),
const
Offset
(
0.0
,
250.0
));
expect
(
box3
.
localToGlobal
(
Offset
.
zero
),
const
Offset
(
0.0
,
500.0
));
await
gesture
.
up
();
await
tester
.
pumpAndSettle
();
});
}
packages/flutter/test/widgets/scroll_behavior_test.dart
View file @
d8da0917
...
...
@@ -80,4 +80,46 @@ void main() {
expect
(
metrics
.
extentAfter
,
equals
(
400.0
));
expect
(
metrics
.
viewportDimension
,
equals
(
600.0
));
});
testWidgets
(
'ScrollBehavior default android overscroll indicator'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
ScrollConfiguration
(
behavior:
const
ScrollBehavior
(),
child:
ListView
(
children:
const
<
Widget
>[
SizedBox
(
height:
1000.0
,
width:
1000.0
,
child:
Text
(
'Test'
),
)
]
)
),
));
expect
(
find
.
byType
(
StretchingOverscrollIndicator
),
findsNothing
);
expect
(
find
.
byType
(
GlowingOverscrollIndicator
),
findsOneWidget
);
},
variant:
TargetPlatformVariant
.
only
(
TargetPlatform
.
android
));
testWidgets
(
'ScrollBehavior stretch android overscroll indicator'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
ScrollConfiguration
(
behavior:
const
ScrollBehavior
(
androidOverscrollIndicator:
AndroidOverscrollIndicator
.
stretch
),
child:
ListView
(
children:
const
<
Widget
>[
SizedBox
(
height:
1000.0
,
width:
1000.0
,
child:
Text
(
'Test'
),
)
]
)
),
));
expect
(
find
.
byType
(
StretchingOverscrollIndicator
),
findsOneWidget
);
expect
(
find
.
byType
(
GlowingOverscrollIndicator
),
findsNothing
);
},
variant:
TargetPlatformVariant
.
only
(
TargetPlatform
.
android
));
}
packages/flutter/test_fixes/cupertino.dart
View file @
d8da0917
...
...
@@ -181,4 +181,8 @@ void main() {
listWheelViewport
=
ListWheelViewport
(
clipToSize:
true
);
listWheelViewport
=
ListWheelViewport
(
clipToSize:
false
);
listWheelViewport
.
clipToSize
;
// Changes made in https://github.com/flutter/flutter/pull/87839
final
OverscrollIndicatorNotification
notification
=
OverscrollIndicatorNotification
(
leading:
true
);
notification
.
disallowGlow
();
}
packages/flutter/test_fixes/cupertino.dart.expect
View file @
d8da0917
...
...
@@ -181,4 +181,8 @@ void main() {
listWheelViewport = ListWheelViewport(clipBehavior: Clip.hardEdge);
listWheelViewport = ListWheelViewport(clipBehavior: Clip.none);
listWheelViewport.clipBehavior;
// Changes made in https://github.com/flutter/flutter/pull/87839
final OverscrollIndicatorNotification notification = OverscrollIndicatorNotification(leading: true);
notification.disallowIndicator();
}
packages/flutter/test_fixes/material.dart
View file @
d8da0917
...
...
@@ -394,4 +394,8 @@ void main() {
themeData
=
ThemeData
.
raw
(
fixTextFieldOutlineLabel:
true
);
themeData
=
themeData
.
copyWith
(
fixTextFieldOutlineLabel:
true
);
themeData
.
fixTextFieldOutlineLabel
;
// Removing field reference not supported.
// Changes made in https://github.com/flutter/flutter/pull/87839
final
OverscrollIndicatorNotification
notification
=
OverscrollIndicatorNotification
(
leading:
true
);
notification
.
disallowGlow
();
}
packages/flutter/test_fixes/material.dart.expect
View file @
d8da0917
...
...
@@ -366,4 +366,8 @@ void main() {
themeData = ThemeData.raw();
themeData = themeData.copyWith();
themeData.fixTextFieldOutlineLabel; // Removing field reference not supported.
// Changes made in https://github.com/flutter/flutter/pull/87839
final OverscrollIndicatorNotification notification = OverscrollIndicatorNotification(leading: true);
notification.disallowIndicator();
}
packages/flutter/test_fixes/widgets.dart
View file @
d8da0917
...
...
@@ -149,4 +149,8 @@ void main() {
listWheelViewport
=
ListWheelViewport
(
clipToSize:
true
);
listWheelViewport
=
ListWheelViewport
(
clipToSize:
false
);
listWheelViewport
.
clipToSize
;
// Changes made in https://github.com/flutter/flutter/pull/87839
final
OverscrollIndicatorNotification
notification
=
OverscrollIndicatorNotification
(
leading:
true
);
notification
.
disallowGlow
();
}
packages/flutter/test_fixes/widgets.dart.expect
View file @
d8da0917
...
...
@@ -149,4 +149,8 @@ void main() {
listWheelViewport = ListWheelViewport(clipBehavior: Clip.hardEdge);
listWheelViewport = ListWheelViewport(clipBehavior: Clip.none);
listWheelViewport.clipBehavior;
// Changes made in https://github.com/flutter/flutter/pull/87839
final OverscrollIndicatorNotification notification = OverscrollIndicatorNotification(leading: true);
notification.disallowIndicator();
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment