Commit 7ac0ce79 authored by Todd Volkert's avatar Todd Volkert

Add API for specifying the system overlay style. (#4422)

Fixes 3544
parent 69ce7f69
......@@ -196,7 +196,7 @@ class OverlayGeometryAppState extends State<OverlayGeometryApp> {
void main() {
runApp(new MaterialApp(
theme: new ThemeData(
brightness: ThemeBrightness.light,
brightness: Brightness.light,
primarySwatch: Colors.blue,
accentColor: Colors.redAccent[200]
),
......
......@@ -145,7 +145,7 @@ void main() {
runApp(new MaterialApp(
title: 'PageableList',
theme: new ThemeData(
brightness: ThemeBrightness.light,
brightness: Brightness.light,
primarySwatch: Colors.blue,
accentColor: Colors.redAccent[200]
),
......
......@@ -160,7 +160,7 @@ class KeyPad extends StatelessWidget {
return new Theme(
data: new ThemeData(
primarySwatch: Colors.purple,
brightness: ThemeBrightness.dark
brightness: Brightness.dark
),
child: new Flexible(
flex: _flex,
......
......@@ -87,7 +87,7 @@ class ContactsDemoState extends State<ContactsDemo> {
final double statusBarHeight = MediaQuery.of(context).padding.top;
return new Theme(
data: new ThemeData(
brightness: ThemeBrightness.light,
brightness: Brightness.light,
primarySwatch: Colors.indigo
),
child: new Scaffold(
......
......@@ -14,7 +14,7 @@ final Map<double, String> _kLogoImages = <double, String>{
170.0: 'packages/flutter_gallery_assets/pesto/logo_big.png',
};
final ThemeData _kTheme = new ThemeData(
brightness: ThemeBrightness.light,
brightness: Brightness.light,
primarySwatch: Colors.teal,
accentColor: Colors.redAccent[200]
);
......
......@@ -45,12 +45,12 @@ final Map<String, WidgetBuilder> kRoutes = <String, WidgetBuilder>{
};
final ThemeData _kGalleryLightTheme = new ThemeData(
brightness: ThemeBrightness.light,
brightness: Brightness.light,
primarySwatch: Colors.purple
);
final ThemeData _kGalleryDarkTheme = new ThemeData(
brightness: ThemeBrightness.dark,
brightness: Brightness.dark,
primarySwatch: Colors.purple
);
......
......@@ -66,12 +66,12 @@ class StocksAppState extends State<StocksApp> {
switch (_configuration.stockMode) {
case StockMode.optimistic:
return new ThemeData(
brightness: ThemeBrightness.light,
brightness: Brightness.light,
primarySwatch: Colors.purple
);
case StockMode.pessimistic:
return new ThemeData(
brightness: ThemeBrightness.dark,
brightness: Brightness.dark,
accentColor: Colors.redAccent[200]
);
}
......
......@@ -177,6 +177,7 @@ class _MaterialAppState extends State<MaterialApp> {
child: new WidgetsApp(
title: config.title,
textStyle: _errorTextStyle,
brightness: theme.brightness,
color: theme?.primaryColor ?? Colors.blue[500], // blue[500] is the primary color of the default theme
navigatorObserver: _heroController,
onGenerateRoute: _onGenerateRoute,
......
......@@ -129,7 +129,7 @@ class MaterialButton extends StatefulWidget {
/// The theme brightness to use for this button.
///
/// Defaults to the brightness from [ThemeData.brightness].
final ThemeBrightness colorBrightness;
final Brightness colorBrightness;
/// The color scheme to use for this button's text.
///
......@@ -193,7 +193,7 @@ class MaterialButton extends StatefulWidget {
class _MaterialButtonState extends State<MaterialButton> {
bool _highlight = false;
ThemeBrightness get _colorBrightness {
Brightness get _colorBrightness {
return config.colorBrightness ?? Theme.of(context).brightness;
}
......@@ -206,17 +206,17 @@ class _MaterialButtonState extends State<MaterialButton> {
return Theme.of(context).accentColor;
case ButtonTextTheme.normal:
switch (_colorBrightness) {
case ThemeBrightness.light:
case Brightness.light:
return Colors.black87;
case ThemeBrightness.dark:
case Brightness.dark:
return Colors.white;
}
}
} else {
switch (_colorBrightness) {
case ThemeBrightness.light:
case Brightness.light:
return Colors.black26;
case ThemeBrightness.dark:
case Brightness.dark:
return Colors.white30;
}
}
......
......@@ -158,11 +158,11 @@ class _DatePickerHeader extends StatelessWidget {
Color dayColor;
Color yearColor;
switch(themeData.primaryColorBrightness) {
case ThemeBrightness.light:
case Brightness.light:
dayColor = mode == _DatePickerMode.day ? Colors.black87 : Colors.black54;
yearColor = mode == _DatePickerMode.year ? Colors.black87 : Colors.black54;
break;
case ThemeBrightness.dark:
case Brightness.dark:
dayColor = mode == _DatePickerMode.day ? Colors.white : Colors.white70;
yearColor = mode == _DatePickerMode.year ? Colors.white : Colors.white70;
break;
......@@ -172,10 +172,10 @@ class _DatePickerHeader extends StatelessWidget {
Color backgroundColor;
switch (themeData.brightness) {
case ThemeBrightness.light:
case Brightness.light:
backgroundColor = themeData.primaryColor;
break;
case ThemeBrightness.dark:
case Brightness.dark:
backgroundColor = themeData.backgroundColor;
break;
}
......@@ -299,7 +299,7 @@ class DayPicker extends StatelessWidget {
if (selectedDate.year == year && selectedDate.month == month && selectedDate.day == day) {
// The selected day gets a circle background highlight, and a contrasting text color.
itemStyle = themeData.textTheme.body2.copyWith(
color: (themeData.brightness == ThemeBrightness.light) ? Colors.white : Colors.black87
color: (themeData.brightness == Brightness.light) ? Colors.white : Colors.black87
);
decoration = new BoxDecoration(
backgroundColor: themeData.accentColor,
......
......@@ -58,9 +58,9 @@ class Dialog extends StatelessWidget {
Color _getColor(BuildContext context) {
switch (Theme.of(context).brightness) {
case ThemeBrightness.light:
case Brightness.light:
return Colors.white;
case ThemeBrightness.dark:
case Brightness.dark:
return Colors.grey[800];
}
}
......
......@@ -54,13 +54,13 @@ class DrawerItem extends StatelessWidget {
Color _getIconColor(ThemeData themeData) {
switch (themeData.brightness) {
case ThemeBrightness.light:
case Brightness.light:
if (selected)
return themeData.primaryColor;
if (onPressed == null)
return Colors.black26;
return Colors.black45;
case ThemeBrightness.dark:
case Brightness.dark:
if (selected)
return themeData.accentColor;
if (onPressed == null)
......@@ -73,9 +73,9 @@ class DrawerItem extends StatelessWidget {
TextStyle result = themeData.textTheme.body2;
if (selected) {
switch (themeData.brightness) {
case ThemeBrightness.light:
case Brightness.light:
return result.copyWith(color: themeData.primaryColor);
case ThemeBrightness.dark:
case Brightness.dark:
return result.copyWith(color: themeData.accentColor);
}
}
......
......@@ -84,7 +84,7 @@ class FlatButton extends StatelessWidget {
/// The theme brightness to use for this button.
///
/// Defaults to the brightness from [ThemeData.brightness].
final ThemeBrightness colorBrightness;
final Brightness colorBrightness;
/// The widget below this widget in the tree.
///
......
......@@ -107,7 +107,7 @@ class _FloatingActionButtonState extends State<FloatingActionButton> {
if (materialColor == null) {
ThemeData themeData = Theme.of(context);
materialColor = themeData.accentColor;
iconColor = themeData.accentColorBrightness == ThemeBrightness.dark ? Colors.white : Colors.black;
iconColor = themeData.accentColorBrightness == Brightness.dark ? Colors.white : Colors.black;
}
Widget result = new Center(
......
......@@ -58,17 +58,17 @@ class Icon extends StatelessWidget {
/// brightness.
final Color color;
Color _getDefaultColorForThemeBrightness(ThemeBrightness brightness) {
Color _getDefaultColorForBrightness(Brightness brightness) {
switch (brightness) {
case ThemeBrightness.dark:
case Brightness.dark:
return Colors.white;
case ThemeBrightness.light:
case Brightness.light:
return Colors.black;
}
}
Color _getDefaultColor(BuildContext context) {
return IconTheme.of(context)?.color ?? _getDefaultColorForThemeBrightness(Theme.of(context).brightness);
return IconTheme.of(context)?.color ?? _getDefaultColorForBrightness(Theme.of(context).brightness);
}
@override
......
......@@ -117,10 +117,10 @@ class _InputState extends State<Input> {
Color activeColor = themeData.hintColor;
if (focused) {
switch (themeData.brightness) {
case ThemeBrightness.dark:
case Brightness.dark:
activeColor = themeData.accentColor;
break;
case ThemeBrightness.light:
case Brightness.light:
activeColor = themeData.primaryColor;
break;
}
......
......@@ -159,7 +159,7 @@ class _PopupMenuItemState<T extends PopupMenuItem<dynamic>> extends State<T> {
)
);
if (!config.enabled) {
final bool isDark = theme.brightness == ThemeBrightness.dark;
final bool isDark = theme.brightness == Brightness.dark;
item = new IconTheme(
data: new IconThemeData(opacity: isDark ? 0.5 : 0.38),
child: item
......
......@@ -80,7 +80,7 @@ class RaisedButton extends StatelessWidget {
/// The theme brightness to use for this button.
///
/// Defaults to the brightness from [ThemeData.brightness].
final ThemeBrightness colorBrightness;
final Brightness colorBrightness;
/// The widget below this widget in the tree.
///
......@@ -98,9 +98,9 @@ class RaisedButton extends StatelessWidget {
if (disabledColor != null)
return disabledColor;
switch (Theme.of(context).brightness) {
case ThemeBrightness.light:
case Brightness.light:
return Colors.black12;
case ThemeBrightness.dark:
case Brightness.dark:
return Colors.white12;
}
}
......
......@@ -175,7 +175,7 @@ class SnackBar extends StatelessWidget {
margin: const EdgeInsets.symmetric(horizontal: _kSideMargins),
child: new Theme(
data: new ThemeData(
brightness: ThemeBrightness.dark,
brightness: Brightness.dark,
accentColor: theme.accentColor,
accentColorBrightness: theme.accentColorBrightness,
textTheme: Typography.white
......
......@@ -80,7 +80,7 @@ class Switch extends StatelessWidget {
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
ThemeData themeData = Theme.of(context);
final bool isDark = themeData.brightness == ThemeBrightness.dark;
final bool isDark = themeData.brightness == Brightness.dark;
Color activeThumbColor = activeColor ?? themeData.accentColor;
Color activeTrackColor = activeThumbColor.withAlpha(0x80);
......
......@@ -7,7 +7,7 @@ import 'package:meta/meta.dart';
import 'theme_data.dart';
export 'theme_data.dart' show ThemeData, ThemeBrightness;
export 'theme_data.dart' show ThemeData;
/// The duration over which theme changes animate.
const Duration kThemeAnimationDuration = const Duration(milliseconds: 200);
......
......@@ -4,28 +4,12 @@
import 'dart:ui' show Color, hashValues;
import 'package:flutter/widgets.dart';
import 'colors.dart';
import 'icon_theme_data.dart';
import 'typography.dart';
/// The contrast needs of a color.
///
/// This is used to describe the contrast needs of a theme as a whole
/// ([ThemeData.brightness]), its primary color
/// ([ThemeData.primaryColorBrightness]), and its accent color
/// ([ThemeData.accentColorBrightness]).
enum ThemeBrightness {
/// The color is dark, and will require a light text color to achieve readable contrast.
///
/// For example, the color might be dark grey, requiring white text.
dark,
/// The color is right, and will require a dark text color to achieve readable contrast.
///
/// For example, the color might be bright white, requiring black text.
light,
}
// Deriving these values is black magic. The spec claims that pressed buttons
// have a highlight of 0x66999999, but that's clearly wrong. The videos in the
// spec show that buttons have a composited highlight of #E1E1E1 on a background
......@@ -68,12 +52,12 @@ class ThemeData {
/// See <https://www.google.com/design/spec/style/color.html> for
/// more discussion on how to pick the right colors.
factory ThemeData({
ThemeBrightness brightness,
Brightness brightness,
Map<int, Color> primarySwatch,
Color primaryColor,
ThemeBrightness primaryColorBrightness,
Brightness primaryColorBrightness,
Color accentColor,
ThemeBrightness accentColorBrightness,
Brightness accentColorBrightness,
Color canvasColor,
Color cardColor,
Color dividerColor,
......@@ -93,13 +77,13 @@ class ThemeData {
TextTheme primaryTextTheme,
IconThemeData primaryIconTheme
}) {
brightness ??= ThemeBrightness.light;
final bool isDark = brightness == ThemeBrightness.dark;
brightness ??= Brightness.light;
final bool isDark = brightness == Brightness.dark;
primarySwatch ??= Colors.blue;
primaryColor ??= isDark ? Colors.grey[900] : primarySwatch[500];
primaryColorBrightness ??= ThemeBrightness.dark;
primaryColorBrightness ??= Brightness.dark;
accentColor ??= isDark ? Colors.tealAccent[200] : primarySwatch[500];
accentColorBrightness ??= ThemeBrightness.dark;
accentColorBrightness ??= Brightness.dark;
canvasColor ??= isDark ? Colors.grey[850] : Colors.grey[50];
cardColor ??= isDark ? Colors.grey[800] : Colors.white;
dividerColor ??= isDark ? const Color(0x1FFFFFFF) : const Color(0x1F000000);
......@@ -116,8 +100,8 @@ class ThemeData {
hintColor ??= isDark ? const Color(0x42FFFFFF) : const Color(0x4C000000);
errorColor ??= Colors.red[700];
textTheme ??= isDark ? Typography.white : Typography.black;
primaryTextTheme ??= primaryColorBrightness == ThemeBrightness.dark ? Typography.white : Typography.black;
primaryIconTheme ??= primaryColorBrightness == ThemeBrightness.dark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black);
primaryTextTheme ??= primaryColorBrightness == Brightness.dark ? Typography.white : Typography.black;
primaryIconTheme ??= primaryColorBrightness == Brightness.dark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black);
return new ThemeData.raw(
brightness: brightness,
primaryColor: primaryColor,
......@@ -202,10 +186,10 @@ class ThemeData {
}
/// A default light blue theme.
factory ThemeData.light() => new ThemeData(brightness: ThemeBrightness.light);
factory ThemeData.light() => new ThemeData(brightness: Brightness.light);
/// A default dark theme with a teal accent color.
factory ThemeData.dark() => new ThemeData(brightness: ThemeBrightness.dark);
factory ThemeData.dark() => new ThemeData(brightness: Brightness.dark);
/// The default theme. Same as [new ThemeData.light].
///
......@@ -216,20 +200,20 @@ class ThemeData {
/// like buttons to determine what color to pick when not using the primary or
/// accent color.
///
/// When the ThemeBrightness is dark, the canvas, card, and primary colors are
/// all dark. When the ThemeBrightness is light, the canvas and card colors
/// When the [Brightness] is dark, the canvas, card, and primary colors are
/// all dark. When the [Brightness] is light, the canvas and card colors
/// are bright, and the primary color's darkness varies as described by
/// primaryColorBrightness. The primaryColor does not contrast well with the
/// card and canvas colors when the brightness is dark; when the brightness is
/// dark, use Colors.white or the accentColor for a contrasting color.
final ThemeBrightness brightness;
final Brightness brightness;
/// The background color for major parts of the app (toolbars, tab bars, etc)
final Color primaryColor;
/// The brightness of the primaryColor. Used to determine the color of text and
/// icons placed on top of the primary color (e.g. toolbar text).
final ThemeBrightness primaryColorBrightness;
final Brightness primaryColorBrightness;
/// The foreground color for widgets (knobs, text, etc)
final Color accentColor;
......@@ -237,7 +221,7 @@ class ThemeData {
/// The brightness of the accentColor. Used to determine the color of text
/// and icons placed on top of the accent color (e.g. the icons on a floating
/// action button).
final ThemeBrightness accentColorBrightness;
final Brightness accentColorBrightness;
/// The color of [Material] when it is of infinite extent, e.g. the
/// body of a [Scaffold].
......
......@@ -211,11 +211,11 @@ class _TimePickerHeader extends StatelessWidget {
Color activeColor;
Color inactiveColor;
switch(themeData.primaryColorBrightness) {
case ThemeBrightness.light:
case Brightness.light:
activeColor = Colors.black87;
inactiveColor = Colors.black54;
break;
case ThemeBrightness.dark:
case Brightness.dark:
activeColor = Colors.white;
inactiveColor = Colors.white70;
break;
......@@ -223,10 +223,10 @@ class _TimePickerHeader extends StatelessWidget {
Color backgroundColor;
switch (themeData.brightness) {
case ThemeBrightness.light:
case Brightness.light:
backgroundColor = themeData.primaryColor;
break;
case ThemeBrightness.dark:
case Brightness.dark:
backgroundColor = themeData.backgroundColor;
break;
}
......@@ -525,10 +525,10 @@ class _DialState extends State<_Dial> {
Color backgroundColor;
switch (themeData.brightness) {
case ThemeBrightness.light:
case Brightness.light:
backgroundColor = Colors.grey[200];
break;
case ThemeBrightness.dark:
case Brightness.dark:
backgroundColor = themeData.backgroundColor;
break;
}
......@@ -538,11 +538,11 @@ class _DialState extends State<_Dial> {
switch (config.mode) {
case _TimePickerMode.hour:
switch (themeData.brightness) {
case ThemeBrightness.light:
case Brightness.light:
primaryLabels = _hoursBlack;
secondaryLabels = _hoursWhite;
break;
case ThemeBrightness.dark:
case Brightness.dark:
primaryLabels = _hoursWhite;
secondaryLabels = _hoursBlack;
break;
......@@ -550,11 +550,11 @@ class _DialState extends State<_Dial> {
break;
case _TimePickerMode.minute:
switch (themeData.brightness) {
case ThemeBrightness.light:
case Brightness.light:
primaryLabels = _minutesBlack;
secondaryLabels = _minutesWhite;
break;
case ThemeBrightness.dark:
case Brightness.dark:
primaryLabels = _minutesWhite;
secondaryLabels = _minutesBlack;
break;
......
......@@ -37,14 +37,35 @@ class SystemChrome {
return (await _systemChromeProxy.setPreferredOrientations(deviceOrientationMask)).success;
}
/// Specifies the description of the current state of the application as it
/// pertains to the application switcher (a.k.a "recent tasks").
///
/// Arguments:
///
/// * [description]: The application description.
///
/// Return Value:
///
/// boolean indicating if the description was conveyed successfully to the
/// embedder.
///
/// Platform Specific Notes:
///
/// If application-specified metadata is unsupported on the platform,
/// specifying it is a no-op and always return true.
static Future<bool> setApplicationSwitcherDescription(mojom.ApplicationSwitcherDescription description) async {
return (await _systemChromeProxy.setApplicationSwitcherDescription(
description)).success;
}
/// Specifies the set of overlays visible on the embedder when the
/// application is running. The embedder may choose to ignore unsupported
/// overlays
///
/// Arguments:
///
/// * [style]: A mask of [SystemUIOverlay] enum values that denotes the overlays
/// to show.
/// * [overlaysMask]: A mask of [SystemUIOverlay] enum values that denotes
/// the overlays to show.
///
/// Return Value:
///
......@@ -56,6 +77,29 @@ class SystemChrome {
/// If the overlay is unsupported on the platform, enabling or disabling
/// that overlay is a no-op and always return true.
static Future<bool> setEnabledSystemUIOverlays(int overlaysMask) async {
return (await _systemChromeProxy.setEnabledSystemUiOverlays(overlaysMask)).success;
return (await _systemChromeProxy.setEnabledSystemUiOverlays(
overlaysMask)).success;
}
/// Specifies the style of the system overlays that are visible on the
/// embedder (if any). The embedder may choose to ignore unsupported
/// overlays.
///
/// Arguments:
///
/// * [style]: A [SystemUiOverlayStyle] enum value denoting the style to use
///
/// Return Value:
///
/// boolean indicating if the preference was conveyed successfully to the
/// embedder.
///
/// Platform Specific Notes:
///
/// If the overlay is unsupported on the platform, enabling or disabling
/// that overlay is a no-op and always return true.
static Future<bool> setSystemUIOverlayStyle(SystemUiOverlayStyle style) async {
return (await _systemChromeProxy.setSystemUiOverlayStyle(
style)).success;
}
}
......@@ -40,6 +40,7 @@ class WidgetsApp extends StatefulWidget {
Key key,
@required this.onGenerateRoute,
this.title,
this.brightness,
this.textStyle,
this.color,
this.navigatorObserver,
......@@ -56,6 +57,9 @@ class WidgetsApp extends StatefulWidget {
/// A one-line description of this app for use in the window manager.
final String title;
/// The overall brightness of the app, describing its contrast needs.
final Brightness brightness;
/// The default text style for [Text] in the application.
final TextStyle textStyle;
......@@ -180,6 +184,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
devicePixelRatio: ui.window.devicePixelRatio,
child: new Title(
title: config.title,
brightness: config.brightness,
color: config.color,
child: new Navigator(
key: _navigator,
......
......@@ -415,6 +415,21 @@ class ClipPath extends SingleChildRenderObjectWidget {
}
}
/// Describes the contrast needs of a color.
enum Brightness {
/// The color is dark and will require a light text color to achieve readable
/// contrast.
///
/// For example, the color might be dark grey, requiring white text.
dark,
/// The color is light and will require a dark text color to achieve readable
/// contrast.
///
/// For example, the color might be bright white, requiring black text.
light,
}
// POSITIONING AND SIZING NODES
......
......@@ -4,6 +4,7 @@
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:sky_services/flutter/platform/system_chrome.mojom.dart' as mojom;
/// A widget that describes this app in the operating system.
class Title extends StatelessWidget {
......@@ -11,6 +12,7 @@ class Title extends StatelessWidget {
Title({
Key key,
this.title,
this.brightness,
this.color,
this.child
}) : super(key: key) {
......@@ -20,6 +22,9 @@ class Title extends StatelessWidget {
/// A one-line description of this app for use in the window manager.
final String title;
/// The brightness against which the window manager should render system text.
final Brightness brightness;
/// A color that the window manager should use to identify this app.
final Color color;
......@@ -28,10 +33,22 @@ class Title extends StatelessWidget {
@override
Widget build(BuildContext context) {
_updateSystemChrome();
updateTaskDescription(label: title, color: color);
return child;
}
/// Updates the system chrome settings based on this title's metadata.
void _updateSystemChrome() {
// TODO(tvolkert): This may result in a decent amount of unnecessary
// overhead in the embedder (on every build). Consider making Title
// a StatefulWidget that only calls into the SystemChrome service when
// it sees that a value has changed.
SystemChrome.setSystemUIOverlayStyle(brightness == Brightness.dark
? mojom.SystemUiOverlayStyle.light
: mojom.SystemUiOverlayStyle.dark);
}
@override
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment