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
516ac574
Commit
516ac574
authored
Nov 22, 2016
by
Hans Muller
Committed by
GitHub
Nov 22, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Allow null DropdownButton values (#6971)
parent
23f269d8
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
111 additions
and
26 deletions
+111
-26
drop_down.dart
packages/flutter/lib/src/material/drop_down.dart
+19
-12
stack.dart
packages/flutter/lib/src/rendering/stack.dart
+6
-8
basic.dart
packages/flutter/lib/src/widgets/basic.dart
+5
-4
drop_down_test.dart
packages/flutter/test/material/drop_down_test.dart
+54
-2
stack_test.dart
packages/flutter/test/widgets/stack_test.dart
+27
-0
No files found.
packages/flutter/lib/src/material/drop_down.dart
View file @
516ac574
...
@@ -52,8 +52,9 @@ class _DropdownMenuPainter extends CustomPainter {
...
@@ -52,8 +52,9 @@ class _DropdownMenuPainter extends CustomPainter {
@override
@override
void
paint
(
Canvas
canvas
,
Size
size
)
{
void
paint
(
Canvas
canvas
,
Size
size
)
{
final
double
selectedItemOffset
=
selectedIndex
*
_kMenuItemHeight
+
_kMenuVerticalPadding
.
top
;
final
Tween
<
double
>
top
=
new
Tween
<
double
>(
final
Tween
<
double
>
top
=
new
Tween
<
double
>(
begin:
(
selectedIndex
*
_kMenuItemHeight
+
_kMenuVerticalPadding
.
top
)
.
clamp
(
0.0
,
size
.
height
-
_kMenuItemHeight
),
begin:
selectedItemOffset
.
clamp
(
0.0
,
size
.
height
-
_kMenuItemHeight
),
end:
0.0
end:
0.0
);
);
...
@@ -411,14 +412,14 @@ class DropdownButtonHideUnderline extends InheritedWidget {
...
@@ -411,14 +412,14 @@ class DropdownButtonHideUnderline extends InheritedWidget {
class
DropdownButton
<
T
>
extends
StatefulWidget
{
class
DropdownButton
<
T
>
extends
StatefulWidget
{
/// Creates a dropdown button.
/// Creates a dropdown button.
///
///
/// The [items] must have distinct values and
[value]
must be among them.
/// The [items] must have distinct values and
if [value] isn't null it
must be among them.
///
///
/// The [elevation] and [iconSize] arguments must not be null (they both have
/// The [elevation] and [iconSize] arguments must not be null (they both have
/// defaults, so do not need to be specified).
/// defaults, so do not need to be specified).
DropdownButton
({
DropdownButton
({
Key
key
,
Key
key
,
@required
this
.
items
,
@required
this
.
items
,
@required
this
.
value
,
this
.
value
,
@required
this
.
onChanged
,
@required
this
.
onChanged
,
this
.
elevation
:
8
,
this
.
elevation
:
8
,
this
.
style
,
this
.
style
,
...
@@ -426,13 +427,16 @@ class DropdownButton<T> extends StatefulWidget {
...
@@ -426,13 +427,16 @@ class DropdownButton<T> extends StatefulWidget {
this
.
isDense
:
false
,
this
.
isDense
:
false
,
})
:
super
(
key:
key
)
{
})
:
super
(
key:
key
)
{
assert
(
items
!=
null
);
assert
(
items
!=
null
);
assert
(
items
.
where
((
DropdownMenuItem
<
T
>
item
)
=>
item
.
value
==
value
).
length
==
1
);
assert
(
value
==
null
||
items
.
where
((
DropdownMenuItem
<
T
>
item
)
=>
item
.
value
==
value
).
length
==
1
);
}
}
/// The list of possible items to select among.
/// The list of possible items to select among.
final
List
<
DropdownMenuItem
<
T
>>
items
;
final
List
<
DropdownMenuItem
<
T
>>
items
;
/// The currently selected item.
/// The currently selected item, or null if no item has been selected. If
/// value is null then the menu is popped up as if the first item was
/// selected.
final
T
value
;
final
T
value
;
/// Called when the user selects an item.
/// Called when the user selects an item.
...
@@ -470,22 +474,23 @@ class DropdownButton<T> extends StatefulWidget {
...
@@ -470,22 +474,23 @@ class DropdownButton<T> extends StatefulWidget {
}
}
class
_DropdownButtonState
<
T
>
extends
State
<
DropdownButton
<
T
>>
{
class
_DropdownButtonState
<
T
>
extends
State
<
DropdownButton
<
T
>>
{
int
_selectedIndex
;
@override
@override
void
initState
()
{
void
initState
()
{
super
.
initState
();
super
.
initState
();
_updateSelectedIndex
();
_updateSelectedIndex
();
assert
(
_selectedIndex
!=
null
);
}
}
@override
@override
void
didUpdateConfig
(
DropdownButton
<
T
>
oldConfig
)
{
void
didUpdateConfig
(
DropdownButton
<
T
>
oldConfig
)
{
if
(
config
.
items
[
_selectedIndex
].
value
!=
config
.
value
)
_updateSelectedIndex
();
_updateSelectedIndex
();
}
}
int
_selectedIndex
;
void
_updateSelectedIndex
()
{
void
_updateSelectedIndex
()
{
assert
(
config
.
value
==
null
||
config
.
items
.
where
((
DropdownMenuItem
<
T
>
item
)
=>
item
.
value
==
config
.
value
).
length
==
1
);
_selectedIndex
=
null
;
for
(
int
itemIndex
=
0
;
itemIndex
<
config
.
items
.
length
;
itemIndex
++)
{
for
(
int
itemIndex
=
0
;
itemIndex
<
config
.
items
.
length
;
itemIndex
++)
{
if
(
config
.
items
[
itemIndex
].
value
==
config
.
value
)
{
if
(
config
.
items
[
itemIndex
].
value
==
config
.
value
)
{
_selectedIndex
=
itemIndex
;
_selectedIndex
=
itemIndex
;
...
@@ -502,7 +507,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> {
...
@@ -502,7 +507,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> {
Navigator
.
push
(
context
,
new
_DropdownRoute
<
T
>(
Navigator
.
push
(
context
,
new
_DropdownRoute
<
T
>(
items:
config
.
items
,
items:
config
.
items
,
buttonRect:
_kMenuHorizontalPadding
.
inflateRect
(
itemRect
),
buttonRect:
_kMenuHorizontalPadding
.
inflateRect
(
itemRect
),
selectedIndex:
_selectedIndex
,
selectedIndex:
_selectedIndex
??
0
,
elevation:
config
.
elevation
,
elevation:
config
.
elevation
,
theme:
Theme
.
of
(
context
,
shadowThemeOnly:
true
),
theme:
Theme
.
of
(
context
,
shadowThemeOnly:
true
),
style:
_textStyle
,
style:
_textStyle
,
...
@@ -533,10 +538,12 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> {
...
@@ -533,10 +538,12 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> {
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
mainAxisSize:
MainAxisSize
.
min
,
mainAxisSize:
MainAxisSize
.
min
,
children:
<
Widget
>[
children:
<
Widget
>[
// The button's size is defined by its largest menu item. If value is
// null then an item does not appear.
new
IndexedStack
(
new
IndexedStack
(
index:
_selectedIndex
,
index:
_selectedIndex
,
alignment:
FractionalOffset
.
centerLeft
,
alignment:
FractionalOffset
.
centerLeft
,
children:
config
.
items
children:
config
.
items
,
),
),
new
Icon
(
Icons
.
arrow_drop_down
,
new
Icon
(
Icons
.
arrow_drop_down
,
size:
config
.
iconSize
,
size:
config
.
iconSize
,
...
...
packages/flutter/lib/src/rendering/stack.dart
View file @
516ac574
...
@@ -454,7 +454,7 @@ class RenderStack extends RenderBox
...
@@ -454,7 +454,7 @@ class RenderStack extends RenderBox
class
RenderIndexedStack
extends
RenderStack
{
class
RenderIndexedStack
extends
RenderStack
{
/// Creates a stack render object that paints a single child.
/// Creates a stack render object that paints a single child.
///
///
///
The [index] argument must not be null
.
///
If the [index] parameter is null, nothing is displayed
.
RenderIndexedStack
({
RenderIndexedStack
({
List
<
RenderBox
>
children
,
List
<
RenderBox
>
children
,
FractionalOffset
alignment:
FractionalOffset
.
topLeft
,
FractionalOffset
alignment:
FractionalOffset
.
topLeft
,
...
@@ -462,15 +462,12 @@ class RenderIndexedStack extends RenderStack {
...
@@ -462,15 +462,12 @@ class RenderIndexedStack extends RenderStack {
})
:
_index
=
index
,
super
(
})
:
_index
=
index
,
super
(
children:
children
,
children:
children
,
alignment:
alignment
alignment:
alignment
)
{
);
assert
(
index
!=
null
);
}
/// The index of the child to show.
/// The index of the child to show
, null if nothing is to be displayed
.
int
get
index
=>
_index
;
int
get
index
=>
_index
;
int
_index
;
int
_index
;
set
index
(
int
value
)
{
set
index
(
int
value
)
{
assert
(
value
!=
null
);
if
(
_index
!=
value
)
{
if
(
_index
!=
value
)
{
_index
=
value
;
_index
=
value
;
markNeedsLayout
();
markNeedsLayout
();
...
@@ -478,6 +475,7 @@ class RenderIndexedStack extends RenderStack {
...
@@ -478,6 +475,7 @@ class RenderIndexedStack extends RenderStack {
}
}
RenderBox
_childAtIndex
()
{
RenderBox
_childAtIndex
()
{
assert
(
index
!=
null
);
RenderBox
child
=
firstChild
;
RenderBox
child
=
firstChild
;
int
i
=
0
;
int
i
=
0
;
while
(
child
!=
null
&&
i
<
index
)
{
while
(
child
!=
null
&&
i
<
index
)
{
...
@@ -492,7 +490,7 @@ class RenderIndexedStack extends RenderStack {
...
@@ -492,7 +490,7 @@ class RenderIndexedStack extends RenderStack {
@override
@override
bool
hitTestChildren
(
HitTestResult
result
,
{
Point
position
})
{
bool
hitTestChildren
(
HitTestResult
result
,
{
Point
position
})
{
if
(
firstChild
==
null
)
if
(
firstChild
==
null
||
index
==
null
)
return
false
;
return
false
;
assert
(
position
!=
null
);
assert
(
position
!=
null
);
RenderBox
child
=
_childAtIndex
();
RenderBox
child
=
_childAtIndex
();
...
@@ -504,7 +502,7 @@ class RenderIndexedStack extends RenderStack {
...
@@ -504,7 +502,7 @@ class RenderIndexedStack extends RenderStack {
@override
@override
void
paintStack
(
PaintingContext
context
,
Offset
offset
)
{
void
paintStack
(
PaintingContext
context
,
Offset
offset
)
{
if
(
firstChild
==
null
)
if
(
firstChild
==
null
||
index
==
null
)
return
;
return
;
RenderBox
child
=
_childAtIndex
();
RenderBox
child
=
_childAtIndex
();
final
StackParentData
childParentData
=
child
.
parentData
;
final
StackParentData
childParentData
=
child
.
parentData
;
...
...
packages/flutter/lib/src/widgets/basic.dart
View file @
516ac574
...
@@ -1534,7 +1534,10 @@ class Stack extends MultiChildRenderObjectWidget {
...
@@ -1534,7 +1534,10 @@ class Stack extends MultiChildRenderObjectWidget {
/// A [Stack] that shows a single child from a list of children.
/// A [Stack] that shows a single child from a list of children.
///
///
/// The displayed child is the one with the given [index].
/// The displayed child is the one with the given [index]. The stack is
/// always as big as the largest child.
///
/// If value is null, then nothing is displayed.
///
///
/// For more details, see [Stack].
/// For more details, see [Stack].
class
IndexedStack
extends
Stack
{
class
IndexedStack
extends
Stack
{
...
@@ -1546,9 +1549,7 @@ class IndexedStack extends Stack {
...
@@ -1546,9 +1549,7 @@ class IndexedStack extends Stack {
FractionalOffset
alignment:
FractionalOffset
.
topLeft
,
FractionalOffset
alignment:
FractionalOffset
.
topLeft
,
this
.
index
:
0
,
this
.
index
:
0
,
List
<
Widget
>
children:
const
<
Widget
>[],
List
<
Widget
>
children:
const
<
Widget
>[],
})
:
super
(
key:
key
,
alignment:
alignment
,
children:
children
)
{
})
:
super
(
key:
key
,
alignment:
alignment
,
children:
children
);
assert
(
index
!=
null
);
}
/// The index of the child to show.
/// The index of the child to show.
final
int
index
;
final
int
index
;
...
...
packages/flutter/test/material/drop_down_test.dart
View file @
516ac574
...
@@ -7,8 +7,9 @@ import 'dart:math' as math;
...
@@ -7,8 +7,9 @@ import 'dart:math' as math;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/material.dart'
;
final
List
<
String
>
menuItems
=
<
String
>[
'one'
,
'two'
,
'three'
,
'four'
];
Widget
buildFrame
(
{
Key
buttonKey
,
String
value:
'two'
,
ValueChanged
<
String
>
onChanged
,
bool
isDense:
false
})
{
Widget
buildFrame
(
{
Key
buttonKey
,
String
value:
'two'
,
ValueChanged
<
String
>
onChanged
,
bool
isDense:
false
})
{
final
List
<
String
>
items
=
<
String
>[
'one'
,
'two'
,
'three'
,
'four'
];
return
new
MaterialApp
(
return
new
MaterialApp
(
home:
new
Material
(
home:
new
Material
(
child:
new
Center
(
child:
new
Center
(
...
@@ -17,7 +18,7 @@ Widget buildFrame({ Key buttonKey, String value: 'two', ValueChanged<String> on
...
@@ -17,7 +18,7 @@ Widget buildFrame({ Key buttonKey, String value: 'two', ValueChanged<String> on
value:
value
,
value:
value
,
onChanged:
onChanged
,
onChanged:
onChanged
,
isDense:
isDense
,
isDense:
isDense
,
items:
i
tems
.
map
((
String
item
)
{
items:
menuI
tems
.
map
((
String
item
)
{
return
new
DropdownMenuItem
<
String
>(
return
new
DropdownMenuItem
<
String
>(
key:
new
ValueKey
<
String
>(
item
),
key:
new
ValueKey
<
String
>(
item
),
value:
item
,
value:
item
,
...
@@ -265,4 +266,55 @@ void main() {
...
@@ -265,4 +266,55 @@ void main() {
// should have the same size and location.
// should have the same size and location.
checkSelectedItemTextGeometry
(
tester
,
'two'
);
checkSelectedItemTextGeometry
(
tester
,
'two'
);
});
});
testWidgets
(
'Size of DropdownButton with null value'
,
(
WidgetTester
tester
)
async
{
Key
buttonKey
=
new
UniqueKey
();
String
value
;
Widget
build
()
=>
buildFrame
(
buttonKey:
buttonKey
,
value:
value
);
await
tester
.
pumpWidget
(
build
());
RenderBox
buttonBoxNullValue
=
tester
.
renderObject
(
find
.
byKey
(
buttonKey
));
assert
(
buttonBoxNullValue
.
attached
);
value
=
'three'
;
await
tester
.
pumpWidget
(
build
());
RenderBox
buttonBox
=
tester
.
renderObject
(
find
.
byKey
(
buttonKey
));
assert
(
buttonBox
.
attached
);
// A DropDown button with a null value should be the same size as a
// one with a non-null value.
expect
(
buttonBox
.
localToGlobal
(
Point
.
origin
),
equals
(
buttonBoxNullValue
.
localToGlobal
(
Point
.
origin
)));
expect
(
buttonBox
.
size
,
equals
(
buttonBoxNullValue
.
size
));
});
testWidgets
(
'Layout of a DropdownButton with null value'
,
(
WidgetTester
tester
)
async
{
Key
buttonKey
=
new
UniqueKey
();
String
value
;
void
onChanged
(
String
newValue
)
{
value
=
newValue
;
}
Widget
build
()
=>
buildFrame
(
buttonKey:
buttonKey
,
value:
value
,
onChanged:
onChanged
);
await
tester
.
pumpWidget
(
build
());
RenderBox
buttonBox
=
tester
.
renderObject
(
find
.
byKey
(
buttonKey
));
assert
(
buttonBox
.
attached
);
// Show the menu.
await
tester
.
tap
(
find
.
byKey
(
buttonKey
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
// finish the menu animation
// Tap on item 'one', which must appear over the button.
await
tester
.
tap
(
find
.
byKey
(
buttonKey
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
// finish the menu animation
await
tester
.
pumpWidget
(
build
());
expect
(
value
,
equals
(
'one'
));
});
}
}
packages/flutter/test/widgets/stack_test.dart
View file @
516ac574
...
@@ -262,6 +262,33 @@ void main() {
...
@@ -262,6 +262,33 @@ void main() {
expect
(
renderBox
.
size
.
height
,
equals
(
12.0
));
expect
(
renderBox
.
size
.
height
,
equals
(
12.0
));
});
});
testWidgets
(
'IndexedStack with null index'
,
(
WidgetTester
tester
)
async
{
bool
tapped
;
await
tester
.
pumpWidget
(
new
Center
(
child:
new
IndexedStack
(
index:
null
,
children:
<
Widget
>[
new
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
()
{
print
(
"HELLO"
);
tapped
=
true
;
},
child:
new
SizedBox
(
width:
200.0
,
height:
200.0
,
),
),
],
),
),
);
await
tester
.
tap
(
find
.
byType
(
IndexedStack
));
RenderBox
box
=
tester
.
renderObject
(
find
.
byType
(
IndexedStack
));
expect
(
box
.
size
,
equals
(
const
Size
(
200.0
,
200.0
)));
expect
(
tapped
,
isNull
);
});
testWidgets
(
'Stack clip test'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Stack clip test'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
new
Center
(
new
Center
(
...
...
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