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
6a1ac731
Commit
6a1ac731
authored
Sep 29, 2016
by
Hans Muller
Committed by
GitHub
Sep 29, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
PopupMenuButton menus inherit the button theme (#6132)
parent
d3d9e1ca
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
243 additions
and
15 deletions
+243
-15
app.dart
packages/flutter/lib/src/material/app.dart
+1
-0
bottom_sheet.dart
packages/flutter/lib/src/material/bottom_sheet.dart
+10
-3
dialog.dart
packages/flutter/lib/src/material/dialog.dart
+9
-3
drop_down.dart
packages/flutter/lib/src/material/drop_down.dart
+10
-3
popup_menu.dart
packages/flutter/lib/src/material/popup_menu.dart
+11
-3
theme.dart
packages/flutter/lib/src/material/theme.dart
+29
-3
theme_test.dart
packages/flutter/test/material/theme_test.dart
+173
-0
No files found.
packages/flutter/lib/src/material/app.dart
View file @
6a1ac731
...
...
@@ -246,6 +246,7 @@ class _MaterialAppState extends State<MaterialApp> {
ThemeData
theme
=
config
.
theme
??
new
ThemeData
.
fallback
();
Widget
result
=
new
AnimatedTheme
(
data:
theme
,
isMaterialAppTheme:
true
,
child:
new
WidgetsApp
(
key:
new
GlobalObjectKey
(
this
),
title:
config
.
title
,
...
...
packages/flutter/lib/src/material/bottom_sheet.dart
View file @
6a1ac731
...
...
@@ -8,6 +8,7 @@ import 'package:flutter/widgets.dart';
import
'colors.dart'
;
import
'material.dart'
;
import
'theme.dart'
;
const
Duration
_kBottomSheetDuration
=
const
Duration
(
milliseconds:
200
);
const
double
_kMinFlingVelocity
=
700.0
;
...
...
@@ -204,10 +205,12 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> {
class
_ModalBottomSheetRoute
<
T
>
extends
PopupRoute
<
T
>
{
_ModalBottomSheetRoute
({
Completer
<
T
>
completer
,
this
.
builder
this
.
builder
,
this
.
theme
,
})
:
super
(
completer:
completer
);
final
WidgetBuilder
builder
;
final
ThemeData
theme
;
@override
Duration
get
transitionDuration
=>
_kBottomSheetDuration
;
...
...
@@ -229,7 +232,10 @@ class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
@override
Widget
buildPage
(
BuildContext
context
,
Animation
<
double
>
animation
,
Animation
<
double
>
forwardAnimation
)
{
return
new
_ModalBottomSheet
<
T
>(
route:
this
);
Widget
bottomSheet
=
new
_ModalBottomSheet
<
T
>(
route:
this
);
if
(
theme
!=
null
)
bottomSheet
=
new
Theme
(
data:
theme
,
child:
bottomSheet
);
return
bottomSheet
;
}
}
...
...
@@ -257,7 +263,8 @@ Future<dynamic/*=T*/> showModalBottomSheet/*<T>*/({ BuildContext context, Widget
final
Completer
<
dynamic
/*=T*/
>
completer
=
new
Completer
<
dynamic
/*=T*/
>();
Navigator
.
push
(
context
,
new
_ModalBottomSheetRoute
<
dynamic
/*=T*/
>(
completer:
completer
,
builder:
builder
builder:
builder
,
theme:
Theme
.
of
(
context
,
shadowThemeOnly:
true
),
));
return
completer
.
future
;
}
packages/flutter/lib/src/material/dialog.dart
View file @
6a1ac731
...
...
@@ -280,10 +280,12 @@ class SimpleDialog extends StatelessWidget {
class
_DialogRoute
<
T
>
extends
PopupRoute
<
T
>
{
_DialogRoute
({
Completer
<
T
>
completer
,
this
.
child
this
.
child
,
this
.
theme
,
})
:
super
(
completer:
completer
);
final
Widget
child
;
final
ThemeData
theme
;
@override
Duration
get
transitionDuration
=>
const
Duration
(
milliseconds:
150
);
...
...
@@ -296,7 +298,7 @@ class _DialogRoute<T> extends PopupRoute<T> {
@override
Widget
buildPage
(
BuildContext
context
,
Animation
<
double
>
animation
,
Animation
<
double
>
forwardAnimation
)
{
return
child
;
return
theme
!=
null
?
new
Theme
(
data:
theme
,
child:
child
)
:
child
;
}
@override
...
...
@@ -324,6 +326,10 @@ class _DialogRoute<T> extends PopupRoute<T> {
/// * <https://www.google.com/design/spec/components/dialogs.html>
Future
<
dynamic
/*=T*/
>
showDialog
/*<T>*/
({
BuildContext
context
,
Widget
child
})
{
Completer
<
dynamic
/*=T*/
>
completer
=
new
Completer
<
dynamic
/*=T*/
>();
Navigator
.
push
(
context
,
new
_DialogRoute
<
dynamic
/*=T*/
>(
completer:
completer
,
child:
child
));
Navigator
.
push
(
context
,
new
_DialogRoute
<
dynamic
/*=T*/
>(
completer:
completer
,
child:
child
,
theme:
Theme
.
of
(
context
,
shadowThemeOnly:
true
),
));
return
completer
.
future
;
}
packages/flutter/lib/src/material/drop_down.dart
View file @
6a1ac731
...
...
@@ -293,7 +293,8 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
this
.
buttonRect
,
this
.
selectedIndex
,
this
.
elevation
:
8
,
TextStyle
style
this
.
theme
,
TextStyle
style
,
})
:
_style
=
style
,
super
(
completer:
completer
)
{
assert
(
style
!=
null
);
}
...
...
@@ -303,6 +304,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
final
Rect
buttonRect
;
final
int
selectedIndex
;
final
int
elevation
;
final
ThemeData
theme
;
// The layout gets this route's scrollableKey so that it can scroll the
/// selected item into position, but only on the initial layout.
bool
initialLayout
=
true
;
...
...
@@ -329,9 +331,13 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
@override
Widget
buildPage
(
BuildContext
context
,
Animation
<
double
>
animation
,
Animation
<
double
>
forwardAnimation
)
{
Widget
menu
=
new
_DropdownMenu
<
T
>(
route:
this
);
if
(
theme
!=
null
)
menu
=
new
Theme
(
data:
theme
,
child:
menu
);
return
new
CustomSingleChildLayout
(
delegate:
new
_DropdownMenuRouteLayout
<
T
>(
route:
this
),
child:
new
_DropdownMenu
<
T
>(
route:
this
)
child:
menu
,
);
}
}
...
...
@@ -501,7 +507,8 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> {
buttonRect:
_kMenuHorizontalPadding
.
inflateRect
(
itemRect
),
selectedIndex:
_selectedIndex
,
elevation:
config
.
elevation
,
style:
_textStyle
theme:
Theme
.
of
(
context
,
shadowThemeOnly:
true
),
style:
_textStyle
,
);
Navigator
.
push
(
context
,
_currentRoute
);
completer
.
future
.
then
((
_DropdownRouteResult
<
T
>
newValue
)
{
...
...
packages/flutter/lib/src/material/popup_menu.dart
View file @
6a1ac731
...
...
@@ -374,13 +374,15 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
this
.
position
,
this
.
items
,
this
.
initialValue
,
this
.
elevation
this
.
elevation
,
this
.
theme
})
:
super
(
completer:
completer
);
final
RelativeRect
position
;
final
List
<
PopupMenuEntry
<
T
>>
items
;
final
dynamic
initialValue
;
final
int
elevation
;
final
ThemeData
theme
;
@override
Animation
<
double
>
createAnimation
()
{
...
...
@@ -411,9 +413,14 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
selectedItemOffset
+=
items
[
i
].
height
;
}
}
Widget
menu
=
new
_PopupMenu
<
T
>(
route:
this
);
if
(
theme
!=
null
)
menu
=
new
Theme
(
data:
theme
,
child:
menu
);
return
new
CustomSingleChildLayout
(
delegate:
new
_PopupMenuRouteLayout
(
position
,
selectedItemOffset
),
child:
new
_PopupMenu
<
T
>(
route:
this
)
child:
menu
);
}
}
...
...
@@ -438,7 +445,8 @@ Future<dynamic/*=T*/> showMenu/*<T>*/({
position:
position
,
items:
items
,
initialValue:
initialValue
,
elevation:
elevation
elevation:
elevation
,
theme:
Theme
.
of
(
context
,
shadowThemeOnly:
true
),
));
return
completer
.
future
;
}
...
...
packages/flutter/lib/src/material/theme.dart
View file @
6a1ac731
...
...
@@ -18,6 +18,7 @@ const Duration kThemeAnimationDuration = const Duration(milliseconds: 200);
///
/// * [AnimatedTheme]
/// * [ThemeData]
/// * [MaterialApp]
class
Theme
extends
InheritedWidget
{
/// Applies the given theme [data] to [child].
///
...
...
@@ -25,6 +26,7 @@ class Theme extends InheritedWidget {
Theme
({
Key
key
,
@required
this
.
data
,
this
.
isMaterialAppTheme
:
false
,
Widget
child
})
:
super
(
key:
key
,
child:
child
)
{
assert
(
child
!=
null
);
...
...
@@ -34,14 +36,33 @@ class Theme extends InheritedWidget {
/// Specifies the color and typography values for descendant widgets.
final
ThemeData
data
;
/// True if this theme was installed by the [MaterialApp].
///
/// When an app uses the [Navigator] to push a route, the route's widgets
/// will only inherit from the app's theme, even though the widget that
/// triggered the push may inherit from a theme that "shadows" the app's
/// theme because it's deeper in the widget tree. Apps can find the shadowing
/// theme with `Theme.of(context, shadowThemeOnly: true)` and pass it along
/// to the class that creates a route's widgets. Material widgets that push
/// routes, like [PopupMenuButton] and [DropdownButton], do this.
final
bool
isMaterialAppTheme
;
static
final
ThemeData
_kFallbackTheme
=
new
ThemeData
.
fallback
();
/// The data from the closest instance of this class that encloses the given context.
///
/// Defaults to the fallback theme data if none exists.
static
ThemeData
of
(
BuildContext
context
)
{
Theme
theme
=
context
.
inheritFromWidgetOfExactType
(
Theme
);
return
theme
?.
data
??
_kFallbackTheme
;
///
/// If [shadowThemeOnly] is true and the closest Theme ancestor was installed by
/// the [MaterialApp] - in other words if the closest Theme ancestor does not
/// shadow the app's theme - then return null. This property is specified in
/// situations where its useful to wrap a route's widgets with a Theme, but only
/// when the app's theme is being shadowed by a theme widget that is farather
/// down in the tree. See [isMaterialAppTheme].
static
ThemeData
of
(
BuildContext
context
,
{
bool
shadowThemeOnly:
false
})
{
final
Theme
theme
=
context
.
inheritFromWidgetOfExactType
(
Theme
);
final
ThemeData
themeData
=
theme
?.
data
??
_kFallbackTheme
;
return
shadowThemeOnly
?
(
theme
.
isMaterialAppTheme
?
null
:
themeData
)
:
themeData
;
}
@override
...
...
@@ -77,6 +98,7 @@ class AnimatedTheme extends ImplicitlyAnimatedWidget {
AnimatedTheme
({
Key
key
,
@required
this
.
data
,
this
.
isMaterialAppTheme
:
false
,
Curve
curve:
Curves
.
linear
,
Duration
duration:
kThemeAnimationDuration
,
this
.
child
...
...
@@ -88,6 +110,9 @@ class AnimatedTheme extends ImplicitlyAnimatedWidget {
/// Specifies the color and typography values for descendant widgets.
final
ThemeData
data
;
/// True if this theme was created by the [MaterialApp]. See [Theme.isMaterialAppTheme].
final
bool
isMaterialAppTheme
;
/// The widget below this widget in the tree.
final
Widget
child
;
...
...
@@ -108,6 +133,7 @@ class _AnimatedThemeState extends AnimatedWidgetBaseState<AnimatedTheme> {
@override
Widget
build
(
BuildContext
context
)
{
return
new
Theme
(
isMaterialAppTheme:
config
.
isMaterialAppTheme
,
child:
config
.
child
,
data:
_data
.
evaluate
(
animation
)
);
...
...
packages/flutter/test/material/theme_test.dart
0 → 100644
View file @
6a1ac731
// Copyright 2016 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
'package:flutter/material.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
testWidgets
(
'PopupMenu inherits app theme'
,
(
WidgetTester
tester
)
async
{
final
Key
popupMenuButtonKey
=
new
UniqueKey
();
await
tester
.
pumpWidget
(
new
MaterialApp
(
theme:
new
ThemeData
(
brightness:
Brightness
.
dark
),
home:
new
Scaffold
(
appBar:
new
AppBar
(
actions:
<
Widget
>[
new
PopupMenuButton
<
String
>(
key:
popupMenuButtonKey
,
itemBuilder:
(
BuildContext
context
)
{
return
<
PopupMenuItem
<
String
>>[
new
PopupMenuItem
<
String
>(
child:
new
Text
(
'menuItem'
))
];
}
),
]
)
)
)
);
await
tester
.
tap
(
find
.
byKey
(
popupMenuButtonKey
));
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
Theme
.
of
(
tester
.
element
(
find
.
text
(
'menuItem'
))).
brightness
,
equals
(
Brightness
.
dark
));
});
testWidgets
(
'PopupMenu inherits shadowed app theme'
,
(
WidgetTester
tester
)
async
{
// Regression test for https://github.com/flutter/flutter/issues/5572
final
Key
popupMenuButtonKey
=
new
UniqueKey
();
await
tester
.
pumpWidget
(
new
MaterialApp
(
theme:
new
ThemeData
(
brightness:
Brightness
.
dark
),
home:
new
Theme
(
data:
new
ThemeData
(
brightness:
Brightness
.
light
),
child:
new
Scaffold
(
appBar:
new
AppBar
(
actions:
<
Widget
>[
new
PopupMenuButton
<
String
>(
key:
popupMenuButtonKey
,
itemBuilder:
(
BuildContext
context
)
{
return
<
PopupMenuItem
<
String
>>[
new
PopupMenuItem
<
String
>(
child:
new
Text
(
'menuItem'
))
];
}
),
]
)
)
)
)
);
await
tester
.
tap
(
find
.
byKey
(
popupMenuButtonKey
));
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
Theme
.
of
(
tester
.
element
(
find
.
text
(
'menuItem'
))).
brightness
,
equals
(
Brightness
.
light
));
});
testWidgets
(
'DropdownMenu inherits shadowed app theme'
,
(
WidgetTester
tester
)
async
{
final
Key
dropdownMenuButtonKey
=
new
UniqueKey
();
await
tester
.
pumpWidget
(
new
MaterialApp
(
theme:
new
ThemeData
(
brightness:
Brightness
.
dark
),
home:
new
Theme
(
data:
new
ThemeData
(
brightness:
Brightness
.
light
),
child:
new
Scaffold
(
appBar:
new
AppBar
(
actions:
<
Widget
>[
new
DropdownButton
<
String
>(
key:
dropdownMenuButtonKey
,
onChanged:
(
String
newValue
)
{
},
value:
'menuItem'
,
items:
<
DropdownMenuItem
<
String
>>[
new
DropdownMenuItem
<
String
>(
value:
'menuItem'
,
child:
new
Text
(
'menuItem'
),
),
],
)
]
)
)
)
)
);
await
tester
.
tap
(
find
.
byKey
(
dropdownMenuButtonKey
));
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
for
(
Element
item
in
tester
.
elementList
(
find
.
text
(
'menuItem'
)))
expect
(
Theme
.
of
(
item
).
brightness
,
equals
(
Brightness
.
light
));
});
testWidgets
(
'ModalBottomSheet inherits shadowed app theme'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
new
MaterialApp
(
theme:
new
ThemeData
(
brightness:
Brightness
.
dark
),
home:
new
Theme
(
data:
new
ThemeData
(
brightness:
Brightness
.
light
),
child:
new
Scaffold
(
body:
new
Center
(
child:
new
Builder
(
builder:
(
BuildContext
context
)
{
return
new
RaisedButton
(
onPressed:
()
{
showModalBottomSheet
(
context:
context
,
builder:
(
BuildContext
context
)
=>
new
Text
(
'bottomSheet'
),
);
},
child:
new
Text
(
'SHOW'
),
);
}
)
)
)
)
)
);
await
tester
.
tap
(
find
.
text
(
'SHOW'
));
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
Theme
.
of
(
tester
.
element
(
find
.
text
(
'bottomSheet'
))).
brightness
,
equals
(
Brightness
.
light
));
await
tester
.
tap
(
find
.
text
(
'bottomSheet'
));
// dismiss the bottom sheet
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
});
testWidgets
(
'Dialog inherits shadowed app theme'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
ScaffoldState
>
scaffoldKey
=
new
GlobalKey
<
ScaffoldState
>();
await
tester
.
pumpWidget
(
new
MaterialApp
(
theme:
new
ThemeData
(
brightness:
Brightness
.
dark
),
home:
new
Theme
(
data:
new
ThemeData
(
brightness:
Brightness
.
light
),
child:
new
Scaffold
(
key:
scaffoldKey
,
body:
new
Center
(
child:
new
Builder
(
builder:
(
BuildContext
context
)
{
return
new
RaisedButton
(
onPressed:
()
{
showDialog
(
context:
context
,
child:
new
Text
(
'dialog'
),
);
},
child:
new
Text
(
'SHOW'
),
);
}
)
)
)
)
)
);
await
tester
.
tap
(
find
.
text
(
'SHOW'
));
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
Theme
.
of
(
tester
.
element
(
find
.
text
(
'dialog'
))).
brightness
,
equals
(
Brightness
.
light
));
});
}
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