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
f4a0511e
Unverified
Commit
f4a0511e
authored
Nov 20, 2020
by
Kate Lovett
Committed by
GitHub
Nov 20, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Re-land 'Default Keyboard ScrollActions with PrimaryScrollController' (#70893)
parent
a76289bb
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
262 additions
and
142 deletions
+262
-142
all_elements_bench.dart
...ks/microbenchmarks/lib/foundation/all_elements_bench.dart
+1
-1
page_scaffold.dart
packages/flutter/lib/src/cupertino/page_scaffold.dart
+9
-6
scaffold.dart
packages/flutter/lib/src/material/scaffold.dart
+24
-29
primary_scroll_controller.dart
...es/flutter/lib/src/widgets/primary_scroll_controller.dart
+13
-0
routes.dart
packages/flutter/lib/src/widgets/routes.dart
+41
-35
scroll_view.dart
packages/flutter/lib/src/widgets/scroll_view.dart
+5
-0
scrollable.dart
packages/flutter/lib/src/widgets/scrollable.dart
+25
-2
shortcuts.dart
packages/flutter/lib/src/widgets/shortcuts.dart
+1
-1
single_child_scroll_view.dart
...ges/flutter/lib/src/widgets/single_child_scroll_view.dart
+5
-0
app_bar_test.dart
packages/flutter/test/material/app_bar_test.dart
+25
-36
debug_test.dart
packages/flutter/test/material/debug_test.dart
+1
-0
scaffold_test.dart
packages/flutter/test/material/scaffold_test.dart
+1
-2
scroll_view_test.dart
packages/flutter/test/widgets/scroll_view_test.dart
+45
-0
scrollable_test.dart
packages/flutter/test/widgets/scrollable_test.dart
+66
-30
No files found.
dev/benchmarks/microbenchmarks/lib/foundation/all_elements_bench.dart
View file @
f4a0511e
...
...
@@ -50,7 +50,7 @@ Future<void> main() async {
print
(
'flutter_test allElements benchmark... (
${WidgetsBinding.instance.renderViewElement}
)'
);
// Make sure we get enough elements to process for consistent benchmark runs
int
elementCount
=
collectAllElementsFrom
(
WidgetsBinding
.
instance
.
renderViewElement
,
skipOffstage:
false
).
length
;
while
(
elementCount
<
24
82
)
{
while
(
elementCount
<
24
58
)
{
await
Future
<
void
>.
delayed
(
Duration
.
zero
);
elementCount
=
collectAllElementsFrom
(
WidgetsBinding
.
instance
.
renderViewElement
,
skipOffstage:
false
).
length
;
}
...
...
packages/flutter/lib/src/cupertino/page_scaffold.dart
View file @
f4a0511e
...
...
@@ -12,6 +12,12 @@ import 'theme.dart';
/// The scaffold lays out the navigation bar on top and the content between or
/// behind the navigation bar.
///
/// When tapping a status bar at the top of the CupertinoPageScaffold, an
/// animation will complete for the current primary [ScrollView], scrolling to
/// the beginning. This is done using the [PrimaryScrollController] that
/// encloses the [ScrollView]. The [ScrollView.primary] flag is used to connect
/// a [ScrollView] to the enclosing [PrimaryScrollController].
///
/// See also:
///
/// * [CupertinoTabScaffold], a similar widget for tabbed applications.
...
...
@@ -75,11 +81,11 @@ class CupertinoPageScaffold extends StatefulWidget {
}
class
_CupertinoPageScaffoldState
extends
State
<
CupertinoPageScaffold
>
{
final
ScrollController
_primaryScrollController
=
ScrollController
();
void
_handleStatusBarTap
()
{
final
ScrollController
?
_primaryScrollController
=
PrimaryScrollController
.
of
(
context
);
// Only act on the scroll controller if it has any attached scroll positions.
if
(
_primaryScrollController
.
hasClients
)
{
if
(
_primaryScrollController
!=
null
&&
_primaryScrollController
.
hasClients
)
{
_primaryScrollController
.
animateTo
(
0.0
,
// Eyeballed from iOS.
...
...
@@ -163,10 +169,7 @@ class _CupertinoPageScaffoldState extends State<CupertinoPageScaffold> {
child:
Stack
(
children:
<
Widget
>[
// The main content being at the bottom is added to the stack first.
PrimaryScrollController
(
controller:
_primaryScrollController
,
child:
paddedContent
,
),
paddedContent
,
if
(
widget
.
navigationBar
!=
null
)
Positioned
(
top:
0.0
,
...
...
packages/flutter/lib/src/material/scaffold.dart
View file @
f4a0511e
...
...
@@ -2670,13 +2670,11 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
// iOS FEATURES - status bar tap, back gesture
// On iOS, tapping the status bar scrolls the app's primary scrollable to the
// top. We implement this by
providing a
primary scroll controller and
// top. We implement this by
looking up the
primary scroll controller and
// scrolling it to the top when tapped.
final
ScrollController
_primaryScrollController
=
ScrollController
();
void
_handleStatusBarTap
()
{
if
(
_primaryScrollController
.
hasClients
)
{
final
ScrollController
?
_primaryScrollController
=
PrimaryScrollController
.
of
(
context
);
if
(
_primaryScrollController
!=
null
&&
_primaryScrollController
.
hasClients
)
{
_primaryScrollController
.
animateTo
(
0.0
,
duration:
const
Duration
(
milliseconds:
300
),
...
...
@@ -3160,30 +3158,27 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
return
_ScaffoldScope
(
hasDrawer:
hasDrawer
,
geometryNotifier:
_geometryNotifier
,
child:
PrimaryScrollController
(
controller:
_primaryScrollController
,
child:
Material
(
color:
widget
.
backgroundColor
??
themeData
.
scaffoldBackgroundColor
,
child:
AnimatedBuilder
(
animation:
_floatingActionButtonMoveController
,
builder:
(
BuildContext
context
,
Widget
?
child
)
{
return
CustomMultiChildLayout
(
children:
children
,
delegate:
_ScaffoldLayout
(
extendBody:
_extendBody
,
extendBodyBehindAppBar:
widget
.
extendBodyBehindAppBar
,
minInsets:
minInsets
,
minViewPadding:
minViewPadding
,
currentFloatingActionButtonLocation:
_floatingActionButtonLocation
!,
floatingActionButtonMoveAnimationProgress:
_floatingActionButtonMoveController
.
value
,
floatingActionButtonMotionAnimator:
_floatingActionButtonAnimator
,
geometryNotifier:
_geometryNotifier
,
previousFloatingActionButtonLocation:
_previousFloatingActionButtonLocation
!,
textDirection:
textDirection
,
isSnackBarFloating:
isSnackBarFloating
,
snackBarWidth:
snackBarWidth
,
),
);
}),
),
child:
Material
(
color:
widget
.
backgroundColor
??
themeData
.
scaffoldBackgroundColor
,
child:
AnimatedBuilder
(
animation:
_floatingActionButtonMoveController
,
builder:
(
BuildContext
context
,
Widget
?
child
)
{
return
CustomMultiChildLayout
(
children:
children
,
delegate:
_ScaffoldLayout
(
extendBody:
_extendBody
,
extendBodyBehindAppBar:
widget
.
extendBodyBehindAppBar
,
minInsets:
minInsets
,
minViewPadding:
minViewPadding
,
currentFloatingActionButtonLocation:
_floatingActionButtonLocation
!,
floatingActionButtonMoveAnimationProgress:
_floatingActionButtonMoveController
.
value
,
floatingActionButtonMotionAnimator:
_floatingActionButtonAnimator
,
geometryNotifier:
_geometryNotifier
,
previousFloatingActionButtonLocation:
_previousFloatingActionButtonLocation
!,
textDirection:
textDirection
,
isSnackBarFloating:
isSnackBarFloating
,
snackBarWidth:
snackBarWidth
,
),
);
}),
),
);
}
...
...
packages/flutter/lib/src/widgets/primary_scroll_controller.dart
View file @
f4a0511e
...
...
@@ -16,6 +16,19 @@ import 'scroll_controller.dart';
/// This mechanism can be used to provide default behavior for scroll views in a
/// subtree. For example, the [Scaffold] uses this mechanism to implement the
/// scroll-to-top gesture on iOS.
///
/// Another default behavior handled by the PrimaryScrollController is default
/// [ScrollAction]s. If a ScrollAction is not handled by an otherwise focused
/// part of the application, the ScrollAction will be evaluated using the scroll
/// view associated with a PrimaryScrollController, for example, when executing
/// [Shortcuts] key events like page up and down.
///
/// See also:
/// * [ScrollAction], an [Action] that scrolls the [Scrollable] that encloses
/// the current [primaryFocus] or is attached to the PrimaryScrollController.
/// * [Shortcuts], a widget that establishes a [ShortcutManager] to be used
/// by its descendants when invoking an [Action] via a keyboard key
/// combination that maps to an [Intent].
class
PrimaryScrollController
extends
InheritedWidget
{
/// Creates a widget that associates a [ScrollController] with a subtree.
const
PrimaryScrollController
({
...
...
packages/flutter/lib/src/widgets/routes.dart
View file @
f4a0511e
...
...
@@ -18,7 +18,9 @@ import 'modal_barrier.dart';
import
'navigator.dart'
;
import
'overlay.dart'
;
import
'page_storage.dart'
;
import
'primary_scroll_controller.dart'
;
import
'restoration.dart'
;
import
'scroll_controller.dart'
;
import
'transitions.dart'
;
// Examples can assume:
...
...
@@ -722,6 +724,7 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> {
/// The node this scope will use for its root [FocusScope] widget.
final
FocusScopeNode
focusScopeNode
=
FocusScopeNode
(
debugLabel:
'
$_ModalScopeState
Focus Scope'
);
final
ScrollController
primaryScrollController
=
ScrollController
();
@override
void
initState
()
{
...
...
@@ -802,44 +805,47 @@ class _ModalScopeState<T> extends State<_ModalScope<T>> {
actions:
<
Type
,
Action
<
Intent
>>{
DismissIntent:
_DismissModalAction
(
context
),
},
child:
FocusScope
(
node:
focusScopeNode
,
// immutable
child:
RepaintBoundary
(
child:
AnimatedBuilder
(
animation:
_listenable
,
// immutable
builder:
(
BuildContext
context
,
Widget
?
child
)
{
return
widget
.
route
.
buildTransitions
(
context
,
widget
.
route
.
animation
!,
widget
.
route
.
secondaryAnimation
!,
// This additional AnimatedBuilder is include because if the
// value of the userGestureInProgressNotifier changes, it's
// only necessary to rebuild the IgnorePointer widget and set
// the focus node's ability to focus.
AnimatedBuilder
(
animation:
widget
.
route
.
navigator
?.
userGestureInProgressNotifier
??
ValueNotifier
<
bool
>(
false
),
builder:
(
BuildContext
context
,
Widget
?
child
)
{
final
bool
ignoreEvents
=
_shouldIgnoreFocusRequest
;
focusScopeNode
.
canRequestFocus
=
!
ignoreEvents
;
return
IgnorePointer
(
ignoring:
ignoreEvents
,
child:
child
,
child:
PrimaryScrollController
(
controller:
primaryScrollController
,
child:
FocusScope
(
node:
focusScopeNode
,
// immutable
child:
RepaintBoundary
(
child:
AnimatedBuilder
(
animation:
_listenable
,
// immutable
builder:
(
BuildContext
context
,
Widget
?
child
)
{
return
widget
.
route
.
buildTransitions
(
context
,
widget
.
route
.
animation
!,
widget
.
route
.
secondaryAnimation
!,
// This additional AnimatedBuilder is include because if the
// value of the userGestureInProgressNotifier changes, it's
// only necessary to rebuild the IgnorePointer widget and set
// the focus node's ability to focus.
AnimatedBuilder
(
animation:
widget
.
route
.
navigator
?.
userGestureInProgressNotifier
??
ValueNotifier
<
bool
>(
false
),
builder:
(
BuildContext
context
,
Widget
?
child
)
{
final
bool
ignoreEvents
=
_shouldIgnoreFocusRequest
;
focusScopeNode
.
canRequestFocus
=
!
ignoreEvents
;
return
IgnorePointer
(
ignoring:
ignoreEvents
,
child:
child
,
);
},
child:
child
,
),
);
},
child:
_page
??=
RepaintBoundary
(
key:
widget
.
route
.
_subtreeKey
,
// immutable
child:
Builder
(
builder:
(
BuildContext
context
)
{
return
widget
.
route
.
buildPage
(
context
,
widget
.
route
.
animation
!,
widget
.
route
.
secondaryAnimation
!,
);
},
child:
child
,
),
);
},
child:
_page
??=
RepaintBoundary
(
key:
widget
.
route
.
_subtreeKey
,
// immutable
child:
Builder
(
builder:
(
BuildContext
context
)
{
return
widget
.
route
.
buildPage
(
context
,
widget
.
route
.
animation
!,
widget
.
route
.
secondaryAnimation
!,
);
},
),
),
),
...
...
packages/flutter/lib/src/widgets/scroll_view.dart
View file @
f4a0511e
...
...
@@ -149,6 +149,11 @@ abstract class ScrollView extends StatelessWidget {
/// sufficient content to actually scroll. Otherwise, by default the user can
/// only scroll the view if it has sufficient content. See [physics].
///
/// Also when true, the scroll view is used for default [ScrollAction]s. If a
/// ScrollAction is not handled by an otherwise focused part of the application,
/// the ScrollAction will be evaluated using this scroll view, for example,
/// when executing [Shortcuts] key events like page up and down.
///
/// On iOS, this also identifies the scroll view that will scroll to top in
/// response to a tap in the status bar.
///
...
...
packages/flutter/lib/src/widgets/scrollable.dart
View file @
f4a0511e
...
...
@@ -17,6 +17,7 @@ import 'focus_manager.dart';
import
'framework.dart'
;
import
'gesture_detector.dart'
;
import
'notification_listener.dart'
;
import
'primary_scroll_controller.dart'
;
import
'restoration.dart'
;
import
'restoration_properties.dart'
;
import
'scroll_configuration.dart'
;
...
...
@@ -959,6 +960,10 @@ class ScrollIntent extends Intent {
/// An [Action] that scrolls the [Scrollable] that encloses the current
/// [primaryFocus] by the amount configured in the [ScrollIntent] given to it.
///
/// If a Scrollable cannot be found above the current [primaryFocus], the
/// [PrimaryScrollController] will be considered for default handling of
/// [ScrollAction]s.
///
/// If [Scrollable.incrementCalculator] is null for the scrollable, the default
/// for a [ScrollIntent.type] set to [ScrollIncrementType.page] is 80% of the
/// size of the scroll window, and for [ScrollIncrementType.line], 50 logical
...
...
@@ -967,7 +972,21 @@ class ScrollAction extends Action<ScrollIntent> {
@override
bool
isEnabled
(
ScrollIntent
intent
)
{
final
FocusNode
?
focus
=
primaryFocus
;
return
focus
!=
null
&&
focus
.
context
!=
null
&&
Scrollable
.
of
(
focus
.
context
!)
!=
null
;
final
bool
contextIsValid
=
focus
!=
null
&&
focus
.
context
!=
null
;
if
(
contextIsValid
)
{
// Check for primary scrollable within the current context
if
(
Scrollable
.
of
(
focus
!.
context
!)
!=
null
)
return
true
;
// Check for fallback scrollable with context from PrimaryScrollController
if
(
PrimaryScrollController
.
of
(
focus
.
context
!)
!=
null
)
{
final
ScrollController
?
primaryScrollController
=
PrimaryScrollController
.
of
(
focus
.
context
!);
return
primaryScrollController
!=
null
&&
primaryScrollController
.
hasClients
&&
primaryScrollController
.
position
.
context
.
notificationContext
!=
null
&&
Scrollable
.
of
(
primaryScrollController
.
position
.
context
.
notificationContext
!)
!=
null
;
}
}
return
false
;
}
// Returns the scroll increment for a single scroll request, for use when
...
...
@@ -1051,7 +1070,11 @@ class ScrollAction extends Action<ScrollIntent> {
@override
void
invoke
(
ScrollIntent
intent
)
{
final
ScrollableState
?
state
=
Scrollable
.
of
(
primaryFocus
!.
context
!);
ScrollableState
?
state
=
Scrollable
.
of
(
primaryFocus
!.
context
!);
if
(
state
==
null
)
{
final
ScrollController
?
primaryScrollController
=
PrimaryScrollController
.
of
(
primaryFocus
!.
context
!);
state
=
Scrollable
.
of
(
primaryScrollController
!.
position
.
context
.
notificationContext
!);
}
assert
(
state
!=
null
,
'
$ScrollAction
was invoked on a context that has no scrollable parent'
);
assert
(
state
!.
position
.
hasPixels
,
'Scrollable must be laid out before it can be scrolled via a ScrollAction'
);
assert
(
state
!.
position
.
viewportDimension
!=
null
);
...
...
packages/flutter/lib/src/widgets/shortcuts.dart
View file @
f4a0511e
...
...
@@ -382,7 +382,7 @@ class ShortcutManager extends ChangeNotifier with Diagnosticable {
}
}
/// A widget that establishes a
n
[ShortcutManager] to be used by its descendants
/// A widget that establishes a [ShortcutManager] to be used by its descendants
/// when invoking an [Action] via a keyboard key combination that maps to an
/// [Intent].
///
...
...
packages/flutter/lib/src/widgets/single_child_scroll_view.dart
View file @
f4a0511e
...
...
@@ -270,6 +270,11 @@ class SingleChildScrollView extends StatelessWidget {
/// Whether this is the primary scroll view associated with the parent
/// [PrimaryScrollController].
///
/// When true, the scroll view is used for default [ScrollAction]s. If a
/// ScrollAction is not handled by an otherwise focused part of the application,
/// the ScrollAction will be evaluated using this scroll view, for example,
/// when executing [Shortcuts] key events like page up and down.
///
/// On iOS, this identifies the scroll view that will scroll to top in
/// response to a tap in the status bar.
///
...
...
packages/flutter/test/material/app_bar_test.dart
View file @
f4a0511e
...
...
@@ -19,43 +19,32 @@ Widget buildSliverAppBarApp({
bool
snap
=
false
,
double
toolbarHeight
=
kToolbarHeight
,
})
{
return
Localizations
(
locale:
const
Locale
(
'en'
,
'US'
),
delegates:
const
<
LocalizationsDelegate
<
dynamic
>>[
DefaultMaterialLocalizations
.
delegate
,
DefaultWidgetsLocalizations
.
delegate
,
],
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
MediaQuery
(
data:
const
MediaQueryData
(),
child:
Scaffold
(
body:
DefaultTabController
(
length:
3
,
child:
CustomScrollView
(
primary:
true
,
slivers:
<
Widget
>[
SliverAppBar
(
title:
const
Text
(
'AppBar Title'
),
floating:
floating
,
pinned:
pinned
,
collapsedHeight:
collapsedHeight
,
expandedHeight:
expandedHeight
,
toolbarHeight:
toolbarHeight
,
snap:
snap
,
bottom:
TabBar
(
tabs:
<
String
>[
'A'
,
'B'
,
'C'
].
map
<
Widget
>((
String
t
)
=>
Tab
(
text:
'TAB
$t
'
)).
toList
(),
),
),
SliverToBoxAdapter
(
child:
Container
(
height:
1200.0
,
color:
Colors
.
orange
[
400
],
),
),
],
return
MaterialApp
(
home:
Scaffold
(
body:
DefaultTabController
(
length:
3
,
child:
CustomScrollView
(
primary:
true
,
slivers:
<
Widget
>[
SliverAppBar
(
title:
const
Text
(
'AppBar Title'
),
floating:
floating
,
pinned:
pinned
,
collapsedHeight:
collapsedHeight
,
expandedHeight:
expandedHeight
,
toolbarHeight:
toolbarHeight
,
snap:
snap
,
bottom:
TabBar
(
tabs:
<
String
>[
'A'
,
'B'
,
'C'
].
map
<
Widget
>((
String
t
)
=>
Tab
(
text:
'TAB
$t
'
)).
toList
(),
),
),
),
SliverToBoxAdapter
(
child:
Container
(
height:
1200.0
,
color:
Colors
.
orange
[
400
],
),
),
],
),
),
),
...
...
packages/flutter/test/material/debug_test.dart
View file @
f4a0511e
...
...
@@ -135,6 +135,7 @@ void main() {
' _FocusMarker
\n
'
' Semantics
\n
'
' FocusScope
\n
'
' PrimaryScrollController
\n
'
' _ActionsMarker
\n
'
' Actions
\n
'
' Builder
\n
'
...
...
packages/flutter/test/material/scaffold_test.dart
View file @
f4a0511e
...
...
@@ -2128,12 +2128,11 @@ void main() {
' AnimatedBuilder
\n
'
' DefaultTextStyle
\n
'
' AnimatedDefaultTextStyle
\n
'
' _InkFeatures-[GlobalKey#
342d
0 ink renderer]
\n
'
' _InkFeatures-[GlobalKey#
0000
0 ink renderer]
\n
'
' NotificationListener<LayoutChangedNotification>
\n
'
' PhysicalModel
\n
'
' AnimatedPhysicalModel
\n
'
' Material
\n
'
' PrimaryScrollController
\n
'
' _ScaffoldScope
\n
'
' Scaffold
\n
'
' MediaQuery
\n
'
...
...
packages/flutter/test/widgets/scroll_view_test.dart
View file @
f4a0511e
...
...
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/services.dart'
show
LogicalKeyboardKey
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter/gestures.dart'
show
DragStartBehavior
;
...
...
@@ -1259,4 +1260,48 @@ void main() {
semanticChildCount:
4
,
),
throwsAssertionError
);
});
testWidgets
(
'PrimaryScrollController provides fallback ScrollActions'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
MaterialApp
(
home:
CustomScrollView
(
primary:
true
,
slivers:
List
<
Widget
>.
generate
(
20
,
(
int
index
)
{
return
SliverToBoxAdapter
(
child:
Focus
(
autofocus:
index
==
0
,
child:
SizedBox
(
key:
ValueKey
<
String
>(
'Box
$index
'
),
height:
50.0
),
),
);
},
),
),
),
);
final
ScrollController
controller
=
PrimaryScrollController
.
of
(
tester
.
element
(
find
.
byType
(
CustomScrollView
))
)!;
await
tester
.
pumpAndSettle
();
expect
(
controller
.
position
.
pixels
,
equals
(
0.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
0.0
,
800.0
,
50.0
)),
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
pageDown
);
await
tester
.
pumpAndSettle
();
expect
(
controller
.
position
.
pixels
,
equals
(
400.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
-
400.0
,
800.0
,
-
350.0
)),
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
pageUp
);
await
tester
.
pumpAndSettle
();
expect
(
controller
.
position
.
pixels
,
equals
(
0.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
0.0
,
800.0
,
50.0
)),
);
});
}
packages/flutter/test/widgets/scrollable_test.dart
View file @
f4a0511e
...
...
@@ -521,14 +521,20 @@ void main() {
await
tester
.
pumpAndSettle
();
expect
(
controller
.
position
.
pixels
,
equals
(
0.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
0.0
,
800.0
,
50.0
)));
await
tester
.
sendKeyDownEvent
(
modifierKey
);
// We exclude the modifier keys here for web testing since default web shortcuts
// do not use a modifier key with arrow keys for ScrollActions.
if
(!
kIsWeb
)
await
tester
.
sendKeyDownEvent
(
modifierKey
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowDown
);
await
tester
.
sendKeyUpEvent
(
modifierKey
);
if
(!
kIsWeb
)
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
-
50.0
,
800.0
,
0.0
)));
await
tester
.
sendKeyDownEvent
(
modifierKey
);
if
(!
kIsWeb
)
await
tester
.
sendKeyDownEvent
(
modifierKey
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowUp
);
await
tester
.
sendKeyUpEvent
(
modifierKey
);
if
(!
kIsWeb
)
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
0.0
,
800.0
,
50.0
)));
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
pageDown
);
...
...
@@ -537,7 +543,7 @@ void main() {
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
pageUp
);
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
0.0
,
800.0
,
50.0
)));
}
,
skip:
isBrowser
);
// https://github.com/flutter/flutter/issues/43694
}
);
testWidgets
(
'Horizontal scrollables are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
...
...
@@ -567,17 +573,23 @@ void main() {
await
tester
.
pumpAndSettle
();
expect
(
controller
.
position
.
pixels
,
equals
(
0.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
0.0
,
50.0
,
600.0
)));
await
tester
.
sendKeyDownEvent
(
modifierKey
);
// We exclude the modifier keys here for web testing since default web shortcuts
// do not use a modifier key with arrow keys for ScrollActions.
if
(!
kIsWeb
)
await
tester
.
sendKeyDownEvent
(
modifierKey
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
sendKeyUpEvent
(
modifierKey
);
if
(!
kIsWeb
)
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(-
50.0
,
0.0
,
0.0
,
600.0
)));
await
tester
.
sendKeyDownEvent
(
modifierKey
);
if
(!
kIsWeb
)
await
tester
.
sendKeyDownEvent
(
modifierKey
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowLeft
);
await
tester
.
sendKeyUpEvent
(
modifierKey
);
if
(!
kIsWeb
)
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
0.0
,
50.0
,
600.0
)));
}
,
skip:
isBrowser
);
// https://github.com/flutter/flutter/issues/43694
}
);
testWidgets
(
'Horizontal scrollables are scrolled the correct direction in RTL locales.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
...
...
@@ -610,17 +622,23 @@ void main() {
await
tester
.
pumpAndSettle
();
expect
(
controller
.
position
.
pixels
,
equals
(
0.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
750.0
,
0.0
,
800.0
,
600.0
)));
await
tester
.
sendKeyDownEvent
(
modifierKey
);
// We exclude the modifier keys here for web testing since default web shortcuts
// do not use a modifier key with arrow keys for ScrollActions.
if
(!
kIsWeb
)
await
tester
.
sendKeyDownEvent
(
modifierKey
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowLeft
);
await
tester
.
sendKeyUpEvent
(
modifierKey
);
if
(!
kIsWeb
)
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
800.0
,
0.0
,
850.0
,
600.0
)));
await
tester
.
sendKeyDownEvent
(
modifierKey
);
if
(!
kIsWeb
)
await
tester
.
sendKeyDownEvent
(
modifierKey
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
sendKeyUpEvent
(
modifierKey
);
if
(!
kIsWeb
)
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
750.0
,
0.0
,
800.0
,
600.0
)));
}
,
skip:
isBrowser
);
// https://github.com/flutter/flutter/issues/43694
}
);
testWidgets
(
'Reversed vertical scrollables are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
...
...
@@ -652,14 +670,20 @@ void main() {
await
tester
.
pumpAndSettle
();
expect
(
controller
.
position
.
pixels
,
equals
(
0.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
550.0
,
800.0
,
600.0
)));
await
tester
.
sendKeyDownEvent
(
modifierKey
);
// We exclude the modifier keys here for web testing since default web shortcuts
// do not use a modifier key with arrow keys for ScrollActions.
if
(!
kIsWeb
)
await
tester
.
sendKeyDownEvent
(
modifierKey
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowUp
);
await
tester
.
sendKeyUpEvent
(
modifierKey
);
if
(!
kIsWeb
)
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
600.0
,
800.0
,
650.0
)));
await
tester
.
sendKeyDownEvent
(
modifierKey
);
if
(!
kIsWeb
)
await
tester
.
sendKeyDownEvent
(
modifierKey
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowDown
);
await
tester
.
sendKeyUpEvent
(
modifierKey
);
if
(!
kIsWeb
)
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
550.0
,
800.0
,
600.0
)));
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
pageUp
);
...
...
@@ -668,7 +692,7 @@ void main() {
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
pageDown
);
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
550.0
,
800.0
,
600.0
)));
}
,
skip:
isBrowser
);
// https://github.com/flutter/flutter/issues/43694
}
);
testWidgets
(
'Reversed horizontal scrollables are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
...
...
@@ -701,16 +725,22 @@ void main() {
await
tester
.
pumpAndSettle
();
expect
(
controller
.
position
.
pixels
,
equals
(
0.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
750.0
,
0.0
,
800.0
,
600.00
)));
await
tester
.
sendKeyDownEvent
(
modifierKey
);
// We exclude the modifier keys here for web testing since default web shortcuts
// do not use a modifier key with arrow keys for ScrollActions.
if
(!
kIsWeb
)
await
tester
.
sendKeyDownEvent
(
modifierKey
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowLeft
);
await
tester
.
sendKeyUpEvent
(
modifierKey
);
if
(!
kIsWeb
)
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Box 0'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
800.0
,
0.0
,
850.0
,
600.0
)));
await
tester
.
sendKeyDownEvent
(
modifierKey
);
if
(!
kIsWeb
)
await
tester
.
sendKeyDownEvent
(
modifierKey
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowRight
);
await
tester
.
sendKeyUpEvent
(
modifierKey
);
if
(!
kIsWeb
)
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
pumpAndSettle
();
}
,
skip:
isBrowser
);
// https://github.com/flutter/flutter/issues/43694
}
);
testWidgets
(
'Custom scrollables with a center sliver are scrolled when activated via keyboard.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
...
...
@@ -747,24 +777,30 @@ void main() {
expect
(
controller
.
position
.
pixels
,
equals
(
0.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Item 10'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
0.0
,
800.0
,
100.0
)));
for
(
int
i
=
0
;
i
<
10
;
++
i
)
{
await
tester
.
sendKeyDownEvent
(
modifierKey
);
// We exclude the modifier keys here for web testing since default web shortcuts
// do not use a modifier key with arrow keys for ScrollActions.
if
(!
kIsWeb
)
await
tester
.
sendKeyDownEvent
(
modifierKey
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowDown
);
await
tester
.
sendKeyUpEvent
(
modifierKey
);
if
(!
kIsWeb
)
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
pumpAndSettle
();
}
// Starts at #10 already, so doesn't work out to 500.0 because it hits bottom.
expect
(
controller
.
position
.
pixels
,
equals
(
400.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Item 10'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
-
400.0
,
800.0
,
-
300.0
)));
for
(
int
i
=
0
;
i
<
10
;
++
i
)
{
await
tester
.
sendKeyDownEvent
(
modifierKey
);
if
(!
kIsWeb
)
await
tester
.
sendKeyDownEvent
(
modifierKey
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
arrowUp
);
await
tester
.
sendKeyUpEvent
(
modifierKey
);
if
(!
kIsWeb
)
await
tester
.
sendKeyUpEvent
(
modifierKey
);
await
tester
.
pumpAndSettle
();
}
// Goes up two past "center" where it started, so negative.
expect
(
controller
.
position
.
pixels
,
equals
(-
100.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
const
ValueKey
<
String
>(
'Item 10'
),
skipOffstage:
false
)),
equals
(
const
Rect
.
fromLTRB
(
0.0
,
100.0
,
800.0
,
200.0
)));
}
,
skip:
isBrowser
);
// https://github.com/flutter/flutter/issues/43694
}
);
testWidgets
(
'Can recommendDeferredLoadingForContext - animation'
,
(
WidgetTester
tester
)
async
{
final
List
<
String
>
widgetTracker
=
<
String
>[];
...
...
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