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
aac5b048
Unverified
Commit
aac5b048
authored
Nov 01, 2018
by
Jonah Williams
Committed by
GitHub
Nov 01, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
CupertinoPicker semantics (#23551)
parent
e92ac484
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
257 additions
and
22 deletions
+257
-22
picker.dart
packages/flutter/lib/src/cupertino/picker.dart
+147
-11
proxy_box.dart
packages/flutter/lib/src/rendering/proxy_box.dart
+0
-1
list_wheel_scroll_view.dart
packages/flutter/lib/src/widgets/list_wheel_scroll_view.dart
+8
-5
date_picker_test.dart
packages/flutter/test/cupertino/date_picker_test.dart
+98
-1
list_wheel_scroll_view_test.dart
...ges/flutter/test/widgets/list_wheel_scroll_view_test.dart
+4
-4
No files found.
packages/flutter/lib/src/cupertino/picker.dart
View file @
aac5b048
...
...
@@ -161,6 +161,32 @@ class CupertinoPicker extends StatefulWidget {
class
_CupertinoPickerState
extends
State
<
CupertinoPicker
>
{
int
_lastHapticIndex
;
FixedExtentScrollController
_controller
;
@override
void
initState
()
{
super
.
initState
();
if
(
widget
.
scrollController
==
null
)
{
_controller
=
FixedExtentScrollController
();
}
}
@override
void
didUpdateWidget
(
CupertinoPicker
oldWidget
)
{
if
(
widget
.
scrollController
!=
null
&&
oldWidget
.
scrollController
==
null
)
{
_controller
=
null
;
}
else
if
(
widget
.
scrollController
==
null
&&
oldWidget
.
scrollController
!=
null
)
{
assert
(
_controller
==
null
);
_controller
=
FixedExtentScrollController
();
}
super
.
didUpdateWidget
(
oldWidget
);
}
@override
void
dispose
()
{
_controller
?.
dispose
();
super
.
dispose
();
}
void
_handleSelectedItemChanged
(
int
index
)
{
// Only the haptic engine hardware on iOS devices would produce the
...
...
@@ -246,17 +272,20 @@ class _CupertinoPickerState extends State<CupertinoPicker> {
Widget
result
=
Stack
(
children:
<
Widget
>[
Positioned
.
fill
(
child:
ListWheelScrollView
.
useDelegate
(
controller:
widget
.
scrollController
,
physics:
const
FixedExtentScrollPhysics
(),
diameterRatio:
widget
.
diameterRatio
,
perspective:
_kDefaultPerspective
,
offAxisFraction:
widget
.
offAxisFraction
,
useMagnifier:
widget
.
useMagnifier
,
magnification:
widget
.
magnification
,
itemExtent:
widget
.
itemExtent
,
onSelectedItemChanged:
_handleSelectedItemChanged
,
childDelegate:
widget
.
childDelegate
,
child:
_CupertinoPickerSemantics
(
scrollController:
widget
.
scrollController
??
_controller
,
child:
ListWheelScrollView
.
useDelegate
(
controller:
widget
.
scrollController
??
_controller
,
physics:
const
FixedExtentScrollPhysics
(),
diameterRatio:
widget
.
diameterRatio
,
perspective:
_kDefaultPerspective
,
offAxisFraction:
widget
.
offAxisFraction
,
useMagnifier:
widget
.
useMagnifier
,
magnification:
widget
.
magnification
,
itemExtent:
widget
.
itemExtent
,
onSelectedItemChanged:
_handleSelectedItemChanged
,
childDelegate:
widget
.
childDelegate
,
),
),
),
_buildGradientScreen
(),
...
...
@@ -274,3 +303,110 @@ class _CupertinoPickerState extends State<CupertinoPicker> {
return
result
;
}
}
// Turns the scroll semantics of the ListView into a single adjustable semantics
// node. This is done by removing all of the child semantics of the scroll
// wheel and using the scroll indexes to look up the current, previous, and
// next semantic label. This label is then turned into the value of a new
// adjustable semantic node, with adjustment callbacks wired to move the
// scroll controller.
class
_CupertinoPickerSemantics
extends
SingleChildRenderObjectWidget
{
const
_CupertinoPickerSemantics
({
Key
key
,
Widget
child
,
@required
this
.
scrollController
,
})
:
super
(
key:
key
,
child:
child
);
final
FixedExtentScrollController
scrollController
;
@override
RenderObject
createRenderObject
(
BuildContext
context
)
=>
_RenderCupertinoPickerSemantics
(
scrollController
,
Directionality
.
of
(
context
));
@override
void
updateRenderObject
(
BuildContext
context
,
covariant
_RenderCupertinoPickerSemantics
renderObject
)
{
renderObject
..
textDirection
=
Directionality
.
of
(
context
)
..
controller
=
scrollController
;
}
}
class
_RenderCupertinoPickerSemantics
extends
RenderProxyBox
{
_RenderCupertinoPickerSemantics
(
FixedExtentScrollController
controller
,
this
.
_textDirection
)
{
this
.
controller
=
controller
;
}
FixedExtentScrollController
get
controller
=>
_controller
;
FixedExtentScrollController
_controller
;
set
controller
(
FixedExtentScrollController
value
)
{
if
(
value
==
_controller
)
return
;
if
(
_controller
!=
null
)
_controller
.
removeListener
(
_handleScrollUpdate
);
else
_currentIndex
=
value
.
initialItem
??
0
;
value
.
addListener
(
_handleScrollUpdate
);
_controller
=
value
;
}
TextDirection
get
textDirection
=>
_textDirection
;
TextDirection
_textDirection
;
set
textDirection
(
TextDirection
value
)
{
if
(
textDirection
==
value
)
return
;
_textDirection
=
value
;
markNeedsSemanticsUpdate
();
}
int
_currentIndex
=
0
;
void
_handleIncrease
()
{
controller
.
jumpToItem
(
_currentIndex
+
1
);
}
void
_handleDecrease
()
{
if
(
_currentIndex
==
0
)
return
;
controller
.
jumpToItem
(
_currentIndex
-
1
);
}
void
_handleScrollUpdate
()
{
if
(
controller
.
selectedItem
==
_currentIndex
)
return
;
_currentIndex
=
controller
.
selectedItem
;
markNeedsSemanticsUpdate
();
}
@override
void
describeSemanticsConfiguration
(
SemanticsConfiguration
config
)
{
super
.
describeSemanticsConfiguration
(
config
);
config
.
isSemanticBoundary
=
true
;
config
.
textDirection
=
textDirection
;
}
@override
void
assembleSemanticsNode
(
SemanticsNode
node
,
SemanticsConfiguration
config
,
Iterable
<
SemanticsNode
>
children
)
{
if
(
children
.
isEmpty
)
return
super
.
assembleSemanticsNode
(
node
,
config
,
children
);
final
SemanticsNode
scrollable
=
children
.
first
;
final
Map
<
int
,
SemanticsNode
>
indexedChildren
=
<
int
,
SemanticsNode
>{};
scrollable
.
visitChildren
((
SemanticsNode
child
)
{
assert
(
child
.
indexInParent
!=
null
);
indexedChildren
[
child
.
indexInParent
]
=
child
;
return
true
;
});
if
(
indexedChildren
[
_currentIndex
]
==
null
)
{
return
node
.
updateWith
(
config:
config
);
}
config
.
value
=
indexedChildren
[
_currentIndex
].
label
;
final
SemanticsNode
previousChild
=
indexedChildren
[
_currentIndex
-
1
];
final
SemanticsNode
nextChild
=
indexedChildren
[
_currentIndex
+
1
];
if
(
nextChild
!=
null
)
{
config
.
increasedValue
=
nextChild
.
label
;
config
.
onIncrease
=
_handleIncrease
;
}
if
(
previousChild
!=
null
)
{
config
.
decreasedValue
=
previousChild
.
label
;
config
.
onDecrease
=
_handleDecrease
;
}
node
.
updateWith
(
config:
config
);
}
}
packages/flutter/lib/src/rendering/proxy_box.dart
View file @
aac5b048
...
...
@@ -4032,7 +4032,6 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
super
.
visitChildrenForSemantics
(
visitor
);
}
@override
void
describeSemanticsConfiguration
(
SemanticsConfiguration
config
)
{
super
.
describeSemanticsConfiguration
(
config
);
...
...
packages/flutter/lib/src/widgets/list_wheel_scroll_view.dart
View file @
aac5b048
...
...
@@ -10,6 +10,7 @@ import 'package:flutter/physics.dart';
import
'package:flutter/rendering.dart'
;
import
'package:flutter/scheduler.dart'
;
import
'basic.dart'
;
import
'framework.dart'
;
import
'notification_listener.dart'
;
import
'scroll_context.dart'
;
...
...
@@ -91,7 +92,7 @@ class ListWheelChildListDelegate extends ListWheelChildDelegate {
Widget
build
(
BuildContext
context
,
int
index
)
{
if
(
index
<
0
||
index
>=
children
.
length
)
return
null
;
return
children
[
index
]
;
return
IndexedSemantics
(
child:
children
[
index
],
index:
index
)
;
}
@override
...
...
@@ -137,7 +138,7 @@ class ListWheelChildLoopingListDelegate extends ListWheelChildDelegate {
Widget
build
(
BuildContext
context
,
int
index
)
{
if
(
children
.
isEmpty
)
return
null
;
return
children
[
index
%
children
.
length
]
;
return
IndexedSemantics
(
child:
children
[
index
%
children
.
length
],
index:
index
)
;
}
@override
...
...
@@ -178,11 +179,13 @@ class ListWheelChildBuilderDelegate extends ListWheelChildDelegate {
@override
Widget
build
(
BuildContext
context
,
int
index
)
{
if
(
childCount
==
null
)
return
builder
(
context
,
index
);
if
(
childCount
==
null
)
{
final
Widget
child
=
builder
(
context
,
index
);
return
child
==
null
?
null
:
IndexedSemantics
(
child:
child
,
index:
index
);
}
if
(
index
<
0
||
index
>=
childCount
)
return
null
;
return
builder
(
context
,
index
);
return
IndexedSemantics
(
child:
builder
(
context
,
index
),
index:
index
);
}
@override
...
...
packages/flutter/test/cupertino/date_picker_test.dart
View file @
aac5b048
// Copyright 2017 The Chromium 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
'dart:ui'
;
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/semantics.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
...
...
@@ -581,4 +584,98 @@ void main() {
expect
(
date
,
DateTime
(
2018
,
1
,
1
,
15
,
59
));
});
});
}
\ No newline at end of file
testWidgets
(
'scrollController can be removed or added'
,
(
WidgetTester
tester
)
async
{
final
SemanticsHandle
handle
=
tester
.
ensureSemantics
();
int
lastSelectedItem
;
void
onSelectedItemChanged
(
int
index
)
{
lastSelectedItem
=
index
;
}
await
tester
.
pumpWidget
(
_buildPicker
(
controller:
FixedExtentScrollController
(),
onSelectedItemChanged:
onSelectedItemChanged
,
));
tester
.
binding
.
pipelineOwner
.
semanticsOwner
.
performAction
(
1
,
SemanticsAction
.
increase
);
await
tester
.
pumpAndSettle
();
expect
(
lastSelectedItem
,
1
);
await
tester
.
pumpWidget
(
_buildPicker
(
onSelectedItemChanged:
onSelectedItemChanged
,
));
tester
.
binding
.
pipelineOwner
.
semanticsOwner
.
performAction
(
1
,
SemanticsAction
.
increase
);
await
tester
.
pumpAndSettle
();
expect
(
lastSelectedItem
,
2
);
await
tester
.
pumpWidget
(
_buildPicker
(
controller:
FixedExtentScrollController
(),
onSelectedItemChanged:
onSelectedItemChanged
,
));
tester
.
binding
.
pipelineOwner
.
semanticsOwner
.
performAction
(
1
,
SemanticsAction
.
increase
);
await
tester
.
pumpAndSettle
();
expect
(
lastSelectedItem
,
3
);
handle
.
dispose
();
});
testWidgets
(
'picker exports semantics'
,
(
WidgetTester
tester
)
async
{
final
SemanticsHandle
handle
=
tester
.
ensureSemantics
();
debugResetSemanticsIdCounter
();
int
lastSelectedItem
;
await
tester
.
pumpWidget
(
_buildPicker
(
onSelectedItemChanged:
(
int
index
)
{
lastSelectedItem
=
index
;
}));
expect
(
tester
.
getSemantics
(
find
.
byType
(
CupertinoPicker
)),
matchesSemantics
(
children:
<
Matcher
>[
matchesSemantics
(
hasIncreaseAction:
true
,
hasDecreaseAction:
false
,
increasedValue:
'1'
,
value:
'0'
,
textDirection:
TextDirection
.
ltr
,
),
],
));
tester
.
binding
.
pipelineOwner
.
semanticsOwner
.
performAction
(
1
,
SemanticsAction
.
increase
);
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getSemantics
(
find
.
byType
(
CupertinoPicker
)),
matchesSemantics
(
children:
<
Matcher
>[
matchesSemantics
(
hasIncreaseAction:
true
,
hasDecreaseAction:
true
,
increasedValue:
'2'
,
decreasedValue:
'0'
,
value:
'1'
,
textDirection:
TextDirection
.
ltr
,
),
],
));
expect
(
lastSelectedItem
,
1
);
handle
.
dispose
();
});
}
Widget
_buildPicker
(
{
FixedExtentScrollController
controller
,
ValueChanged
<
int
>
onSelectedItemChanged
})
{
return
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CupertinoPicker
(
scrollController:
controller
,
itemExtent:
100.0
,
onSelectedItemChanged:
onSelectedItemChanged
,
children:
List
<
Widget
>.
generate
(
100
,
(
int
index
)
{
return
Center
(
child:
Container
(
width:
400.0
,
height:
100.0
,
child:
Text
(
index
.
toString
()),
),
);
}),
),
);
}
packages/flutter/test/widgets/list_wheel_scroll_view_test.dart
View file @
aac5b048
...
...
@@ -331,7 +331,7 @@ void main() {
)
);
final
RenderListWheelViewport
viewport
=
tester
.
firstRenderObject
(
find
.
byType
(
Text
)).
parent
;
final
RenderListWheelViewport
viewport
=
tester
.
firstRenderObject
(
find
.
byType
(
Text
)).
parent
.
parent
;
// Item 0 is in the middle. There are 3 children visible after it, so the
// value of childCount should be 4.
...
...
@@ -515,7 +515,7 @@ void main() {
),
);
final
RenderListWheelViewport
viewport
=
tester
.
firstRenderObject
(
find
.
byType
(
Container
)).
parent
;
final
RenderListWheelViewport
viewport
=
tester
.
firstRenderObject
(
find
.
byType
(
Container
)).
parent
.
parent
;
expect
(
viewport
,
paints
..
transform
(
matrix4:
equals
(<
dynamic
>[
1.0
,
0.0
,
0.0
,
0.0
,
...
...
@@ -573,7 +573,7 @@ void main() {
),
);
final
RenderListWheelViewport
viewport
=
tester
.
firstRenderObject
(
find
.
byType
(
Container
)).
parent
;
final
RenderListWheelViewport
viewport
=
tester
.
firstRenderObject
(
find
.
byType
(
Container
)).
parent
.
parent
;
expect
(
viewport
,
paints
..
transform
(
matrix4:
equals
(<
dynamic
>[
1.0
,
0.0
,
0.0
,
0.0
,
...
...
@@ -694,7 +694,7 @@ void main() {
),
);
final
RenderListWheelViewport
viewport
=
tester
.
firstRenderObject
(
find
.
byType
(
Container
)).
parent
;
final
RenderListWheelViewport
viewport
=
tester
.
firstRenderObject
(
find
.
byType
(
Container
)).
parent
.
parent
;
expect
(
viewport
,
paints
..
transform
(
matrix4:
equals
(<
dynamic
>[
...
...
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