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
f8e9a4ff
Unverified
Commit
f8e9a4ff
authored
Mar 13, 2020
by
gaaclarke
Committed by
GitHub
Mar 13, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added option to specify you want the keyboard to be dismissed when you scroll. (#52068)
parent
9186dfc3
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
153 additions
and
6 deletions
+153
-6
scroll_view.dart
packages/flutter/lib/src/widgets/scroll_view.dart
+46
-6
scroll_view_test.dart
packages/flutter/test/widgets/scroll_view_test.dart
+107
-0
No files found.
packages/flutter/lib/src/widgets/scroll_view.dart
View file @
f8e9a4ff
...
...
@@ -8,15 +8,30 @@ import 'package:flutter/rendering.dart';
import
'package:flutter/gestures.dart'
;
import
'basic.dart'
;
import
'focus_manager.dart'
;
import
'focus_scope.dart'
;
import
'framework.dart'
;
import
'media_query.dart'
;
import
'notification_listener.dart'
;
import
'primary_scroll_controller.dart'
;
import
'scroll_controller.dart'
;
import
'scroll_notification.dart'
;
import
'scroll_physics.dart'
;
import
'scrollable.dart'
;
import
'sliver.dart'
;
import
'viewport.dart'
;
/// A representation of how a [ScrollView] should dismiss the on-screen
/// keyboard.
enum
ScrollViewKeyboardDismissBehavior
{
/// `manual` means there is no automatic dimissal of the on-screen keyboard.
/// It is up to the client to dismiss the keyboard.
manual
,
/// `onDrag` means that the [ScrollView] will dismiss an on-screen keyboard
/// when a drag begins.
onDrag
,
}
/// A widget that scrolls.
///
/// Scrollable widgets consist of three pieces:
...
...
@@ -70,6 +85,7 @@ abstract class ScrollView extends StatelessWidget {
this
.
cacheExtent
,
this
.
semanticChildCount
,
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
})
:
assert
(
scrollDirection
!=
null
),
assert
(
reverse
!=
null
),
assert
(
shrinkWrap
!=
null
),
...
...
@@ -232,6 +248,10 @@ abstract class ScrollView extends StatelessWidget {
/// {@macro flutter.widgets.scrollable.dragStartBehavior}
final
DragStartBehavior
dragStartBehavior
;
/// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will
/// dismiss the keyboard automatically.
final
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
;
/// Returns the [AxisDirection] in which the scroll view scrolls.
///
/// Combines the [scrollDirection] with the [reverse] boolean to obtain the
...
...
@@ -297,9 +317,8 @@ abstract class ScrollView extends StatelessWidget {
final
List
<
Widget
>
slivers
=
buildSlivers
(
context
);
final
AxisDirection
axisDirection
=
getDirection
(
context
);
final
ScrollController
scrollController
=
primary
?
PrimaryScrollController
.
of
(
context
)
:
controller
;
final
ScrollController
scrollController
=
primary
?
PrimaryScrollController
.
of
(
context
)
:
controller
;
final
Scrollable
scrollable
=
Scrollable
(
dragStartBehavior:
dragStartBehavior
,
axisDirection:
axisDirection
,
...
...
@@ -310,9 +329,24 @@ abstract class ScrollView extends StatelessWidget {
return
buildViewport
(
context
,
offset
,
axisDirection
,
slivers
);
},
);
return
primary
&&
scrollController
!=
null
?
PrimaryScrollController
.
none
(
child:
scrollable
)
:
scrollable
;
final
Widget
scrollableResult
=
primary
&&
scrollController
!=
null
?
PrimaryScrollController
.
none
(
child:
scrollable
)
:
scrollable
;
if
(
keyboardDismissBehavior
==
ScrollViewKeyboardDismissBehavior
.
onDrag
)
{
return
NotificationListener
<
ScrollUpdateNotification
>(
child:
scrollableResult
,
onNotification:
(
ScrollUpdateNotification
notification
)
{
final
FocusScopeNode
focusScope
=
FocusScope
.
of
(
context
);
if
(
notification
.
dragDetails
!=
null
&&
focusScope
.
hasFocus
)
{
focusScope
.
unfocus
();
}
return
false
;
},
);
}
else
{
return
scrollableResult
;
}
}
@override
...
...
@@ -504,6 +538,7 @@ abstract class BoxScrollView extends ScrollView {
double
cacheExtent
,
int
semanticChildCount
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
})
:
super
(
key:
key
,
scrollDirection:
scrollDirection
,
...
...
@@ -515,6 +550,7 @@ abstract class BoxScrollView extends ScrollView {
cacheExtent:
cacheExtent
,
semanticChildCount:
semanticChildCount
,
dragStartBehavior:
dragStartBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
);
/// The amount of space by which to inset the children.
...
...
@@ -877,6 +913,7 @@ class ListView extends BoxScrollView {
List
<
Widget
>
children
=
const
<
Widget
>[],
int
semanticChildCount
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
})
:
childrenDelegate
=
SliverChildListDelegate
(
children
,
addAutomaticKeepAlives:
addAutomaticKeepAlives
,
...
...
@@ -895,6 +932,7 @@ class ListView extends BoxScrollView {
cacheExtent:
cacheExtent
,
semanticChildCount:
semanticChildCount
??
children
.
length
,
dragStartBehavior:
dragStartBehavior
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
);
/// Creates a scrollable, linear array of widgets that are created on demand.
...
...
@@ -1031,6 +1069,7 @@ class ListView extends BoxScrollView {
bool
addRepaintBoundaries
=
true
,
bool
addSemanticIndexes
=
true
,
double
cacheExtent
,
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
})
:
assert
(
itemBuilder
!=
null
),
assert
(
separatorBuilder
!=
null
),
assert
(
itemCount
!=
null
&&
itemCount
>=
0
),
...
...
@@ -1071,6 +1110,7 @@ class ListView extends BoxScrollView {
padding:
padding
,
cacheExtent:
cacheExtent
,
semanticChildCount:
itemCount
,
keyboardDismissBehavior:
keyboardDismissBehavior
,
);
/// Creates a scrollable, linear array of widgets with a custom child model.
...
...
packages/flutter/test/widgets/scroll_view_test.dart
View file @
f8e9a4ff
...
...
@@ -9,6 +9,51 @@ import 'package:flutter/material.dart';
import
'states.dart'
;
class
MaterialLocalizationsDelegate
extends
LocalizationsDelegate
<
MaterialLocalizations
>
{
@override
bool
isSupported
(
Locale
locale
)
=>
true
;
@override
Future
<
MaterialLocalizations
>
load
(
Locale
locale
)
=>
DefaultMaterialLocalizations
.
load
(
locale
);
@override
bool
shouldReload
(
MaterialLocalizationsDelegate
old
)
=>
false
;
}
class
WidgetsLocalizationsDelegate
extends
LocalizationsDelegate
<
WidgetsLocalizations
>
{
@override
bool
isSupported
(
Locale
locale
)
=>
true
;
@override
Future
<
WidgetsLocalizations
>
load
(
Locale
locale
)
=>
DefaultWidgetsLocalizations
.
load
(
locale
);
@override
bool
shouldReload
(
WidgetsLocalizationsDelegate
old
)
=>
false
;
}
Widget
textFieldBoilerplate
(
{
Widget
child
})
{
return
MaterialApp
(
home:
Localizations
(
locale:
const
Locale
(
'en'
,
'US'
),
delegates:
<
LocalizationsDelegate
<
dynamic
>>[
WidgetsLocalizationsDelegate
(),
MaterialLocalizationsDelegate
(),
],
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
MediaQuery
(
data:
const
MediaQueryData
(
size:
Size
(
800.0
,
600.0
)),
child:
Center
(
child:
Material
(
child:
child
,
),
),
),
),
),
);
}
void
main
(
)
{
testWidgets
(
'ListView control test'
,
(
WidgetTester
tester
)
async
{
final
List
<
String
>
log
=
<
String
>[];
...
...
@@ -52,6 +97,68 @@ void main() {
log
.
clear
();
});
testWidgets
(
'ListView dismiss keyboard onDrag test'
,
(
WidgetTester
tester
)
async
{
final
List
<
FocusNode
>
focusNodes
=
List
<
FocusNode
>.
generate
(
50
,
(
int
i
)
=>
FocusNode
());
await
tester
.
pumpWidget
(
textFieldBoilerplate
(
child:
ListView
(
padding:
const
EdgeInsets
.
all
(
0
),
keyboardDismissBehavior:
ScrollViewKeyboardDismissBehavior
.
onDrag
,
children:
focusNodes
.
map
((
FocusNode
focusNode
)
{
return
Container
(
height:
50
,
color:
Colors
.
green
,
child:
TextField
(
focusNode:
focusNode
,
style:
const
TextStyle
(
fontSize:
24
,
fontWeight:
FontWeight
.
bold
,
)),
);
}).
toList
(),
)));
final
Finder
finder
=
find
.
byType
(
TextField
).
first
;
final
TextField
textField
=
tester
.
widget
(
finder
);
await
tester
.
showKeyboard
(
finder
);
expect
(
textField
.
focusNode
.
hasFocus
,
isTrue
);
await
tester
.
drag
(
finder
,
const
Offset
(
0.0
,
-
40.0
));
await
tester
.
pumpAndSettle
();
expect
(
textField
.
focusNode
.
hasFocus
,
isFalse
);
});
testWidgets
(
'ListView dismiss keyboard manual test'
,
(
WidgetTester
tester
)
async
{
final
List
<
FocusNode
>
focusNodes
=
List
<
FocusNode
>.
generate
(
50
,
(
int
i
)
=>
FocusNode
());
await
tester
.
pumpWidget
(
textFieldBoilerplate
(
child:
ListView
(
padding:
const
EdgeInsets
.
all
(
0
),
keyboardDismissBehavior:
ScrollViewKeyboardDismissBehavior
.
manual
,
children:
focusNodes
.
map
((
FocusNode
focusNode
)
{
return
Container
(
height:
50
,
color:
Colors
.
green
,
child:
TextField
(
focusNode:
focusNode
,
style:
const
TextStyle
(
fontSize:
24
,
fontWeight:
FontWeight
.
bold
,
)),
);
}).
toList
(),
)));
final
Finder
finder
=
find
.
byType
(
TextField
).
first
;
final
TextField
textField
=
tester
.
widget
(
finder
);
await
tester
.
showKeyboard
(
finder
);
expect
(
textField
.
focusNode
.
hasFocus
,
isTrue
);
await
tester
.
drag
(
finder
,
const
Offset
(
0.0
,
-
40.0
));
await
tester
.
pumpAndSettle
();
expect
(
textField
.
focusNode
.
hasFocus
,
isTrue
);
});
testWidgets
(
'ListView restart ballistic activity out of range'
,
(
WidgetTester
tester
)
async
{
Widget
buildListView
(
int
n
)
{
return
Directionality
(
...
...
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