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
3cd8c314
Unverified
Commit
3cd8c314
authored
Nov 01, 2019
by
LongCatIsLooong
Committed by
GitHub
Nov 01, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update CupertinoSlidingSegmentedControl control/feedback mechanism (#43932)
parent
a192e296
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
308 additions
and
371 deletions
+308
-371
sliding_segmented_control.dart
.../flutter/lib/src/cupertino/sliding_segmented_control.dart
+80
-105
sliding_segmented_control_test.dart
...lutter/test/cupertino/sliding_segmented_control_test.dart
+228
-266
No files found.
packages/flutter/lib/src/cupertino/sliding_segmented_control.dart
View file @
3cd8c314
...
...
@@ -94,56 +94,12 @@ class _FontWeightTween extends Tween<FontWeight> {
/// argument must be an ordered [Map] such as a [LinkedHashMap], the ordering of
/// the keys will determine the order of the widgets in the segmented control.
///
/// When the state of the segmented control changes, the widget changes the
/// [controller]'s value to the map key associated with the newly selected widget,
/// causing all of its listeners to be notified.
///
/// {@tool dartpad --template=stateful_widget_material}
///
/// This sample shows two [CupertinoSlidingSegmentedControl]s that mirror each other.
///
/// ```dart
/// final Map<int, Widget> children = const <int, Widget>{
/// 0: Text('Child 1'),
/// 1: Text('Child 2'),
/// 2: Text('Child 3'),
/// };
///
/// // No segment is initially selected because the controller's value is null.
/// final ValueNotifier<int> controller = ValueNotifier<int>(null);
///
/// @override
/// void initState() {
/// super.initState();
/// // Prints a message whenever the currently selected widget changes.
/// controller.addListener(() { print('selected: ${controller.value}'); });
/// }
///
/// @override
/// Widget build(BuildContext context) {
/// return Center(
/// child: Column(
/// children: <Widget>[
/// CupertinoSlidingSegmentedControl<int>(
/// children: children,
/// controller: controller,
/// ),
/// CupertinoSlidingSegmentedControl<int>(
/// children: children,
/// controller: controller,
/// ),
/// ],
/// ),
/// );
/// }
///
/// @override
/// void dispose() {
/// controller.dispose();
/// super.dispose();
/// }
/// ```
/// {@end-tool}
/// When the state of the segmented control changes, the widget calls the
/// [onValueChanged] callback. The map key associated with the newly selected
/// widget is returned in the [onValueChanged] callback. Typically, widgets
/// that use a segmented control will listen for the [onValueChanged] callback
/// and rebuild the segmented control with a new [groupValue] to update which
/// option is currently selected.
///
/// The [children] will be displayed in the order of the keys in the [Map].
/// The height of the segmented control is determined by the height of the
...
...
@@ -166,33 +122,34 @@ class _FontWeightTween extends Tween<FontWeight> {
class
CupertinoSlidingSegmentedControl
<
T
>
extends
StatefulWidget
{
/// Creates an iOS-style segmented control bar.
///
/// The [children] and [
controller] arguments must not be null. The [children]
///
argument must be an ordered [Map] such as a [LinkedHashMap]. Further, the
/// length of the [children] list must be greater than one.
/// The [children] and [
onValueChanged] arguments must not be null. The
///
[children] argument must be an ordered [Map] such as a [LinkedHashMap].
///
Further, the
length of the [children] list must be greater than one.
///
/// Each widget value in the map of [children] must have an associated
[Map]
key
///
of type [T] that uniquely identifies this widget. This key will become the
///
[controller]'s new value, when the corresponding child widget from the
///
[children] map
is selected.
/// Each widget value in the map of [children] must have an associated key
///
that uniquely identifies this widget. This key is what will be returned
///
in the [onValueChanged] callback when a new value from the [children] map
/// is selected.
///
/// The [
controller]'s [ValueNotifier.value] is the currently selected value for
///
the segmented control. If it is null, no widget will appear as selected. The
///
[controller]'s value must be either null or one of the keys in the [children]
/// map.
/// The [
groupValue] is the currently selected value for the segmented control.
///
If no [groupValue] is provided, or the [groupValue] is null, no widget will
///
appear as selected. The [groupValue] must be either null or one of the keys
///
in the [children]
map.
CupertinoSlidingSegmentedControl
({
Key
key
,
@required
this
.
children
,
@required
this
.
controller
,
@required
this
.
onValueChanged
,
this
.
groupValue
,
this
.
thumbColor
=
_kThumbColor
,
this
.
padding
=
_kHorizontalItemPadding
,
this
.
backgroundColor
=
CupertinoColors
.
tertiarySystemFill
,
})
:
assert
(
children
!=
null
),
assert
(
children
.
length
>=
2
),
assert
(
padding
!=
null
),
assert
(
controller
!=
null
),
assert
(
onValueChanged
!=
null
),
assert
(
controller
.
value
==
null
||
children
.
keys
.
any
((
T
child
)
=>
child
==
controller
.
v
alue
),
"The controller's value must be either null or one of the keys in the children map."
,
groupValue
==
null
||
children
.
keys
.
contains
(
groupV
alue
),
'The groupValue must be either null or one of the keys in the children map.'
,
),
super
(
key:
key
);
...
...
@@ -203,16 +160,58 @@ class CupertinoSlidingSegmentedControl<T> extends StatefulWidget {
/// This attribute must be an ordered [Map] such as a [LinkedHashMap].
final
Map
<
T
,
Widget
>
children
;
/// A [ValueNotifier]<[T]> that controls the currently selected child.
/// The identifier of the widget that is currently selected.
///
/// This must be one of the keys in the [Map] of [children].
/// If this attribute is null, no widget will be initially selected.
final
T
groupValue
;
/// The callback that is called when a new option is tapped.
///
/// This attribute must not be null.
///
/// The segmented control passes the newly selected widget's associated key
/// to the callback but does not actually change state until the parent
/// widget rebuilds the segmented control with the new [groupValue].
///
/// The callback provided to [onValueChanged] should update the state of
/// the parent [StatefulWidget] using the [State.setState] method, so that
/// the parent gets rebuilt; for example:
///
/// Its value must be one of the keys in the [Map] of [children], or null, in
/// which case no widget will be selected.
/// {@tool sample}
///
/// The [controller]'s value changes when the user drags the thumb to a different
/// child widget, or taps on a different child widget. Its value can also be
/// changed programmatically, in which case all sliding animations will play as
/// if the new selected child widget was tapped on.
final
ValueNotifier
<
T
>
controller
;
/// ```dart
/// class SegmentedControlExample extends StatefulWidget {
/// @override
/// State createState() => SegmentedControlExampleState();
/// }
///
/// class SegmentedControlExampleState extends State<SegmentedControlExample> {
/// final Map<int, Widget> children = const {
/// 0: Text('Child 1'),
/// 1: Text('Child 2'),
/// };
///
/// int currentValue;
///
/// @override
/// Widget build(BuildContext context) {
/// return Container(
/// child: CupertinoSlidingSegmentedControl<int>(
/// children: children,
/// onValueChanged: (int newValue) {
/// setState(() {
/// currentValue = newValue;
/// });
/// },
/// groupValue: currentValue,
/// ),
/// );
/// }
/// }
/// ```
/// {@end-tool}
final
ValueChanged
<
T
>
onValueChanged
;
/// The color used to paint the rounded rect behind the [children] and the separators.
///
...
...
@@ -240,7 +239,7 @@ class _SegmentedControlState<T> extends State<CupertinoSlidingSegmentedControl<T
with
TickerProviderStateMixin
<
CupertinoSlidingSegmentedControl
<
T
>>
{
final
Map
<
T
,
AnimationController
>
_highlightControllers
=
<
T
,
AnimationController
>{};
final
Tween
<
FontWeight
>
_highlightTween
=
_FontWeightTween
(
begin:
FontWeight
.
normal
,
end:
FontWeight
.
w
6
00
);
final
Tween
<
FontWeight
>
_highlightTween
=
_FontWeightTween
(
begin:
FontWeight
.
normal
,
end:
FontWeight
.
w
5
00
);
final
Map
<
T
,
AnimationController
>
_pressControllers
=
<
T
,
AnimationController
>{};
final
Tween
<
double
>
_pressTween
=
Tween
<
double
>(
begin:
1
,
end:
0.2
);
...
...
@@ -255,8 +254,6 @@ class _SegmentedControlState<T> extends State<CupertinoSlidingSegmentedControl<T
final
HorizontalDragGestureRecognizer
drag
=
HorizontalDragGestureRecognizer
();
final
LongPressGestureRecognizer
longPress
=
LongPressGestureRecognizer
();
ValueNotifier
<
T
>
controller
;
AnimationController
_createHighlightAnimationController
({
bool
isCompleted
=
false
})
{
return
AnimationController
(
duration:
_kHighlightAnimationDuration
,
...
...
@@ -284,9 +281,7 @@ class _SegmentedControlState<T> extends State<CupertinoSlidingSegmentedControl<T
drag
.
team
=
team
;
team
.
captain
=
drag
;
controller
=
widget
.
controller
;
controller
.
addListener
(
_didChangeControllerValue
);
_highlighted
=
controller
.
value
;
_highlighted
=
widget
.
groupValue
;
thumbController
=
AnimationController
(
duration:
_kSpringAnimationDuration
,
...
...
@@ -308,7 +303,7 @@ class _SegmentedControlState<T> extends State<CupertinoSlidingSegmentedControl<T
for
(
T
currentKey
in
widget
.
children
.
keys
)
{
_highlightControllers
[
currentKey
]
=
_createHighlightAnimationController
(
isCompleted:
currentKey
==
controller
.
v
alue
,
// Highlight the current selection.
isCompleted:
currentKey
==
widget
.
groupV
alue
,
// Highlight the current selection.
);
_pressControllers
[
currentKey
]
=
_createFadeoutAnimationController
();
}
...
...
@@ -336,15 +331,7 @@ class _SegmentedControlState<T> extends State<CupertinoSlidingSegmentedControl<T
}
}
if
(
controller
!=
widget
.
controller
)
{
controller
.
removeListener
(
_didChangeControllerValue
);
controller
=
widget
.
controller
;
controller
.
addListener
(
_didChangeControllerValue
);
}
if
(
controller
.
value
!=
oldWidget
.
controller
.
value
)
{
highlighted
=
widget
.
controller
.
value
;
}
highlighted
=
widget
.
groupValue
;
}
@override
...
...
@@ -368,18 +355,6 @@ class _SegmentedControlState<T> extends State<CupertinoSlidingSegmentedControl<T
super
.
dispose
();
}
void
_didChangeControllerValue
()
{
assert
(
controller
.
value
==
null
||
widget
.
children
.
keys
.
contains
(
controller
.
value
),
"The controller's value
${controller.value}
must be either null "
'or one of the keys in the children map:
${widget.children.keys}
'
,
);
setState
(()
{
// Mark the state as dirty.
});
}
// Play highlight animation for the child located at _highlightControllers[at].
void
_animateHighlightController
({
T
at
,
bool
forward
})
{
if
(
at
==
null
)
...
...
@@ -413,7 +388,7 @@ class _SegmentedControlState<T> extends State<CupertinoSlidingSegmentedControl<T
}
void
didChangeSelectedViaGesture
()
{
controller
.
value
=
_highlighted
;
widget
.
onValueChanged
(
_highlighted
)
;
}
T
indexToKey
(
int
index
)
=>
index
==
null
?
null
:
keys
[
index
];
...
...
@@ -447,9 +422,9 @@ class _SegmentedControlState<T> extends State<CupertinoSlidingSegmentedControl<T
style:
textStyle
,
child:
Semantics
(
button:
true
,
onTap:
()
{
controller
.
value
=
currentKey
;
},
onTap:
()
{
widget
.
onValueChanged
(
currentKey
)
;
},
inMutuallyExclusiveGroup:
true
,
selected:
controller
.
v
alue
==
currentKey
,
selected:
widget
.
groupV
alue
==
currentKey
,
child:
Opacity
(
opacity:
_pressTween
.
evaluate
(
_pressControllers
[
currentKey
]),
// Expand the hitTest area to be as large as the Opacity widget.
...
...
@@ -464,7 +439,7 @@ class _SegmentedControlState<T> extends State<CupertinoSlidingSegmentedControl<T
children
.
add
(
child
);
}
final
int
selectedIndex
=
controller
.
value
==
null
?
null
:
keys
.
indexOf
(
controller
.
v
alue
);
final
int
selectedIndex
=
widget
.
groupValue
==
null
?
null
:
keys
.
indexOf
(
widget
.
groupV
alue
);
final
Widget
box
=
_SegmentedControlRenderWidget
<
T
>(
children:
children
,
...
...
packages/flutter/test/cupertino/sliding_segmented_control_test.dart
View file @
3cd8c314
...
...
@@ -36,32 +36,51 @@ Widget setupSimpleSegmentedControl() {
0
:
Text
(
'Child 1'
),
1
:
Text
(
'Child 2'
),
};
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
0
);
return
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
controller:
controller
,
),
builder:
(
BuildContext
context
)
{
return
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
);
},
);
}
Widget
boilerplate
(
{
Widget
child
})
{
StateSetter
setState
;
int
groupValue
=
0
;
void
defaultCallback
(
int
newValue
)
{
setState
(()
{
groupValue
=
newValue
;
});
}
Widget
boilerplate
(
{
WidgetBuilder
builder
})
{
return
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Center
(
child:
child
),
child:
Center
(
child:
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setter
)
{
setState
=
setter
;
return
builder
(
context
);
}),
),
);
}
void
main
(
)
{
testWidgets
(
'Children and controller and padding arguments can not be null'
,
(
WidgetTester
tester
)
async
{
setUp
(()
{
setState
=
null
;
groupValue
=
0
;
});
testWidgets
(
'Children and onValueChanged and padding arguments can not be null'
,
(
WidgetTester
tester
)
async
{
groupValue
=
null
;
try
{
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
children:
null
,
controller:
ValueNotifier
<
int
>(
null
),
),
CupertinoSlidingSegmentedControl
<
int
>(
children:
null
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
),
);
fail
(
'Should not be possible to create segmented control with null children'
);
...
...
@@ -76,26 +95,24 @@ void main() {
try
{
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
controller:
null
,
),
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
groupValue:
groupValue
,
onValueChanged:
null
,
),
);
fail
(
'Should not be possible to create segmented control without a
controller
'
);
fail
(
'Should not be possible to create segmented control without a
n onValueChanged
'
);
}
on
AssertionError
catch
(
e
)
{
expect
(
e
.
toString
(),
contains
(
'
controller
'
));
expect
(
e
.
toString
(),
contains
(
'
onValueChanged
'
));
}
try
{
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
controller:
ValueNotifier
<
int
>(
null
),
padding:
null
,
),
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
padding:
null
,
),
);
fail
(
'Should not be possible to create segmented control with null padding'
);
...
...
@@ -106,13 +123,13 @@ void main() {
testWidgets
(
'Need at least 2 children'
,
(
WidgetTester
tester
)
async
{
final
Map
<
int
,
Widget
>
children
=
<
int
,
Widget
>{};
groupValue
=
null
;
try
{
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
controller:
ValueNotifier
<
int
>(
null
),
),
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
),
);
fail
(
'Should not be possible to create a segmented control with no children'
);
...
...
@@ -123,11 +140,10 @@ void main() {
children
[
0
]
=
const
Text
(
'Child 1'
);
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
controller:
ValueNotifier
<
int
>(
null
),
),
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
),
);
fail
(
'Should not be possible to create a segmented control with just one child'
);
...
...
@@ -135,20 +151,20 @@ void main() {
expect
(
e
.
toString
(),
contains
(
'children.length'
));
}
groupValue
=
-
1
;
try
{
children
[
1
]
=
const
Text
(
'Child 2'
);
children
[
2
]
=
const
Text
(
'Child 3'
);
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
controller:
ValueNotifier
<
int
>(-
1
),
),
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
),
);
fail
(
'Should not be possible to create a segmented control with a
controller
pointing to a non-existent child'
);
fail
(
'Should not be possible to create a segmented control with a
groupValue
pointing to a non-existent child'
);
}
on
AssertionError
catch
(
e
)
{
expect
(
e
.
toString
(),
contains
(
'
v
alue must be either null or one of the keys in the children map'
));
expect
(
e
.
toString
(),
contains
(
'
groupV
alue must be either null or one of the keys in the children map'
));
}
});
...
...
@@ -185,11 +201,14 @@ void main() {
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
key:
key
,
children:
children
,
controller:
ValueNotifier
<
int
>(
null
),
),
builder:
(
BuildContext
context
)
{
return
CupertinoSlidingSegmentedControl
<
int
>(
key:
key
,
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
);
},
),
);
...
...
@@ -204,12 +223,15 @@ void main() {
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
key:
key
,
padding:
const
EdgeInsets
.
fromLTRB
(
1
,
3
,
5
,
7
),
children:
children
,
controller:
ValueNotifier
<
int
>(
null
),
),
builder:
(
BuildContext
context
)
{
return
CupertinoSlidingSegmentedControl
<
int
>(
key:
key
,
padding:
const
EdgeInsets
.
fromLTRB
(
1
,
3
,
5
,
7
),
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
);
},
),
);
...
...
@@ -230,119 +252,29 @@ void main() {
2
:
Text
(
'Child 3'
),
};
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
0
);
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
controller:
controller
,
),
builder:
(
BuildContext
context
)
{
return
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
);
},
),
);
expect
(
controller
.
v
alue
,
0
);
expect
(
groupV
alue
,
0
);
await
tester
.
tap
(
find
.
text
(
'Child 2'
));
expect
(
controller
.
value
,
1
);
// Tapping the currently selected item should not change controller's value.
bool
valueChanged
=
false
;
controller
.
addListener
(()
{
valueChanged
=
true
;
});
expect
(
groupValue
,
1
);
// Tapping the currently selected item should not change groupValue.
await
tester
.
tap
(
find
.
text
(
'Child 2'
));
expect
(
valueChanged
,
isFalse
);
expect
(
controller
.
value
,
1
);
});
testWidgets
(
'Changing controller works'
,
(
WidgetTester
tester
)
async
{
const
Map
<
int
,
Widget
>
children
=
<
int
,
Widget
>{
0
:
Text
(
'Child 1'
),
1
:
Text
(
'Child 2'
),
2
:
Text
(
'Child 3'
),
};
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
0
);
final
ValueNotifier
<
int
>
newControlelr
=
ValueNotifier
<
int
>(
null
);
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
controller:
controller
,
),
),
);
expect
(
currentUnscaledThumbRect
(
tester
,
useGlobalCoordinate:
true
).
center
,
offsetMoreOrLessEquals
(
tester
.
getCenter
(
find
.
text
(
'Child 1'
))),
);
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
controller:
newControlelr
,
),
),
);
expect
(
currentUnscaledThumbRect
(
tester
,
useGlobalCoordinate:
true
),
isNull
,
);
});
testWidgets
(
'Can change controller value in build method'
,
(
WidgetTester
tester
)
async
{
const
Map
<
int
,
Widget
>
children
=
<
int
,
Widget
>{
0
:
Text
(
'Child 1'
),
1
:
Text
(
'Child 2'
),
2
:
Text
(
'Child 3'
),
};
int
currentIndex
=
0
;
StateSetter
setState
;
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
currentIndex
);
await
tester
.
pumpWidget
(
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setter
)
{
setState
=
setter
;
if
(
controller
.
value
!=
currentIndex
)
controller
.
value
=
currentIndex
;
return
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
controller:
controller
,
),
);
},
),
);
expect
(
currentUnscaledThumbRect
(
tester
,
useGlobalCoordinate:
true
).
center
,
offsetMoreOrLessEquals
(
tester
.
getCenter
(
find
.
text
(
'Child 1'
))),
);
setState
(()
{
currentIndex
=
2
;
});
await
tester
.
pump
();
await
tester
.
pumpAndSettle
();
expect
(
currentUnscaledThumbRect
(
tester
,
useGlobalCoordinate:
true
).
center
,
offsetMoreOrLessEquals
(
tester
.
getCenter
(
find
.
text
(
'Child 3'
)),
epsilon:
0.01
),
);
expect
(
groupValue
,
1
);
});
testWidgets
(
...
...
@@ -353,16 +285,15 @@ void main() {
1
:
Icon
(
IconData
(
1
)),
};
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
0
);
await
tester
.
pumpWidget
(
CupertinoApp
(
theme:
const
CupertinoThemeData
(
brightness:
Brightness
.
dark
),
home:
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
home:
boilerplate
(
builder:
(
BuildContext
context
)
{
return
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
controller:
controller
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
);
},
),
...
...
@@ -371,7 +302,7 @@ void main() {
DefaultTextStyle
textStyle
=
tester
.
widget
(
find
.
widgetWithText
(
DefaultTextStyle
,
'Child 1'
).
first
);
expect
(
textStyle
.
style
.
fontWeight
,
FontWeight
.
w
6
00
);
expect
(
textStyle
.
style
.
fontWeight
,
FontWeight
.
w
5
00
);
await
tester
.
tap
(
find
.
byIcon
(
const
IconData
(
1
)));
await
tester
.
pump
();
...
...
@@ -379,6 +310,7 @@ void main() {
textStyle
=
tester
.
widget
(
find
.
widgetWithText
(
DefaultTextStyle
,
'Child 1'
).
first
);
expect
(
groupValue
,
1
);
expect
(
textStyle
.
style
.
fontWeight
,
FontWeight
.
normal
);
},
);
...
...
@@ -389,7 +321,6 @@ void main() {
1
:
Icon
(
IconData
(
1
)),
};
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
0
);
Brightness
brightness
=
Brightness
.
light
;
StateSetter
setState
;
...
...
@@ -400,12 +331,15 @@ void main() {
return
MediaQuery
(
data:
MediaQueryData
(
platformBrightness:
brightness
),
child:
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
controller:
controller
,
thumbColor:
CupertinoColors
.
systemGreen
,
backgroundColor:
CupertinoColors
.
systemRed
,
),
builder:
(
BuildContext
context
)
{
return
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
thumbColor:
CupertinoColors
.
systemGreen
,
backgroundColor:
CupertinoColors
.
systemRed
,
);
},
),
);
},
...
...
@@ -443,14 +377,15 @@ void main() {
2
:
Placeholder
(),
};
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
0
);
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
controller:
controller
,
),
builder:
(
BuildContext
context
)
{
return
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
);
},
),
);
},
...
...
@@ -468,16 +403,18 @@ void main() {
1
:
Text
(
'Child 2'
),
};
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
null
);
groupValue
=
null
;
await
tester
.
pumpWidget
(
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
controller:
controller
,
),
builder:
(
BuildContext
context
)
{
return
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
);
},
);
},
),
...
...
@@ -496,14 +433,17 @@ void main() {
};
// Child 3 is intially selected.
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
2
)
;
groupValue
=
2
;
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
controller:
controller
,
),
builder:
(
BuildContext
context
)
{
return
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
);
},
),
);
...
...
@@ -571,15 +511,16 @@ void main() {
2
:
Container
(
constraints:
const
BoxConstraints
.
tightFor
(
height:
200.0
)),
};
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
null
);
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
controller:
controller
,
),
builder:
(
BuildContext
context
)
{
return
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
);
},
),
);
...
...
@@ -600,14 +541,16 @@ void main() {
2
:
Container
(
constraints:
const
BoxConstraints
.
tightFor
(
width:
200.0
)),
};
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
null
);
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
controller:
controller
,
),
builder:
(
BuildContext
context
)
{
return
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
);
},
),
);
...
...
@@ -628,19 +571,20 @@ void main() {
1
:
SizedBox
(
width:
70
),
};
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
null
);
await
tester
.
pumpWidget
(
boilerplate
(
child:
Row
(
children:
<
Widget
>[
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
controller:
controller
,
),
],
),
builder:
(
BuildContext
context
)
{
return
Row
(
children:
<
Widget
>[
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
),
],
);
},
),
);
...
...
@@ -660,14 +604,19 @@ void main() {
1
:
Text
(
'Child 2'
),
};
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
null
);
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
rtl
,
child:
Center
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
controller:
controller
,
child:
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setter
)
{
setState
=
setter
;
return
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
);
},
),
),
),
...
...
@@ -682,16 +631,19 @@ void main() {
0
:
Text
(
'Child 1'
),
1
:
Text
(
'Child 2'
),
};
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
0
);
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
rtl
,
child:
Center
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
controller:
controller
,
child:
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setter
)
{
setState
=
setter
;
return
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
);
},
),
),
),
...
...
@@ -718,17 +670,15 @@ void main() {
1
:
Text
(
'Child 2'
),
};
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
0
);
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Center
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
boilerplate
(
builder:
(
BuildContext
context
)
{
return
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
controller:
controller
,
),
),
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
);
},
),
);
...
...
@@ -810,25 +760,26 @@ void main() {
children
[
0
]
=
const
Text
(
'Child 1'
);
children
[
1
]
=
const
SizedBox
();
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
0
);
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
controller:
controller
,
),
builder:
(
BuildContext
context
)
{
return
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
);
},
),
);
expect
(
controller
.
v
alue
,
0
);
expect
(
groupV
alue
,
0
);
final
Offset
centerOfTwo
=
tester
.
getCenter
(
find
.
byWidget
(
children
[
1
]));
// Tap just inside segment bounds
await
tester
.
tapAt
(
centerOfTwo
+
const
Offset
(
10
,
0
));
expect
(
controller
.
v
alue
,
1
);
expect
(
groupV
alue
,
1
);
});
testWidgets
(
'Thumb animation is correct when the selected segment changes'
,
(
WidgetTester
tester
)
async
{
...
...
@@ -917,20 +868,23 @@ void main() {
1
:
Text
(
'B'
),
2
:
Text
(
'C'
),
};
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
0
);
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
controller:
controller
,
),
builder:
(
BuildContext
context
)
{
return
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
);
},
),
);
await
tester
.
tap
(
find
.
text
(
'B'
));
await
tester
.
pump
();
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
40
));
// Between A and B.
...
...
@@ -965,15 +919,16 @@ void main() {
children
[
2
]
=
const
Text
(
'C'
);
children
[
3
]
=
const
Text
(
'D'
);
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
0
);
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
controller:
controller
,
),
builder:
(
BuildContext
context
)
{
return
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
);
},
),
);
...
...
@@ -984,11 +939,14 @@ void main() {
children
[
1
]
=
const
Text
(
'B'
);
await
tester
.
pumpWidget
(
boilerplate
(
child:
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
controller:
controller
,
),
builder:
(
BuildContext
context
)
{
return
CupertinoSlidingSegmentedControl
<
int
>(
key:
const
ValueKey
<
String
>(
'Segmented Control'
),
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
);
},
),
);
...
...
@@ -1005,7 +963,6 @@ void main() {
0
:
Text
(
'Child 1'
),
1
:
Text
(
'Child 2'
),
};
final
ValueNotifier
<
int
>
controller
=
ValueNotifier
<
int
>(
0
);
final
ScrollController
scrollController
=
ScrollController
();
await
tester
.
pumpWidget
(
...
...
@@ -1015,9 +972,14 @@ void main() {
controller:
scrollController
,
children:
<
Widget
>[
const
SizedBox
(
height:
100
),
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
controller:
controller
,
boilerplate
(
builder:
(
BuildContext
context
)
{
return
CupertinoSlidingSegmentedControl
<
int
>(
children:
children
,
groupValue:
groupValue
,
onValueChanged:
defaultCallback
,
);
},
),
const
SizedBox
(
height:
1000
),
],
...
...
@@ -1029,7 +991,7 @@ void main() {
await
tester
.
tap
(
find
.
text
(
'Child 2'
));
await
tester
.
pump
();
expect
(
controller
.
v
alue
,
1
);
expect
(
groupV
alue
,
1
);
// Vertical drag works for the scroll view.
final
TestGesture
gesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
text
(
'Child 1'
)));
...
...
@@ -1043,14 +1005,14 @@ void main() {
expect
(
scrollController
.
offset
,
100
);
// Does not affect the segmented control.
expect
(
controller
.
v
alue
,
1
);
expect
(
groupV
alue
,
1
);
await
gesture
.
moveBy
(
const
Offset
(
0
,
100
));
await
gesture
.
up
();
await
tester
.
pump
();
expect
(
scrollController
.
offset
,
0
);
expect
(
controller
.
v
alue
,
1
);
expect
(
groupV
alue
,
1
);
// Long press vertical drag is recognized by the segmented control.
await
gesture
.
down
(
tester
.
getCenter
(
find
.
text
(
'Child 1'
)));
...
...
@@ -1061,7 +1023,7 @@ void main() {
// Should not scroll.
expect
(
scrollController
.
offset
,
0
);
expect
(
controller
.
v
alue
,
1
);
expect
(
groupV
alue
,
1
);
await
gesture
.
moveBy
(
const
Offset
(
0
,
100
));
await
gesture
.
moveBy
(
const
Offset
(
0
,
100
));
...
...
@@ -1069,7 +1031,7 @@ void main() {
await
tester
.
pump
();
expect
(
scrollController
.
offset
,
0
);
expect
(
controller
.
v
alue
,
0
);
expect
(
groupV
alue
,
0
);
// Horizontal drag is recognized by the segmentedControl.
await
gesture
.
down
(
tester
.
getCenter
(
find
.
text
(
'Child 1'
)));
...
...
@@ -1079,6 +1041,6 @@ void main() {
await
tester
.
pump
();
expect
(
scrollController
.
offset
,
0
);
expect
(
controller
.
v
alue
,
1
);
expect
(
groupV
alue
,
1
);
});
}
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