Revert "Reversion for roll (#22984)" (#23029)

This reverts commit 80f80ab08c0dad8551c91087cd1017aba094d22c.
This commit is contained in:
matthew-carroll 2018-10-12 16:31:34 -07:00 committed by GitHub
parent 53de41cac6
commit 04a26778c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 3045 additions and 1766 deletions

View File

@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// This program generates a Dart "localizations" Map definition that combines // This program generates a getTranslation() function that looks up the
// the contents of the arb files. The map can be used to lookup a localized // translations contained by the arb files. The returned value is an
// string: `localizations[localeString][resourceId]`. // instance of GlobalMaterialLocalizations that corresponds to a single
// locale.
// //
// The *.arb files are in packages/flutter_localizations/lib/src/l10n. // The *.arb files are in packages/flutter_localizations/lib/src/l10n.
// //
@ -273,6 +274,8 @@ String generateType(Map<String, dynamic> attributes) {
switch (attributes['x-flutter-type']) { switch (attributes['x-flutter-type']) {
case 'icuShortTimePattern': case 'icuShortTimePattern':
return 'TimeOfDayFormat'; return 'TimeOfDayFormat';
case 'scriptCategory':
return 'ScriptCategory';
} }
} }
return 'String'; return 'String';
@ -308,6 +311,12 @@ const Map<String, String> _icuTimeOfDayToEnum = <String, String>{
'ah:mm': 'TimeOfDayFormat.a_space_h_colon_mm', 'ah:mm': 'TimeOfDayFormat.a_space_h_colon_mm',
}; };
const Map<String, String> _scriptCategoryToEnum = <String, String>{
'English-like': 'ScriptCategory.englishLike',
'dense': 'ScriptCategory.dense',
'tall': 'ScriptCategory.tall',
};
/// Returns the literal that describes the value returned by getters /// Returns the literal that describes the value returned by getters
/// with the given attributes. /// with the given attributes.
/// ///
@ -330,6 +339,15 @@ String generateValue(String value, Map<String, dynamic> attributes) {
); );
} }
return _icuTimeOfDayToEnum[value]; return _icuTimeOfDayToEnum[value];
case 'scriptCategory':
if (!_scriptCategoryToEnum.containsKey(value)) {
throw Exception(
'"$value" is not one of the scriptCategory values supported '
'by the material library. Here is the list of supported '
'values:\n ' + _scriptCategoryToEnum.keys.join('\n ')
);
}
return _scriptCategoryToEnum[value];
} }
} }
return generateString(value); return generateString(value);

View File

@ -57,7 +57,7 @@ class TypographyDemo extends StatelessWidget {
TextStyleItem(name: 'Body 2', style: textTheme.body2, text: 'Medium 14sp'), TextStyleItem(name: 'Body 2', style: textTheme.body2, text: 'Medium 14sp'),
TextStyleItem(name: 'Body 1', style: textTheme.body1, text: 'Regular 14sp'), TextStyleItem(name: 'Body 1', style: textTheme.body1, text: 'Regular 14sp'),
TextStyleItem(name: 'Caption', style: textTheme.caption, text: 'Regular 12sp'), TextStyleItem(name: 'Caption', style: textTheme.caption, text: 'Regular 12sp'),
TextStyleItem(name: 'Button', style: textTheme.button, text: 'MEDIUM (ALL CAPS) 14sp') TextStyleItem(name: 'Button', style: textTheme.button, text: 'MEDIUM (ALL CAPS) 14sp'),
]; ];
if (MediaQuery.of(context).size.width > 500.0) { if (MediaQuery.of(context).size.width > 500.0) {

View File

@ -24,17 +24,23 @@ TextTheme _buildTextTheme(TextTheme base) {
ThemeData _buildDarkTheme() { ThemeData _buildDarkTheme() {
const Color primaryColor = Color(0xFF0175c2); const Color primaryColor = Color(0xFF0175c2);
const Color secondaryColor = Color(0xFF13B9FD);
final ThemeData base = ThemeData.dark(); final ThemeData base = ThemeData.dark();
final ColorScheme colorScheme = const ColorScheme.dark().copyWith(
primary: primaryColor,
secondary: secondaryColor,
);
return base.copyWith( return base.copyWith(
primaryColor: primaryColor, primaryColor: primaryColor,
buttonColor: primaryColor, buttonColor: primaryColor,
indicatorColor: Colors.white, indicatorColor: Colors.white,
accentColor: const Color(0xFF13B9FD), accentColor: secondaryColor,
canvasColor: const Color(0xFF202124), canvasColor: const Color(0xFF202124),
scaffoldBackgroundColor: const Color(0xFF202124), scaffoldBackgroundColor: const Color(0xFF202124),
backgroundColor: const Color(0xFF202124), backgroundColor: const Color(0xFF202124),
errorColor: const Color(0xFFB00020), errorColor: const Color(0xFFB00020),
buttonTheme: const ButtonThemeData( buttonTheme: ButtonThemeData(
colorScheme: colorScheme,
textTheme: ButtonTextTheme.primary, textTheme: ButtonTextTheme.primary,
), ),
textTheme: _buildTextTheme(base.textTheme), textTheme: _buildTextTheme(base.textTheme),
@ -45,19 +51,26 @@ ThemeData _buildDarkTheme() {
ThemeData _buildLightTheme() { ThemeData _buildLightTheme() {
const Color primaryColor = Color(0xFF0175c2); const Color primaryColor = Color(0xFF0175c2);
const Color secondaryColor = Color(0xFF13B9FD);
final ColorScheme colorScheme = const ColorScheme.light().copyWith(
primary: primaryColor,
secondary: secondaryColor,
);
final ThemeData base = ThemeData.light(); final ThemeData base = ThemeData.light();
return base.copyWith( return base.copyWith(
colorScheme: colorScheme,
primaryColor: primaryColor, primaryColor: primaryColor,
buttonColor: primaryColor, buttonColor: primaryColor,
indicatorColor: Colors.white, indicatorColor: Colors.white,
splashColor: Colors.white24, splashColor: Colors.white24,
splashFactory: InkRipple.splashFactory, splashFactory: InkRipple.splashFactory,
accentColor: const Color(0xFF13B9FD), accentColor: secondaryColor,
canvasColor: Colors.white, canvasColor: Colors.white,
scaffoldBackgroundColor: Colors.white, scaffoldBackgroundColor: Colors.white,
backgroundColor: Colors.white, backgroundColor: Colors.white,
errorColor: const Color(0xFFB00020), errorColor: const Color(0xFFB00020),
buttonTheme: const ButtonThemeData( buttonTheme: ButtonThemeData(
colorScheme: colorScheme,
textTheme: ButtonTextTheme.primary, textTheme: ButtonTextTheme.primary,
), ),
textTheme: _buildTextTheme(base.textTheme), textTheme: _buildTextTheme(base.textTheme),

View File

@ -649,12 +649,14 @@ void main() {
handle.dispose(); handle.dispose();
}); });
testWidgets('overscroll_demo $themeName', (WidgetTester tester) async { testWidgets('overscroll_demo', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics(); final AutomatedTestWidgetsFlutterBinding binding = tester.binding;
await tester.pumpWidget(MaterialApp(theme: theme, home: const OverscrollDemo())); binding.addTime(const Duration(seconds: 3));
await expectLater(tester, meetsGuideline(textContrastGuideline)); final SemanticsHandle handle = tester.ensureSemantics();
handle.dispose(); await tester.pumpWidget(const MaterialApp(home: OverscrollDemo()));
}); await expectLater(tester, meetsGuideline(textContrastGuideline));
handle.dispose();
});
testWidgets('page_selector_demo $themeName', (WidgetTester tester) async { testWidgets('page_selector_demo $themeName', (WidgetTester tester) async {
final AutomatedTestWidgetsFlutterBinding binding = tester.binding; final AutomatedTestWidgetsFlutterBinding binding = tester.binding;

View File

@ -32,6 +32,7 @@ export 'src/material/checkbox_list_tile.dart';
export 'src/material/chip.dart'; export 'src/material/chip.dart';
export 'src/material/chip_theme.dart'; export 'src/material/chip_theme.dart';
export 'src/material/circle_avatar.dart'; export 'src/material/circle_avatar.dart';
export 'src/material/color_scheme.dart';
export 'src/material/colors.dart'; export 'src/material/colors.dart';
export 'src/material/constants.dart'; export 'src/material/constants.dart';
export 'src/material/data_table.dart'; export 'src/material/data_table.dart';
@ -65,6 +66,7 @@ export 'src/material/input_border.dart';
export 'src/material/input_decorator.dart'; export 'src/material/input_decorator.dart';
export 'src/material/list_tile.dart'; export 'src/material/list_tile.dart';
export 'src/material/material.dart'; export 'src/material/material.dart';
export 'src/material/material_button.dart';
export 'src/material/material_localizations.dart'; export 'src/material/material_localizations.dart';
export 'src/material/mergeable_material.dart'; export 'src/material/mergeable_material.dart';
export 'src/material/outline_button.dart'; export 'src/material/outline_button.dart';
@ -95,6 +97,7 @@ export 'src/material/tabs.dart';
export 'src/material/text_field.dart'; export 'src/material/text_field.dart';
export 'src/material/text_form_field.dart'; export 'src/material/text_form_field.dart';
export 'src/material/text_selection.dart'; export 'src/material/text_selection.dart';
export 'src/material/text_theme.dart';
export 'src/material/theme.dart'; export 'src/material/theme.dart';
export 'src/material/theme_data.dart'; export 'src/material/theme_data.dart';
export 'src/material/time.dart'; export 'src/material/time.dart';

View File

@ -19,8 +19,8 @@ import 'material.dart';
import 'material_localizations.dart'; import 'material_localizations.dart';
import 'scaffold.dart'; import 'scaffold.dart';
import 'tabs.dart'; import 'tabs.dart';
import 'text_theme.dart';
import 'theme.dart'; import 'theme.dart';
import 'typography.dart';
// Examples can assume: // Examples can assume:
// void _airDress() { } // void _airDress() { }

View File

@ -14,8 +14,8 @@ import 'debug.dart';
import 'ink_well.dart'; import 'ink_well.dart';
import 'material.dart'; import 'material.dart';
import 'material_localizations.dart'; import 'material_localizations.dart';
import 'text_theme.dart';
import 'theme.dart'; import 'theme.dart';
import 'typography.dart';
const double _kActiveFontSize = 14.0; const double _kActiveFontSize = 14.0;
const double _kInactiveFontSize = 12.0; const double _kInactiveFontSize = 12.0;

View File

@ -9,7 +9,6 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'button_theme.dart'; import 'button_theme.dart';
import 'colors.dart';
import 'constants.dart'; import 'constants.dart';
import 'ink_well.dart'; import 'ink_well.dart';
import 'material.dart'; import 'material.dart';
@ -225,238 +224,6 @@ class _RawMaterialButtonState extends State<RawMaterialButton> {
} }
} }
/// A utility class for building Material buttons that depend on the
/// ambient [ButtonTheme] and [Theme].
///
/// The button's size will expand to fit the child widget, if necessary.
///
/// MaterialButtons whose [onPressed] handler is null will be disabled. To have
/// an enabled button, make sure to pass a non-null value for onPressed.
///
/// Rather than using this class directly, consider using [FlatButton] or
/// [RaisedButton], which configure this class with appropriate defaults that
/// match the material design specification.
///
/// To create a button directly, without inheriting theme defaults, use
/// [RawMaterialButton].
///
/// If you want an ink-splash effect for taps, but don't want to use a button,
/// consider using [InkWell] directly.
///
/// See also:
///
/// * [IconButton], to create buttons that contain icons rather than text.
class MaterialButton extends StatelessWidget {
/// Creates a material button.
///
/// Rather than creating a material button directly, consider using
/// [FlatButton] or [RaisedButton]. To create a custom Material button
/// consider using [RawMaterialButton].
///
/// The [clipBehavior] argument must not be null.
const MaterialButton({
Key key,
this.colorBrightness,
this.textTheme,
this.textColor,
this.color,
this.highlightColor,
this.splashColor,
this.elevation,
this.highlightElevation,
this.minWidth,
this.height,
this.padding,
this.materialTapTargetSize,
this.clipBehavior = Clip.none,
@required this.onPressed,
this.child
}) : assert(clipBehavior != null), super(key: key);
/// The theme brightness to use for this button.
///
/// Defaults to the brightness from [ThemeData.brightness].
final Brightness colorBrightness;
/// Defines the button's base colors, and the defaults for the button's minimum
/// size, internal padding, and shape.
final ButtonTextTheme textTheme;
/// The color to use for this button's text.
final Color textColor;
/// The button's fill color, displayed by its [Material], while the button
/// is in its default (unpressed, enabled) state.
///
/// Defaults to null, meaning that the color is automatically derived from the [Theme].
///
/// Typically, a material design color will be used, as follows:
///
/// ```dart
/// MaterialButton(
/// color: Colors.blue[500],
/// onPressed: _handleTap,
/// child: Text('DEMO'),
/// ),
/// ```
final Color color;
/// The primary color of the button when the button is in the down (pressed)
/// state.
///
/// The splash is represented as a circular overlay that appears above the
/// [highlightColor] overlay. The splash overlay has a center point that
/// matches the hit point of the user touch event. The splash overlay will
/// expand to fill the button area if the touch is held for long enough time.
/// If the splash color has transparency then the highlight and button color
/// will show through.
///
/// Defaults to the Theme's splash color, [ThemeData.splashColor].
final Color splashColor;
/// The secondary color of the button when the button is in the down (pressed)
/// state.
///
/// The highlight color is represented as a solid color that is overlaid over
/// the button color (if any). If the highlight color has transparency, the
/// button color will show through. The highlight fades in quickly as the
/// button is held down.
///
/// Defaults to the Theme's highlight color, [ThemeData.highlightColor].
final Color highlightColor;
/// The z-coordinate at which to place this button. This controls the size of
/// the shadow below the button.
///
/// Defaults to 0.
///
/// See also:
///
/// * [FlatButton], a material button specialized for the case where the
/// elevation is zero.
/// * [RaisedButton], a material button specialized for the case where the
/// elevation is non-zero.
final double elevation;
/// The z-coordinate at which to place this button when highlighted. This
/// controls the size of the shadow below the button.
///
/// Defaults to 0.
///
/// See also:
///
/// * [elevation], the default elevation.
final double highlightElevation;
/// The smallest horizontal extent that the button will occupy.
///
/// Defaults to the value from the current [ButtonTheme].
final double minWidth;
/// The vertical extent of the button.
///
/// Defaults to the value from the current [ButtonTheme].
final double height;
/// The internal padding for the button's [child].
///
/// Defaults to the value from the current [ButtonTheme],
/// [ButtonThemeData.padding].
final EdgeInsetsGeometry padding;
/// The callback that is called when the button is tapped or otherwise activated.
///
/// If this is set to null, the button will be disabled.
final VoidCallback onPressed;
/// The widget below this widget in the tree.
///
/// {@macro flutter.widgets.child}
final Widget child;
/// Configures the minimum size of the tap target.
///
/// Defaults to [ThemeData.materialTapTargetSize].
///
/// See also:
///
/// * [MaterialTapTargetSize], for a description of how this affects tap targets.
final MaterialTapTargetSize materialTapTargetSize;
/// {@macro flutter.widgets.Clip}
final Clip clipBehavior;
/// Whether the button is enabled or disabled. Buttons are disabled by default. To
/// enable a button, set its [onPressed] property to a non-null value.
bool get enabled => onPressed != null;
Brightness _getBrightness(ThemeData theme) {
return colorBrightness ?? theme.brightness;
}
ButtonTextTheme _getTextTheme(ButtonThemeData buttonTheme) {
return textTheme ?? buttonTheme.textTheme;
}
Color _getTextColor(ThemeData theme, ButtonThemeData buttonTheme, Color fillColor) {
if (textColor != null)
return textColor;
final bool themeIsDark = _getBrightness(theme) == Brightness.dark;
final bool fillIsDark = fillColor != null
? ThemeData.estimateBrightnessForColor(fillColor) == Brightness.dark
: themeIsDark;
switch (_getTextTheme(buttonTheme)) {
case ButtonTextTheme.normal:
return enabled
? (themeIsDark ? Colors.white : Colors.black87)
: theme.disabledColor;
case ButtonTextTheme.accent:
return enabled
? theme.accentColor
: theme.disabledColor;
case ButtonTextTheme.primary:
return enabled
? (fillIsDark ? Colors.white : Colors.black)
: (themeIsDark ? Colors.white30 : Colors.black38);
}
return null;
}
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final ButtonThemeData buttonTheme = ButtonTheme.of(context);
final Color textColor = _getTextColor(theme, buttonTheme, color);
return RawMaterialButton(
onPressed: onPressed,
fillColor: color,
textStyle: theme.textTheme.button.copyWith(color: textColor),
highlightColor: highlightColor ?? theme.highlightColor,
splashColor: splashColor ?? theme.splashColor,
elevation: elevation ?? 2.0,
highlightElevation: highlightElevation ?? 8.0,
padding: padding ?? buttonTheme.padding,
constraints: buttonTheme.constraints.copyWith(
minWidth: minWidth,
minHeight: height,
),
shape: buttonTheme.shape,
child: child,
materialTapTargetSize: materialTapTargetSize ?? theme.materialTapTargetSize,
clipBehavior: clipBehavior,
);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(FlagProperty('enabled', value: enabled, ifFalse: 'disabled'));
}
}
/// A widget to pad the area around a [MaterialButton]'s inner [Material]. /// A widget to pad the area around a [MaterialButton]'s inner [Material].
/// ///
/// Redirect taps that occur in the padded area around the child to the center /// Redirect taps that occur in the padded area around the child to the center

View File

@ -5,7 +5,15 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'color_scheme.dart';
import 'colors.dart';
import 'constants.dart';
import 'flat_button.dart';
import 'material_button.dart';
import 'outline_button.dart';
import 'raised_button.dart';
import 'theme.dart'; import 'theme.dart';
import 'theme_data.dart' show MaterialTapTargetSize;
/// Used with [ButtonTheme] and [ButtonThemeData] to define a button's base /// Used with [ButtonTheme] and [ButtonThemeData] to define a button's base
/// colors, and the defaults for the button's minimum size, internal padding, /// colors, and the defaults for the button's minimum size, internal padding,
@ -13,8 +21,8 @@ import 'theme.dart';
/// ///
/// See also: /// See also:
/// ///
/// * [RaisedButton], which styles itself based on the ambient [ButtonTheme]. /// * [RaisedButton], [FlatButton], [OutlineButton], which are configured
/// * [FlatButton], which styles itself based on the ambient [ButtonTheme]. /// based on the ambient [ButtonTheme].
enum ButtonTextTheme { enum ButtonTextTheme {
/// Button text is black or white depending on [ThemeData.brightness]. /// Button text is black or white depending on [ThemeData.brightness].
normal, normal,
@ -46,30 +54,19 @@ enum ButtonBarLayoutBehavior {
/// can be overridden with [ButtonTheme]. /// can be overridden with [ButtonTheme].
/// ///
/// The actual appearance of buttons depends on the button theme, the /// The actual appearance of buttons depends on the button theme, the
/// button's enabled state, its elevation (if any) and the overall Material /// button's enabled state, its elevation (if any), and the overall [Theme].
/// theme.
/// ///
/// See also: /// See also:
/// ///
/// * [FlatButton] and [RaisedButton], which are styled based on the /// * [FlatButton] [RaisedButton], and [OutlineButton], which are styled
/// ambient button theme. /// based on the ambient button theme.
/// * [ThemeData.textTheme], `button` is the default text style for button labels.
/// * [ThemeData.buttonColor], the fill color for [RaisedButton]s unless the
/// button theme's text theme is [ButtonTextTheme.primary].
/// * [ThemeData.primaryColor], the fill or text color if a button theme's text
/// theme is [ButtonTextTheme.primary].
/// * [ThemeData.accentColor], the text color for buttons when button theme's
/// text theme is [ButtonTextTheme.accent].
/// * [ThemeData.disabled], the default text color for disabled buttons.
/// * [ThemeData.brightness], used to select contrasting text and fill colors.
/// * [ThemeData.highlightColor], a button [InkWell]'s default highlight color.
/// * [ThemeData.splashColor], a button [InkWell]'s default splash color.
/// * [RawMaterialButton], which can be used to configure a button that doesn't /// * [RawMaterialButton], which can be used to configure a button that doesn't
/// depend on any inherited themes. /// depend on any inherited themes.
class ButtonTheme extends InheritedWidget { class ButtonTheme extends InheritedWidget {
/// Creates a button theme. /// Creates a button theme.
/// ///
/// The [textTheme], [minWidth], and [height] arguments must not be null. /// The [textTheme], [minWidth], [height], and [colorScheme] arguments
/// must not be null.
ButtonTheme({ ButtonTheme({
Key key, Key key,
ButtonTextTheme textTheme = ButtonTextTheme.normal, ButtonTextTheme textTheme = ButtonTextTheme.normal,
@ -79,6 +76,12 @@ class ButtonTheme extends InheritedWidget {
EdgeInsetsGeometry padding, EdgeInsetsGeometry padding,
ShapeBorder shape, ShapeBorder shape,
bool alignedDropdown = false, bool alignedDropdown = false,
Color buttonColor,
Color disabledColor,
Color highlightColor,
Color splashColor,
ColorScheme colorScheme,
MaterialTapTargetSize materialTapTargetSize,
Widget child, Widget child,
}) : assert(textTheme != null), }) : assert(textTheme != null),
assert(minWidth != null && minWidth >= 0.0), assert(minWidth != null && minWidth >= 0.0),
@ -93,6 +96,12 @@ class ButtonTheme extends InheritedWidget {
shape: shape, shape: shape,
alignedDropdown: alignedDropdown, alignedDropdown: alignedDropdown,
layoutBehavior: layoutBehavior, layoutBehavior: layoutBehavior,
buttonColor: buttonColor,
disabledColor: disabledColor,
highlightColor: highlightColor,
splashColor: splashColor,
colorScheme: colorScheme,
materialTapTargetSize: materialTapTargetSize,
), ),
super(key: key, child: child); super(key: key, child: child);
@ -128,6 +137,11 @@ class ButtonTheme extends InheritedWidget {
EdgeInsetsGeometry padding = const EdgeInsets.symmetric(horizontal: 8.0), EdgeInsetsGeometry padding = const EdgeInsets.symmetric(horizontal: 8.0),
ShapeBorder shape, ShapeBorder shape,
bool alignedDropdown = false, bool alignedDropdown = false,
Color buttonColor,
Color disabledColor,
Color highlightColor,
Color splashColor,
ColorScheme colorScheme,
Widget child, Widget child,
ButtonBarLayoutBehavior layoutBehavior = ButtonBarLayoutBehavior.padded, ButtonBarLayoutBehavior layoutBehavior = ButtonBarLayoutBehavior.padded,
}) : assert(textTheme != null), }) : assert(textTheme != null),
@ -142,6 +156,11 @@ class ButtonTheme extends InheritedWidget {
shape: shape, shape: shape,
alignedDropdown: alignedDropdown, alignedDropdown: alignedDropdown,
layoutBehavior: layoutBehavior, layoutBehavior: layoutBehavior,
buttonColor: buttonColor,
disabledColor: disabledColor,
highlightColor: highlightColor,
splashColor: splashColor,
colorScheme: colorScheme,
), ),
super(key: key, child: child); super(key: key, child: child);
@ -156,8 +175,19 @@ class ButtonTheme extends InheritedWidget {
/// ButtonThemeData theme = ButtonTheme.of(context); /// ButtonThemeData theme = ButtonTheme.of(context);
/// ``` /// ```
static ButtonThemeData of(BuildContext context) { static ButtonThemeData of(BuildContext context) {
final ButtonTheme result = context.inheritFromWidgetOfExactType(ButtonTheme); final ButtonTheme inheritedButtonTheme = context.inheritFromWidgetOfExactType(ButtonTheme);
return result?.data ?? Theme.of(context).buttonTheme; ButtonThemeData buttonTheme = inheritedButtonTheme?.data;
if (buttonTheme?.colorScheme == null) { // if buttonTheme or buttonTheme.colorScheme is null
final ThemeData theme = Theme.of(context);
buttonTheme ??= theme.buttonTheme;
if (buttonTheme.colorScheme == null) {
buttonTheme = buttonTheme.copyWith(
colorScheme: theme.buttonTheme.colorScheme ?? theme.colorScheme,
);
assert(buttonTheme.colorScheme != null);
}
}
return buttonTheme;
} }
@override @override
@ -173,7 +203,13 @@ class ButtonThemeData extends Diagnosticable {
/// Create a button theme object that can be used with [ButtonTheme] /// Create a button theme object that can be used with [ButtonTheme]
/// or [ThemeData]. /// or [ThemeData].
/// ///
/// The [textTheme], [minWidth], and [height] parameters must not be null. /// The [textTheme], [minWidth], [height], [alignedDropDown], and
/// [layoutBehavior] parameters must not be null. The [minWidth] and
/// [height] parameters must greater than or equal to zero.
///
/// The ButtonTheme's methods that have a [MaterialButton] parameter and
/// have a name with a `get` prefix are used by [RaisedButton],
/// [OutlineButton], and [FlatButton] to configure a [RawMaterialButton].
const ButtonThemeData({ const ButtonThemeData({
this.textTheme = ButtonTextTheme.normal, this.textTheme = ButtonTextTheme.normal,
this.minWidth = 88.0, this.minWidth = 88.0,
@ -182,13 +218,24 @@ class ButtonThemeData extends Diagnosticable {
ShapeBorder shape, ShapeBorder shape,
this.layoutBehavior = ButtonBarLayoutBehavior.padded, this.layoutBehavior = ButtonBarLayoutBehavior.padded,
this.alignedDropdown = false, this.alignedDropdown = false,
Color buttonColor,
Color disabledColor,
Color highlightColor,
Color splashColor,
this.colorScheme,
MaterialTapTargetSize materialTapTargetSize,
}) : assert(textTheme != null), }) : assert(textTheme != null),
assert(minWidth != null && minWidth >= 0.0), assert(minWidth != null && minWidth >= 0.0),
assert(height != null && height >= 0.0), assert(height != null && height >= 0.0),
assert(alignedDropdown != null), assert(alignedDropdown != null),
assert(layoutBehavior != null), assert(layoutBehavior != null),
_buttonColor = buttonColor,
_disabledColor = disabledColor,
_highlightColor = highlightColor,
_splashColor = splashColor,
_padding = padding, _padding = padding,
_shape = shape; _shape = shape,
_materialTapTargetSize = materialTapTargetSize;
/// The minimum width for buttons. /// The minimum width for buttons.
/// ///
@ -205,10 +252,13 @@ class ButtonThemeData extends Diagnosticable {
/// Defines a button's base colors, and the defaults for the button's minimum /// Defines a button's base colors, and the defaults for the button's minimum
/// size, internal padding, and shape. /// size, internal padding, and shape.
///
/// Despite the name, this property is not a [TextTheme], its value is not a
/// collection of [TextStyle]s.
final ButtonTextTheme textTheme; final ButtonTextTheme textTheme;
/// Defines whether a button bar should size itself with a minimum size /// Defines whether a [ButtonBar] should size itself with a minimum size
/// constraint or padding. /// constraint or with padding.
/// ///
/// Defaults to [ButtonBarLayoutBehavior.padded]. /// Defaults to [ButtonBarLayoutBehavior.padded].
final ButtonBarLayoutBehavior layoutBehavior; final ButtonBarLayoutBehavior layoutBehavior;
@ -232,16 +282,22 @@ class ButtonThemeData extends Diagnosticable {
/// ///
/// Defaults to 24.0 on the left and right if [textTheme] is /// Defaults to 24.0 on the left and right if [textTheme] is
/// [ButtonTextTheme.primary], 16.0 on the left and right otherwise. /// [ButtonTextTheme.primary], 16.0 on the left and right otherwise.
///
/// See also:
///
/// * [getPadding], which is used by [RaisedButton], [OutlineButton]
/// and [FlatButton].
EdgeInsetsGeometry get padding { EdgeInsetsGeometry get padding {
if (_padding != null) if (_padding != null)
return _padding; return _padding;
switch (textTheme) { switch (textTheme) {
case ButtonTextTheme.normal: case ButtonTextTheme.normal:
case ButtonTextTheme.accent: case ButtonTextTheme.accent:
return const EdgeInsets.symmetric(horizontal: 16.0); return const EdgeInsets.symmetric(horizontal: 16.0);
case ButtonTextTheme.primary: case ButtonTextTheme.primary:
return const EdgeInsets.symmetric(horizontal: 24.0); return const EdgeInsets.symmetric(horizontal: 24.0);
} }
assert(false);
return EdgeInsets.zero; return EdgeInsets.zero;
} }
final EdgeInsetsGeometry _padding; final EdgeInsetsGeometry _padding;
@ -255,6 +311,11 @@ class ButtonThemeData extends Diagnosticable {
/// Defaults to a rounded rectangle with circular corner radii of 4.0 if /// Defaults to a rounded rectangle with circular corner radii of 4.0 if
/// [textTheme] is [ButtonTextTheme.primary], a rounded rectangle with /// [textTheme] is [ButtonTextTheme.primary], a rounded rectangle with
/// circular corner radii of 2.0 otherwise. /// circular corner radii of 2.0 otherwise.
///
/// See also:
///
/// * [getShape], which is used by [RaisedButton], [OutlineButton]
/// and [FlatButton].
ShapeBorder get shape { ShapeBorder get shape {
if (_shape != null) if (_shape != null)
return _shape; return _shape;
@ -284,23 +345,417 @@ class ButtonThemeData extends Diagnosticable {
/// This property only affects [DropdownButton] and its menu. /// This property only affects [DropdownButton] and its menu.
final bool alignedDropdown; final bool alignedDropdown;
/// The background fill color for [RaisedButton]s.
///
/// This property is null by default.
///
/// See also:
///
/// * [getFillColor], which is used by [RaisedButton] to compute its
/// background fill color.
final Color _buttonColor;
/// The background fill color for disabled [RaisedButton]s.
///
/// This property is null by default.
///
/// See also:
///
/// * [getDisabledFillColor], which is used by [RaisedButton] to compute its
/// background fill color.
final Color _disabledColor;
/// The color of the overlay that appears when a button is pressed.
///
/// This property is null by default.
///
/// See also:
///
/// * [getHighlightColor], which is used by [RaisedButton], [OutlineButton]
/// and [FlatButton].
final Color _highlightColor;
/// The color of the ink "splash" overlay that appears when a button is tapped.
///
/// This property is null by default.
///
/// See also:
///
/// * [getSplashColor], which is used by [RaisedButton], [OutlineButton]
/// and [FlatButton].
final Color _splashColor;
/// A set of thirteen colors that can be used to derive the button theme's
/// colors.
///
/// This property was added much later than the theme's set of highly
/// specific colors, like [ThemeData.buttonColor], [ThemeData.highlightColor],
/// [ThemeData.splashColor] etc.
///
/// The colors for new button classes can be defined exclusively in terms
/// of [colorScheme]. When it's possible, the existing buttons will
/// (continue to) gradually migrate to it.
final ColorScheme colorScheme;
// The minimum size of a button's tap target.
//
// This property is null by default.
//
// See also:
//
// * [getMaterialTargetTapSize], which is used by [RaisedButton],
// [OutlineButton] and [FlatButton].
final MaterialTapTargetSize _materialTapTargetSize;
/// The [button]'s overall brightness.
///
/// Returns the button's [MaterialButton.colorBrightness] if it is non-null,
/// otherwise the color scheme's [ColorScheme.brightness] is returned.
Brightness getBrightness(MaterialButton button) {
return button.colorBrightness ?? colorScheme.brightness;
}
/// Defines the [button]'s base colors, and the defaults for the button's
/// minimum size, internal padding, and shape.
///
/// Despite the name, this property is not the [TextTheme] whose
/// [TextTheme.button] is used as the button text's [TextStyle].
ButtonTextTheme getTextTheme(MaterialButton button) {
return button.textTheme ?? textTheme;
}
Color _getDisabledColor(MaterialButton button) {
return getBrightness(button) == Brightness.dark
? colorScheme.onSurface.withOpacity(0.30) // default == Colors.white30
: colorScheme.onSurface.withOpacity(0.38); // default == Colors.black38;
}
/// The foreground color of the [button]'s text and icon when
/// [MaterialButton.onPressed] is null (when MaterialButton.enabled is false).
///
/// Returns the button's [MaterialButton.disabledColor] if it is non-null.
/// Otherwise the color scheme's [ColorScheme.onSurface] color is returned
/// with its opacity set to 0.30 if [getBrightness] is dark, 0.38 otherwise.
Color getDisabledTextColor(MaterialButton button) {
if (button.disabledTextColor != null)
return button.disabledTextColor;
return _getDisabledColor(button);
}
/// The [button]'s background color when [MaterialButton.onPressed] is null
/// (when MaterialButton.enabled is false).
///
/// Returns the button's [MaterialButton.disabledColor] if it is non-null.
///
/// Otherwise the the value of the `disabledColor` constructor parameter
/// is returned, if it is non-null.
///
/// Otherwise the color scheme's [ColorScheme.onSurface] color is returned
/// with its opacity set to 0.3 if [getBrightness] is dark, 0.38 otherwise.
Color getDisabledFillColor(MaterialButton button) {
if (button.disabledColor != null)
return button.disabledColor;
if (_disabledColor != null)
return _disabledColor;
return _getDisabledColor(button);
}
/// The button's background fill color or null for buttons that don't have
/// a background color.
///
/// Returns [MaterialButton.color] if it is non-null and the button
/// is enabled.
///
/// Otherwise, returns [MaterialButton.disabledColor] if it is non-null and
/// the button is disabled.
///
/// Otherwise, if button is a [FlatButton] or an [OutlineButton] then null is
/// returned.
///
/// Otherwise, if button is a [RaisedButton], returns the `buttonColor`
/// constructor parameter if it was non-null and the button is enabled.
///
/// Otherwise the fill color depends on the value of [getTextTheme].
///
/// * [ButtonTextTheme.normal] or [ButtonTextTheme.accent], the
/// color scheme's [ColorScheme.primary] color if the [button] is enabled
/// the value of [getDisabledFillColor] otherwise.
/// * [ButtonTextTheme.primary], if the [button] is enabled then the value
/// of the `buttonColor` constructor parameter if it is non-null,
/// otherwise the color scheme's ColorScheme.primary color. If the button
/// is not enabled then the colorScheme's [ColorScheme.onSurface] color
/// with opacity 0.12.
Color getFillColor(MaterialButton button) {
final Color fillColor = button.enabled ? button.color : button.disabledColor;
if (fillColor != null)
return fillColor;
if (button is FlatButton || button is OutlineButton)
return null;
if (button.enabled && button is RaisedButton && _buttonColor != null)
return _buttonColor;
switch (getTextTheme(button)) {
case ButtonTextTheme.normal:
case ButtonTextTheme.accent:
return button.enabled ? colorScheme.primary : getDisabledFillColor(button);
case ButtonTextTheme.primary:
return button.enabled
? _buttonColor ?? colorScheme.primary
: colorScheme.onSurface.withOpacity(0.12);
}
assert(false);
return null;
}
/// The foreground color of the [button]'s text and icon.
///
/// If [button] is not [MaterialButton.enabled], the value of
/// [getDisabledTextColor] is returned. If the button is enabled and
/// [buttonTextColor] is non-null, then [buttonTextColor] is returned.
///
/// Otherwise the text color depends on the value of [getTextTheme]
/// and [getBrightness].
///
/// * [ButtonTextTheme.normal], [Colors.white] if [getBrightness] is dark,
/// otherwise [Colors.black87].
/// * ButtonTextTheme.accent], [colorScheme.secondary].
/// * [ButtonTextTheme.primary], if [getFillColor] is dark then [Colors.white],
/// otherwise if [button] is a [FlatButton] or an [OutlineButton] then
/// [colorScheme.primary], otherwise [Colors.black].
Color getTextColor(MaterialButton button) {
if (!button.enabled)
return getDisabledTextColor(button);
if (button.textColor != null)
return button.textColor;
switch (getTextTheme(button)) {
case ButtonTextTheme.normal:
return getBrightness(button) == Brightness.dark ? Colors.white : Colors.black87;
case ButtonTextTheme.accent:
return colorScheme.secondary;
case ButtonTextTheme.primary: {
final Color fillColor = getFillColor(button);
final bool fillIsDark = fillColor != null
? ThemeData.estimateBrightnessForColor(fillColor) == Brightness.dark
: getBrightness(button) == Brightness.dark;
if (fillIsDark)
return Colors.white;
if (button is FlatButton || button is OutlineButton)
return colorScheme.primary;
return Colors.black;
}
}
assert(false);
return null;
}
/// The color of the ink "splash" overlay that appears when the (enabled)
/// [button] is tapped.
///
/// Returns the button's [MaterialButton.splashColor] if it is non-null.
///
/// Otherwise, returns the value of the `splashColor` constructor parameter
/// it is non-null and [button] is a [RaisedButton] or an [OutlineButton].
///
/// Otherwise, returns the value of the `splashColor` constructor parameter
/// if it is non-null and [button] is a [FlatButton] and
/// [getTextTheme] is not [ButtonTextTheme.primary]
///
/// Otherwise, returns [getTextColor] with an opacity of 0.12.
Color getSplashColor(MaterialButton button) {
if (button.splashColor != null)
return button.splashColor;
if (_splashColor != null && (button is RaisedButton || button is OutlineButton))
return _splashColor;
if (_splashColor != null && button is FlatButton) {
switch (getTextTheme(button)) {
case ButtonTextTheme.normal:
case ButtonTextTheme.accent:
return _splashColor;
case ButtonTextTheme.primary:
break;
}
}
return getTextColor(button).withOpacity(0.12);
}
/// The color of the overlay that appears when the [button] is pressed.
///
/// Returns the button's [MaterialButton.highlightColor] if it is non-null.
/// Otherwise the highlight color depends on [getTextTheme]:
///
/// * [ButtonTextTheme.normal], [ButtonTextTheme.accent]: returns the
/// value of the `highlightColor` constructor parameter if it is non-null,
/// otherwise the value of [getTextColor] with opacity 0.16.
/// * [ButtonTextTheme.primary], returns [Colors.transparent].
Color getHighlightColor(MaterialButton button) {
if (button.highlightColor != null)
return button.highlightColor;
switch (getTextTheme(button)) {
case ButtonTextTheme.normal:
case ButtonTextTheme.accent:
return _highlightColor ?? getTextColor(button).withOpacity(0.16);
case ButtonTextTheme.primary:
return Colors.transparent;
}
assert(false);
return Colors.transparent;
}
/// The [button]'s elevation when it is enabled and has not been pressed.
///
/// Returns the button's [MaterialButton.elevation] if it is non-null.
///
/// If button is a [FlatButton] then elevation is 0.0, otherwise it is 2.0.
double getElevation(MaterialButton button) {
if (button.elevation != null)
return button.elevation;
if (button is FlatButton)
return 0.0;
return 2.0;
}
/// The [button]'s elevation when it is enabled and has been pressed.
///
/// Returns the button's [MaterialButton.highlightElevation] if it is non-null.
///
/// If button is a [FlatButton] then the highlight elevation is 0.0, if it's
/// a [OutlineButton] then the highlight elevation is 2.0, otherise the
/// highlight elevation is 8.0.
double getHighlightElevation(MaterialButton button) {
if (button.highlightElevation != null)
return button.highlightElevation;
if (button is FlatButton)
return 0.0;
if (button is OutlineButton)
return 2.0;
return 8.0;
}
/// The [button]'s elevation when [MaterialButton.onPressed] is null (when
/// MaterialButton.enabled is false).
///
/// Returns the button's [MaterialButton.elevation] if it is non-null.
///
/// Otheriwse the disabled elevation is 0.0.
double getDisabledElevation(MaterialButton button) {
if (button.disabledElevation != null)
return button.disabledElevation;
return 0.0;
}
/// Padding for the [button]'s child (typically the button's label).
///
/// Returns the button's [MaterialButton.padding] if it is non-null.
///
/// If this is a button constructed with [RaisedButton.icon] or
/// [FlatButton.icon] or [OutlineButton.icon] then the padding is:
/// `EdgeInsetsDirectional.only(start: 12.0, end: 16.0)`.
///
/// Otherwise, returns [padding] if it is non-null.
///
/// Otherwise, returns horizontal padding of 24.0 on the left and right if
/// [getTextTheme] is [ButtonTextTheme.primary], 16.0 on the left and right
/// otherwise.
EdgeInsetsGeometry getPadding(MaterialButton button) {
if (button.padding != null)
return button.padding;
if (button is MaterialButtonWithIconMixin)
return const EdgeInsetsDirectional.only(start: 12.0, end: 16.0);
if (_padding != null)
return _padding;
switch (getTextTheme(button)) {
case ButtonTextTheme.normal:
case ButtonTextTheme.accent:
return const EdgeInsets.symmetric(horizontal: 16.0);
case ButtonTextTheme.primary:
return const EdgeInsets.symmetric(horizontal: 24.0);
}
assert(false);
return EdgeInsets.zero;
}
/// The shape of the [button]'s [Material].
///
/// Returns the button's [MaterialButton.shape] if it is non-null, otherwise
/// [shape] is returned.
ShapeBorder getShape(MaterialButton button) {
return button.shape ?? shape;
}
/// The duration of the [button]'s highlight animation.
///
/// Returns the button's [MaterialButton.animationDuration] it if is non-null,
/// otherwise 200ms.
Duration getAnimationDuration(MaterialButton button) {
return button.animationDuration ?? kThemeChangeDuration;
}
/// The [BoxConstraints] that the define the [button]'s size.
///
/// By default this method just returns [constraints]. Subclasses
/// could override this method to return a value that was,
/// for example, based on the button's type.
BoxConstraints getConstraints(MaterialButton button) => constraints;
/// The minimum size of the [button]'s tap target.
///
/// Returns the button's [MaterialButton.tapTargetSize] if it is non-null.
///
/// Otherwise the value of the [materialTapTargetSize] constructor
/// parameter is returned if that's non-null.
///
/// Otherwise [MaterialTapTargetSize.padded] is returned.
MaterialTapTargetSize getMaterialTapTargetSize(MaterialButton button) {
return button.materialTapTargetSize ?? _materialTapTargetSize ?? MaterialTapTargetSize.padded;
}
/// Creates a copy of this button theme data object with the matching fields /// Creates a copy of this button theme data object with the matching fields
/// replaced with the non-null parameter values. /// replaced with the non-null parameter values.
ButtonThemeData copyWith({ ButtonThemeData copyWith({
ButtonTextTheme textTheme, ButtonTextTheme textTheme,
ButtonBarLayoutBehavior layoutBehavior,
double minWidth, double minWidth,
double height, double height,
EdgeInsetsGeometry padding, EdgeInsetsGeometry padding,
ShapeBorder shape, ShapeBorder shape,
bool alignedDropdown, bool alignedDropdown,
Color buttonColor,
Color disabledColor,
Color highlightColor,
Color splashColor,
ColorScheme colorScheme,
MaterialTapTargetSize materialTapTargetSize,
}) { }) {
return ButtonThemeData( return ButtonThemeData(
textTheme: textTheme ?? this.textTheme, textTheme: textTheme ?? this.textTheme,
layoutBehavior: layoutBehavior ?? this.layoutBehavior,
minWidth: minWidth ?? this.minWidth, minWidth: minWidth ?? this.minWidth,
height: height ?? this.height, height: height ?? this.height,
padding: padding ?? this.padding, padding: padding ?? this.padding,
shape: shape ?? this.shape, shape: shape ?? this.shape,
alignedDropdown: alignedDropdown ?? this.alignedDropdown, alignedDropdown: alignedDropdown ?? this.alignedDropdown,
buttonColor: buttonColor ?? _buttonColor,
disabledColor: disabledColor ?? _disabledColor,
highlightColor: highlightColor ?? _highlightColor,
splashColor: splashColor ?? _splashColor,
colorScheme: colorScheme ?? this.colorScheme,
materialTapTargetSize: materialTapTargetSize ?? _materialTapTargetSize,
); );
} }
@ -314,7 +769,13 @@ class ButtonThemeData extends Diagnosticable {
&& height == typedOther.height && height == typedOther.height
&& padding == typedOther.padding && padding == typedOther.padding
&& shape == typedOther.shape && shape == typedOther.shape
&& alignedDropdown == typedOther.alignedDropdown; && alignedDropdown == typedOther.alignedDropdown
&& _buttonColor == typedOther._buttonColor
&& _disabledColor == typedOther._disabledColor
&& _highlightColor == typedOther._highlightColor
&& _splashColor == typedOther._splashColor
&& colorScheme == typedOther.colorScheme
&& _materialTapTargetSize == typedOther._materialTapTargetSize;
} }
@override @override
@ -326,6 +787,12 @@ class ButtonThemeData extends Diagnosticable {
padding, padding,
shape, shape,
alignedDropdown, alignedDropdown,
_buttonColor,
_disabledColor,
_highlightColor,
_splashColor,
colorScheme,
_materialTapTargetSize,
); );
} }
@ -343,5 +810,11 @@ class ButtonThemeData extends Diagnosticable {
defaultValue: defaultTheme.alignedDropdown, defaultValue: defaultTheme.alignedDropdown,
ifTrue: 'dropdown width matches button', ifTrue: 'dropdown width matches button',
)); ));
properties.add(DiagnosticsProperty<Color>('buttonColor', _buttonColor, defaultValue: null));
properties.add(DiagnosticsProperty<Color>('disabledColor', _disabledColor, defaultValue: null));
properties.add(DiagnosticsProperty<Color>('highlightColor', _highlightColor, defaultValue: null));
properties.add(DiagnosticsProperty<Color>('splashColor', _splashColor, defaultValue: null));
properties.add(DiagnosticsProperty<ColorScheme>('colorScheme', colorScheme, defaultValue: defaultTheme.colorScheme));
properties.add(DiagnosticsProperty<MaterialTapTargetSize>('materialTapTargetSize', _materialTapTargetSize, defaultValue: null));
} }
} }

View File

@ -0,0 +1,324 @@
// Copyright 2018 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/foundation.dart';
import 'package:flutter/services.dart' show Brightness;
import 'package:flutter/widgets.dart';
import 'colors.dart';
import 'theme_data.dart';
/// A set of twelve colors based on the
/// [Material spec](https://material.io/design/color/the-color-system.html)
/// that can be used to configure the color properties of most components.
///
/// The [Theme] has a color scheme, [ThemeData.colorScheme], which is constructed
/// with [ColorScheme.fromSwatch].
@immutable
class ColorScheme extends Diagnosticable {
/// Create a ColorScheme instance.
const ColorScheme({
@required this.primary,
@required this.primaryVariant,
@required this.secondary,
@required this.secondaryVariant,
@required this.surface,
@required this.background,
@required this.error,
@required this.onPrimary,
@required this.onSecondary,
@required this.onSurface,
@required this.onBackground,
@required this.onError,
@required this.brightness,
}) : assert(primary != null),
assert(primaryVariant != null),
assert(secondary != null),
assert(secondaryVariant != null),
assert(surface != null),
assert(background != null),
assert(error != null),
assert(onPrimary != null),
assert(onSecondary != null),
assert(onSurface != null),
assert(onBackground != null),
assert(onError != null),
assert(brightness != null);
/// Create a ColorScheme based on a purple primary color that matches the
/// [baseline Material color scheme](https://material.io/design/color/the-color-system.html#color-theme-creation).
const ColorScheme.light({
this.primary = const Color(0xff6200ee),
this.primaryVariant = const Color(0xff3700b3),
this.secondary = const Color(0xff03dac6),
this.secondaryVariant = const Color(0xff018786),
this.surface = Colors.white,
this.background = Colors.white,
this.error = const Color(0xffb00020),
this.onPrimary = Colors.white,
this.onSecondary = Colors.black,
this.onSurface = Colors.black,
this.onBackground = Colors.black,
this.onError = Colors.white,
this.brightness = Brightness.light,
}) : assert(primary != null),
assert(primaryVariant != null),
assert(secondary != null),
assert(secondaryVariant != null),
assert(surface != null),
assert(background != null),
assert(error != null),
assert(onPrimary != null),
assert(onSecondary != null),
assert(onSurface != null),
assert(onBackground != null),
assert(onError != null),
assert(brightness != null);
/// Create dark version of the
/// [baseline Material color scheme](https://material.io/design/color/the-color-system.html#color-theme-creation).
const ColorScheme.dark({
this.primary = const Color(0xffbb86fc),
this.primaryVariant = const Color(0xff4b01d0),
this.secondary = const Color(0xff03dac6),
this.secondaryVariant = const Color(0xff03dac6),
this.surface = Colors.black,
this.background = Colors.black,
this.error = const Color(0xffb00020),
this.onPrimary = Colors.black,
this.onSecondary = Colors.black,
this.onSurface = Colors.white,
this.onBackground = Colors.white,
this.onError = Colors.black,
this.brightness = Brightness.dark,
}) : assert(primary != null),
assert(primaryVariant != null),
assert(secondary != null),
assert(secondaryVariant != null),
assert(surface != null),
assert(background != null),
assert(error != null),
assert(onPrimary != null),
assert(onSecondary != null),
assert(onSurface != null),
assert(onBackground != null),
assert(onError != null),
assert(brightness != null);
/// Create a color scheme from a [MaterialColor] swatch.
///
/// This constructor is used by [ThemeData] to create its default
/// color scheme.
factory ColorScheme.fromSwatch({
MaterialColor primarySwatch = Colors.blue,
Color primaryColorDark,
Color accentColor,
Color cardColor,
Color backgroundColor,
Color errorColor,
Brightness brightness = Brightness.light,
}) {
assert(primarySwatch != null);
assert(brightness != null);
final bool isDark = brightness == Brightness.dark;
final bool primaryIsDark = _brightnessFor(primarySwatch) == Brightness.dark;
final Color secondary = accentColor ?? (isDark ? Colors.tealAccent[200] : primarySwatch);
final bool secondaryIsDark = _brightnessFor(secondary) == Brightness.dark;
return ColorScheme(
primary: primarySwatch,
primaryVariant: primaryColorDark ?? (isDark ? Colors.black : primarySwatch[700]),
secondary: secondary,
secondaryVariant: isDark ? Colors.tealAccent[700] : primarySwatch[700],
surface: cardColor ?? (isDark ? Colors.grey[800] : Colors.white),
background: backgroundColor ?? (isDark ? Colors.grey[700] : primarySwatch[200]),
error: errorColor ?? Colors.red[700],
onPrimary: primaryIsDark ? Colors.white : Colors.black,
onSecondary: secondaryIsDark ? Colors.white : Colors.black,
onSurface: isDark ? Colors.white : Colors.black,
onBackground: primaryIsDark ? Colors.white : Colors.black,
onError: isDark ? Colors.black : Colors.white,
brightness: brightness,
);
}
static Brightness _brightnessFor(Color color) => ThemeData.estimateBrightnessForColor(color);
/// The color displayed most frequently across your apps screens and components.
final Color primary;
/// A darker version of the primary color.
final Color primaryVariant;
/// An accent color that, when used sparingly, calls attention to parts
/// of your app.
final Color secondary;
/// A darker version of the secondary color.
final Color secondaryVariant;
/// The background color for widgets like [Card].
final Color surface;
/// A color that typically appears behind scrollable content.
final Color background;
/// The color to use for input validation errors, e.g. for
/// [InputDecoration.errorText].
final Color error;
/// A color that's clearly legible when drawn on [primary].
///
/// To ensure that an app is accessibile, a contrast ratio of 4.5:1 for [primary]
/// and [onPrimary] is recommended. See
/// <https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html>.
final Color onPrimary;
/// A color that's clearly legible when drawn on [secondary].
///
/// To ensure that an app is accessibile, a contrast ratio of 4.5:1 for [secondary]
/// and [onSecondary] is recommended. See
/// <https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html>.
final Color onSecondary;
/// A color that's clearly legible when drawn on [surface].
///
/// To ensure that an app is accessibile, a contrast ratio of 4.5:1 for [surface]
/// and [onSurface] is recommended. See
/// <https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html>.
final Color onSurface;
/// A color that's clearly legible when drawn on [background].
///
/// To ensure that an app is accessibile, a contrast ratio of 4.5:1 for [background]
/// and [onBackground] is recommended. See
/// <https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html>.
final Color onBackground;
/// A color that's clearly legible when drawn on [error].
///
/// To ensure that an app is accessibile, a contrast ratio of 4.5:1 for [error]
/// and [onError] is recommended. See
/// <https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html>.
final Color onError;
/// The overall brightness of this color scheme.
final Brightness brightness;
/// Creates a copy of this color scheme with the given fields
/// replaced by the non-null parameter values.
ColorScheme copyWith({
Color primary,
Color primaryVariant,
Color secondary,
Color secondaryVariant,
Color surface,
Color background,
Color error,
Color onPrimary,
Color onSecondary,
Color onSurface,
Color onBackground,
Color onError,
Brightness brightness,
}) {
return ColorScheme(
primary: primary ?? this.primary,
primaryVariant: primaryVariant ?? this.primaryVariant,
secondary: secondary ?? this.secondary,
secondaryVariant: secondaryVariant ?? this.secondaryVariant,
surface: surface ?? this.surface,
background: background ?? this.background,
error: error ?? this.error,
onPrimary: onPrimary ?? this.onPrimary,
onSecondary: onSecondary ?? this.onSecondary,
onSurface: onSurface ?? this.onSurface,
onBackground: onBackground ?? this.onBackground,
onError: onError ?? this.onError,
brightness: brightness ?? this.brightness,
);
}
/// Linearly interpolate between two [ColorScheme] objects.
///
/// {@macro flutter.material.themeData.lerp}
static ColorScheme lerp(ColorScheme a, ColorScheme b, double t) {
return ColorScheme(
primary: Color.lerp(a.primary, b.primary, t),
primaryVariant: Color.lerp(a.primaryVariant, b.primaryVariant, t),
secondary: Color.lerp(a.secondary, b.secondary, t),
secondaryVariant: Color.lerp(a.secondaryVariant, b.secondaryVariant, t),
surface: Color.lerp(a.surface, b.surface, t),
background: Color.lerp(a.background, b.background, t),
error: Color.lerp(a.error, b.error, t),
onPrimary: Color.lerp(a.onPrimary, b.onPrimary, t),
onSecondary: Color.lerp(a.onSecondary, b.onSecondary, t),
onSurface: Color.lerp(a.onSurface, b.onSurface, t),
onBackground: Color.lerp(a.onBackground, b.onBackground, t),
onError: Color.lerp(a.onError, b.onError, t),
brightness: t < 0.5 ? a.brightness : b.brightness,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other))
return true;
if (other.runtimeType != runtimeType)
return false;
final ColorScheme otherScheme = other;
return otherScheme.primary == primary
&& otherScheme.primaryVariant == primaryVariant
&& otherScheme.secondary == secondary
&& otherScheme.secondaryVariant == secondaryVariant
&& otherScheme.surface == surface
&& otherScheme.background == background
&& otherScheme.error == error
&& otherScheme.onPrimary == onPrimary
&& otherScheme.onSecondary == onSecondary
&& otherScheme.onSurface == onSurface
&& otherScheme.onBackground == onBackground
&& otherScheme.onError == onError
&& otherScheme.brightness == brightness;
}
@override
int get hashCode {
return hashValues(
primary,
primaryVariant,
secondary,
secondaryVariant,
surface,
background,
error,
onPrimary,
onSecondary,
onSurface,
onBackground,
onError,
brightness,
);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
const ColorScheme defaultScheme = ColorScheme.light();
properties.add(DiagnosticsProperty<Color>('primary', primary, defaultValue: defaultScheme.primary));
properties.add(DiagnosticsProperty<Color>('primaryVariant', primaryVariant, defaultValue: defaultScheme.primaryVariant));
properties.add(DiagnosticsProperty<Color>('secondary', secondary, defaultValue: defaultScheme.secondary));
properties.add(DiagnosticsProperty<Color>('secondaryVariant', secondaryVariant, defaultValue: defaultScheme.secondaryVariant));
properties.add(DiagnosticsProperty<Color>('surface', surface, defaultValue: defaultScheme.surface));
properties.add(DiagnosticsProperty<Color>('background', background, defaultValue: defaultScheme.background));
properties.add(DiagnosticsProperty<Color>('error', error, defaultValue: defaultScheme.error));
properties.add(DiagnosticsProperty<Color>('onPrimary', onPrimary, defaultValue: defaultScheme.onPrimary));
properties.add(DiagnosticsProperty<Color>('onSecondary', onSecondary, defaultValue: defaultScheme.onSecondary));
properties.add(DiagnosticsProperty<Color>('onSurface', onSurface, defaultValue: defaultScheme.onSurface));
properties.add(DiagnosticsProperty<Color>('onBackground', onBackground, defaultValue: defaultScheme.onBackground));
properties.add(DiagnosticsProperty<Color>('onError', onError, defaultValue: defaultScheme.onError));
properties.add(DiagnosticsProperty<Brightness>('brightness', brightness, defaultValue: defaultScheme.brightness));
}
}

View File

@ -21,8 +21,8 @@ import 'icons.dart';
import 'ink_well.dart'; import 'ink_well.dart';
import 'material.dart'; import 'material.dart';
import 'material_localizations.dart'; import 'material_localizations.dart';
import 'text_theme.dart';
import 'theme.dart'; import 'theme.dart';
import 'typography.dart';
/// Initial display mode of the date picker dialog. /// Initial display mode of the date picker dialog.
/// ///

View File

@ -7,7 +7,7 @@ import 'package:flutter/widgets.dart';
import 'button.dart'; import 'button.dart';
import 'button_theme.dart'; import 'button_theme.dart';
import 'colors.dart'; import 'material_button.dart';
import 'theme.dart'; import 'theme.dart';
import 'theme_data.dart'; import 'theme_data.dart';
@ -49,26 +49,43 @@ import 'theme_data.dart';
/// * [InkWell], which implements the ink splash part of a flat button. /// * [InkWell], which implements the ink splash part of a flat button.
/// * [RawMaterialButton], the widget this widget is based on. /// * [RawMaterialButton], the widget this widget is based on.
/// * <https://material.google.com/components/buttons.html> /// * <https://material.google.com/components/buttons.html>
class FlatButton extends StatelessWidget { class FlatButton extends MaterialButton {
/// Create a simple text button. /// Create a simple text button.
const FlatButton({ const FlatButton({
Key key, Key key,
@required this.onPressed, @required VoidCallback onPressed,
this.onHighlightChanged, ValueChanged<bool> onHighlightChanged,
this.textTheme, ButtonTextTheme textTheme,
this.textColor, Color textColor,
this.disabledTextColor, Color disabledTextColor,
this.color, Color color,
this.disabledColor, Color disabledColor,
this.highlightColor, Color highlightColor,
this.splashColor, Color splashColor,
this.colorBrightness, Brightness colorBrightness,
this.padding, EdgeInsetsGeometry padding,
this.shape, ShapeBorder shape,
this.clipBehavior = Clip.none, Clip clipBehavior = Clip.none,
this.materialTapTargetSize, MaterialTapTargetSize materialTapTargetSize,
@required this.child, @required Widget child,
}) : assert(clipBehavior != null), super(key: key); }) : super(
key: key,
onPressed: onPressed,
onHighlightChanged: onHighlightChanged,
textTheme: textTheme,
textColor: textColor,
disabledTextColor: disabledTextColor,
color: color,
disabledColor: disabledColor,
highlightColor: highlightColor,
splashColor: splashColor,
colorBrightness: colorBrightness,
padding: padding,
shape: shape,
clipBehavior: clipBehavior,
materialTapTargetSize: materialTapTargetSize,
child: child,
);
/// Create a text button from a pair of widgets that serve as the button's /// Create a text button from a pair of widgets that serve as the button's
/// [icon] and [label]. /// [icon] and [label].
@ -77,244 +94,47 @@ class FlatButton extends StatelessWidget {
/// at the start, and 16 at the end, with an 8 pixel gap in between. /// at the start, and 16 at the end, with an 8 pixel gap in between.
/// ///
/// The [icon], [label], and [clipBehavior] arguments must not be null. /// The [icon], [label], and [clipBehavior] arguments must not be null.
FlatButton.icon({ factory FlatButton.icon({
Key key, Key key,
@required this.onPressed, @required VoidCallback onPressed,
this.onHighlightChanged, ValueChanged<bool> onHighlightChanged,
this.textTheme, ButtonTextTheme textTheme,
this.textColor, Color textColor,
this.disabledTextColor, Color disabledTextColor,
this.color, Color color,
this.disabledColor, Color disabledColor,
this.highlightColor, Color highlightColor,
this.splashColor, Color splashColor,
this.colorBrightness, Brightness colorBrightness,
this.shape, EdgeInsetsGeometry padding,
this.clipBehavior = Clip.none, ShapeBorder shape,
this.materialTapTargetSize, Clip clipBehavior,
MaterialTapTargetSize materialTapTargetSize,
@required Widget icon, @required Widget icon,
@required Widget label, @required Widget label,
}) : assert(icon != null), }) = _FlatButtonWithIcon;
assert(label != null),
assert(clipBehavior != null),
padding = const EdgeInsetsDirectional.only(start: 12.0, end: 16.0),
child = Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
icon,
const SizedBox(width: 8.0),
label,
],
),
super(key: key);
/// Called when the button is tapped or otherwise activated.
///
/// If this is set to null, the button will be disabled, see [enabled].
final VoidCallback onPressed;
/// Called by the underlying [InkWell] widget's [InkWell.onHighlightChanged]
/// callback.
final ValueChanged<bool> onHighlightChanged;
/// Defines the button's base colors, and the defaults for the button's minimum
/// size, internal padding, and shape.
///
/// Defaults to `ButtonTheme.of(context).textTheme`.
final ButtonTextTheme textTheme;
/// The color to use for this button's text.
///
/// The button's [Material.textStyle] will be the current theme's button
/// text style, [ThemeData.textTheme.button], configured with this color.
///
/// The default text color depends on the button theme's text theme,
/// [ButtonThemeData.textTheme].
///
/// See also:
/// * [disabledTextColor], the text color to use when the button has been
/// disabled.
final Color textColor;
/// The color to use for this button's text when the button is disabled.
///
/// The button's [Material.textStyle] will be the current theme's button
/// text style, [ThemeData.textTheme.button], configured with this color.
///
/// The default value is the theme's disabled color,
/// [ThemeData.disabledColor].
///
/// See also:
/// * [textColor] - The color to use for this button's text when the button is [enabled].
final Color disabledTextColor;
/// The button's fill color, displayed by its [Material], while it
/// is in its default (unpressed, enabled) state.
///
/// Typically not specified for [FlatButton]s.
///
/// The default is null.
final Color color;
/// The fill color of the button when the button is disabled.
///
/// Typically not specified for [FlatButton]s.
///
/// The default is null.
final Color disabledColor;
/// The splash color of the button's [InkWell].
///
/// The ink splash indicates that the button has been touched. It
/// appears on top of the button's child and spreads in an expanding
/// circle beginning where the touch occurred.
///
/// If [textTheme] is [ButtonTextTheme.primary], the default splash color is
/// is based on the theme's primary color [ThemeData.primaryColor],
/// otherwise it's the current theme's splash color, [ThemeData.splashColor].
///
/// The appearance of the splash can be configured with the theme's splash
/// factory, [ThemeData.splashFactory].
final Color splashColor;
/// The highlight color of the button's [InkWell].
///
/// The highlight indicates that the button is actively being pressed. It
/// appears on top of the button's child and quickly spreads to fill
/// the button, and then fades out.
///
/// If [textTheme] is [ButtonTextTheme.primary], the default highlight color is
/// transparent (in other words the highlight doesn't appear). Otherwise it's
/// the current theme's highlight color, [ThemeData.highlightColor].
final Color highlightColor;
/// The theme brightness to use for this button.
///
/// Defaults to the theme's brightness, [ThemeData.brightness].
final Brightness colorBrightness;
/// Configures the minimum size of the tap target.
///
/// Defaults to [ThemeData.materialTapTargetSize].
///
/// See also:
///
/// * [MaterialTapTargetSize], for a description of how this affects tap targets.
final MaterialTapTargetSize materialTapTargetSize;
/// The widget below this widget in the tree.
///
/// Typically a [Text] widget in all caps.
final Widget child;
/// Whether the button is enabled or disabled.
///
/// Buttons are disabled by default. To enable a button, set its [onPressed]
/// property to a non-null value.
bool get enabled => onPressed != null;
/// The internal padding for the button's [child].
///
/// Defaults to the value from the current [ButtonTheme],
/// [ButtonThemeData.padding].
final EdgeInsetsGeometry padding;
/// The shape of the button's [Material].
///
/// The button's highlight and splash are clipped to this shape. If the
/// button has an elevation, then its drop shadow is defined by this
/// shape as well.
final ShapeBorder shape;
/// {@macro flutter.widgets.Clip}
final Clip clipBehavior;
Brightness _getBrightness(ThemeData theme) {
return colorBrightness ?? theme.brightness;
}
ButtonTextTheme _getTextTheme(ButtonThemeData buttonTheme) {
return textTheme ?? buttonTheme.textTheme;
}
Color _getTextColor(ThemeData theme, ButtonThemeData buttonTheme, Color fillColor) {
final Color color = enabled ? textColor : disabledTextColor;
if (color != null)
return color;
final bool themeIsDark = _getBrightness(theme) == Brightness.dark;
final bool fillIsDark = fillColor == null
? themeIsDark
: ThemeData.estimateBrightnessForColor(fillColor) == Brightness.dark;
switch (_getTextTheme(buttonTheme)) {
case ButtonTextTheme.normal:
return enabled
? (themeIsDark ? Colors.white : Colors.black87)
: theme.disabledColor;
case ButtonTextTheme.accent:
return enabled
? theme.accentColor
: theme.disabledColor;
case ButtonTextTheme.primary:
return enabled
? (fillIsDark ? Colors.white : theme.primaryColor)
: (themeIsDark ? Colors.white30 : Colors.black38);
}
return null;
}
Color _getSplashColor(ThemeData theme, ButtonThemeData buttonTheme) {
if (splashColor != null)
return splashColor;
switch (_getTextTheme(buttonTheme)) {
case ButtonTextTheme.normal:
case ButtonTextTheme.accent:
return theme.splashColor;
case ButtonTextTheme.primary:
return _getBrightness(theme) == Brightness.dark
? Colors.white12
: theme.primaryColor.withOpacity(0.12);
}
return Colors.transparent;
}
Color _getHighlightColor(ThemeData theme, ButtonThemeData buttonTheme) {
if (highlightColor != null)
return highlightColor;
switch (_getTextTheme(buttonTheme)) {
case ButtonTextTheme.normal:
case ButtonTextTheme.accent:
return theme.highlightColor;
case ButtonTextTheme.primary:
return Colors.transparent;
}
return Colors.transparent;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
final ButtonThemeData buttonTheme = ButtonTheme.of(context); final ButtonThemeData buttonTheme = ButtonTheme.of(context);
final Color fillColor = enabled ? color : disabledColor;
final Color textColor = _getTextColor(theme, buttonTheme, fillColor);
return RawMaterialButton( return RawMaterialButton(
onPressed: onPressed, onPressed: onPressed,
onHighlightChanged: onHighlightChanged, onHighlightChanged: onHighlightChanged,
fillColor: fillColor, clipBehavior: clipBehavior ?? Clip.none,
textStyle: theme.textTheme.button.copyWith(color: textColor), fillColor: buttonTheme.getFillColor(this),
highlightColor: _getHighlightColor(theme, buttonTheme), textStyle: theme.textTheme.button.copyWith(color: buttonTheme.getTextColor(this)),
splashColor: _getSplashColor(theme, buttonTheme), highlightColor: buttonTheme.getHighlightColor(this),
elevation: 0.0, splashColor: buttonTheme.getSplashColor(this),
highlightElevation: 0.0, elevation: buttonTheme.getElevation(this),
materialTapTargetSize: materialTapTargetSize ?? theme.materialTapTargetSize, highlightElevation: buttonTheme.getHighlightElevation(this),
padding: padding ?? buttonTheme.padding, disabledElevation: buttonTheme.getDisabledElevation(this),
constraints: buttonTheme.constraints, padding: buttonTheme.getPadding(this),
shape: shape ?? buttonTheme.shape, constraints: buttonTheme.getConstraints(this),
clipBehavior: clipBehavior, shape: buttonTheme.getShape(this),
animationDuration: buttonTheme.getAnimationDuration(this),
materialTapTargetSize: buttonTheme.getMaterialTapTargetSize(this),
child: child, child: child,
); );
} }
@ -336,3 +156,56 @@ class FlatButton extends StatelessWidget {
properties.add(DiagnosticsProperty<MaterialTapTargetSize>('materialTapTargetSize', materialTapTargetSize, defaultValue: null)); properties.add(DiagnosticsProperty<MaterialTapTargetSize>('materialTapTargetSize', materialTapTargetSize, defaultValue: null));
} }
} }
/// The type of of FlatButtons created with [FlatButton.icon].
///
/// This class only exists to give FlatButtons created with [FlatButton.icon]
/// a distinct class for the sake of [ButtonTheme]. It can not be instantiated.
class _FlatButtonWithIcon extends FlatButton implements MaterialButtonWithIconMixin {
_FlatButtonWithIcon({
Key key,
@required VoidCallback onPressed,
ValueChanged<bool> onHighlightChanged,
ButtonTextTheme textTheme,
Color textColor,
Color disabledTextColor,
Color color,
Color disabledColor,
Color highlightColor,
Color splashColor,
Brightness colorBrightness,
EdgeInsetsGeometry padding,
ShapeBorder shape,
Clip clipBehavior,
MaterialTapTargetSize materialTapTargetSize,
@required Widget icon,
@required Widget label,
}) : assert(icon != null),
assert(label != null),
super(
key: key,
onPressed: onPressed,
onHighlightChanged: onHighlightChanged,
textTheme: textTheme,
textColor: textColor,
disabledTextColor: disabledTextColor,
color: color,
disabledColor: disabledColor,
highlightColor: highlightColor,
splashColor: splashColor,
colorBrightness: colorBrightness,
padding: padding,
shape: shape,
clipBehavior: clipBehavior,
materialTapTargetSize: materialTapTargetSize,
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
icon,
const SizedBox(width: 8.0),
label,
],
),
);
}

View File

@ -0,0 +1,287 @@
// Copyright 2018 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/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'button.dart';
import 'button_theme.dart';
import 'constants.dart';
import 'ink_well.dart';
import 'material.dart';
import 'theme.dart';
import 'theme_data.dart';
/// A utility class for building Material buttons that depend on the
/// ambient [ButtonTheme] and [Theme].
///
/// The button's size will expand to fit the child widget, if necessary.
///
/// MaterialButtons whose [onPressed] handler is null will be disabled. To have
/// an enabled button, make sure to pass a non-null value for onPressed.
///
/// Rather than using this class directly, consider using [FlatButton],
/// OutlineButton, or [RaisedButton], which configure this class with
/// appropriate defaults that match the material design specification.
///
/// To create a button directly, without inheriting theme defaults, use
/// [RawMaterialButton].
///
/// If you want an ink-splash effect for taps, but don't want to use a button,
/// consider using [InkWell] directly.
///
/// See also:
///
/// * [IconButton], to create buttons that contain icons rather than text.
class MaterialButton extends StatelessWidget {
/// Creates a material button.
///
/// Rather than creating a material button directly, consider using
/// [FlatButton] or [RaisedButton]. To create a custom Material button
/// consider using [RawMaterialButton].
///
/// The [clipBehavior] argument must not be null.
const MaterialButton({
Key key,
@required this.onPressed,
this.onHighlightChanged,
this.textTheme,
this.textColor,
this.disabledTextColor,
this.color,
this.disabledColor,
this.highlightColor,
this.splashColor,
this.colorBrightness,
this.elevation,
this.highlightElevation,
this.disabledElevation,
this.padding,
this.shape,
this.clipBehavior = Clip.none,
this.materialTapTargetSize,
this.animationDuration,
this.minWidth,
this.height,
this.child,
}) : super(key: key);
/// The callback that is called when the button is tapped or otherwise activated.
///
/// If this is set to null, the button will be disabled.
final VoidCallback onPressed;
/// Called by the underlying [InkWell] widget's [InkWell.onHighlightChanged]
/// callback.
final ValueChanged<bool> onHighlightChanged;
/// Defines the button's base colors, and the defaults for the button's minimum
/// size, internal padding, and shape.
///
/// Defaults to `ButtonTheme.of(context).textTheme`.
final ButtonTextTheme textTheme;
/// The color to use for this button's text.
///
/// The button's [Material.textStyle] will be the current theme's button
/// text style, [ThemeData.textTheme.button], configured with this color.
///
/// The default text color depends on the button theme's text theme,
/// [ButtonThemeData.textTheme].
///
/// See also:
/// * [disabledTextColor], the text color to use when the button has been
/// disabled.
final Color textColor;
/// The color to use for this button's text when the button is disabled.
///
/// The button's [Material.textStyle] will be the current theme's button
/// text style, [ThemeData.textTheme.button], configured with this color.
///
/// The default value is the theme's disabled color,
/// [ThemeData.disabledColor].
///
/// See also:
/// * [textColor] - The color to use for this button's text when the button is [enabled].
final Color disabledTextColor;
/// The button's fill color, displayed by its [Material], while it
/// is in its default (unpressed, [enabled]) state.
///
/// The default fill color is the theme's button color, [ThemeData.buttonColor].
///
/// See also:
/// * [disabledColor] - the fill color of the button when the button is disabled.
final Color color;
/// The fill color of the button when the button is disabled.
///
/// The default value of this color is the theme's disabled color,
/// [ThemeData.disabledColor].
///
/// See also:
/// * [color] - the fill color of the button when the button is [enabled].
final Color disabledColor;
/// The splash color of the button's [InkWell].
///
/// The ink splash indicates that the button has been touched. It
/// appears on top of the button's child and spreads in an expanding
/// circle beginning where the touch occurred.
///
/// The default splash color is the current theme's splash color,
/// [ThemeData.splashColor].
///
/// The appearance of the splash can be configured with the theme's splash
/// factory, [ThemeData.splashFactory].
final Color splashColor;
/// The highlight color of the button's [InkWell].
///
/// The highlight indicates that the button is actively being pressed. It
/// appears on top of the button's child and quickly spreads to fill
/// the button, and then fades out.
///
/// If [textTheme] is [ButtonTextTheme.primary], the default highlight color is
/// transparent (in other words the highlight doesn't appear). Otherwise it's
/// the current theme's highlight color, [ThemeData.highlightColor].
final Color highlightColor;
/// The z-coordinate at which to place this button. This controls the size of
/// the shadow below the raised button.
///
/// Defaults to 2, the appropriate elevation for raised buttons.
///
/// See also:
///
/// * [FlatButton], a button with no elevation or fill color.
/// * [disabledElevation], the elevation when the button is disabled.
/// * [highlightElevation], the elevation when the button is pressed.
final double elevation;
/// The elevation for the button's [Material] when the button
/// is [enabled] and pressed.
///
/// This controls the size of the shadow below the button. When a tap
/// down gesture occurs within the button, its [InkWell] displays a
/// [highlightColor] "highlight".
///
/// Defaults to 8.0.
///
/// See also:
///
/// * [elevation], the default elevation.
/// * [disabledElevation], the elevation when the button is disabled.
final double highlightElevation;
/// The elevation for the button's [Material] when the button
/// is not [enabled].
///
/// Defaults to 0.0.
///
/// See also:
///
/// * [elevation], the default elevation.
/// * [highlightElevation], the elevation when the button is pressed.
final double disabledElevation;
/// The theme brightness to use for this button.
///
/// Defaults to the theme's brightness, [ThemeData.brightness].
final Brightness colorBrightness;
/// The button's label.
///
/// Often a [Text] widget in all caps.
final Widget child;
/// Whether the button is enabled or disabled.
///
/// Buttons are disabled by default. To enable a button, set its [onPressed]
/// property to a non-null value.
bool get enabled => onPressed != null;
/// The internal padding for the button's [child].
///
/// Defaults to the value from the current [ButtonTheme],
/// [ButtonThemeData.padding].
final EdgeInsetsGeometry padding;
/// The shape of the button's [Material].
///
/// The button's highlight and splash are clipped to this shape. If the
/// button has an elevation, then its drop shadow is defined by this
/// shape as well.
final ShapeBorder shape;
/// {@macro flutter.widgets.Clip}
final Clip clipBehavior;
/// Defines the duration of animated changes for [shape] and [elevation].
///
/// The default value is [kThemeChangeDuration].
final Duration animationDuration;
/// Configures the minimum size of the tap target.
///
/// Defaults to [ThemeData.materialTapTargetSize].
///
/// See also:
///
/// * [MaterialTapTargetSize], for a description of how this affects tap targets.
final MaterialTapTargetSize materialTapTargetSize;
/// The smallest horizontal extent that the button will occupy.
///
/// Defaults to the value from the current [ButtonTheme].
final double minWidth;
/// The vertical extent of the button.
///
/// Defaults to the value from the current [ButtonTheme].
final double height;
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final ButtonThemeData buttonTheme = ButtonTheme.of(context);
return RawMaterialButton(
onPressed: onPressed,
fillColor: color,
textStyle: theme.textTheme.button.copyWith(color: buttonTheme.getTextColor(this)),
highlightColor: highlightColor ?? theme.highlightColor,
splashColor: splashColor ?? theme.splashColor,
elevation: buttonTheme.getElevation(this),
highlightElevation: buttonTheme.getHighlightElevation(this),
padding: buttonTheme.getPadding(this),
constraints: buttonTheme.getConstraints(this).copyWith(
minWidth: minWidth,
minHeight: height,
),
shape: buttonTheme.shape,
clipBehavior: clipBehavior ?? Clip.none,
animationDuration: buttonTheme.getAnimationDuration(this),
child: child,
materialTapTargetSize: materialTapTargetSize ?? theme.materialTapTargetSize,
);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(FlagProperty('enabled', value: enabled, ifFalse: 'disabled'));
}
}
/// The type of [MaterialButton]s created with [RaisedButton.icon], [FlatButton.icon],
/// and [OutlineButton.icon].
///
/// This mixin only exists to give the "label and icon" button widgets a distinct
/// type for the sake of [ButtonTheme].
abstract class MaterialButtonWithIconMixin {
MaterialButtonWithIconMixin._();
}

View File

@ -7,6 +7,7 @@ import 'dart:async';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'text_theme.dart';
import 'time.dart'; import 'time.dart';
import 'typography.dart'; import 'typography.dart';
@ -34,6 +35,10 @@ import 'typography.dart';
// //
// 5. If you are a Google employee, you should then also follow the instructions // 5. If you are a Google employee, you should then also follow the instructions
// at go/flutter-l10n. If you're not, don't worry about it. // at go/flutter-l10n. If you're not, don't worry about it.
//
// 6. If you're adding a String for the sake of Flutter, not for an app-specific
// version of this interface, you are making a breaking API change. See
// https://flutter.io/design-principles/#handling-breaking-changes.
/// Defines the localized resource values used by the Material widgets. /// Defines the localized resource values used by the Material widgets.
/// ///
@ -168,22 +173,17 @@ abstract class MaterialLocalizations {
/// each supported layout. /// each supported layout.
TimeOfDayFormat timeOfDayFormat({ bool alwaysUse24HourFormat = false }); TimeOfDayFormat timeOfDayFormat({ bool alwaysUse24HourFormat = false });
/// Provides geometric text preferences for the current locale. /// Defines the localized [TextStyle] geometry for [ThemeData.textTheme].
/// ///
/// This text theme is incomplete. For example, it lacks text color /// The [scriptCategory] defines the overall geometry of a [TextTheme] for
/// information. This theme must be merged with another text theme that /// the static [MaterialTextGeometry.localizedFor] method in terms of the
/// provides the missing values. /// three language categories defined in https://material.io/go/design-typography.
/// ///
/// Typically a complete theme is obtained via [Theme.of], which can be /// Generally speaking, font sizes for [ScriptCategory.tall] and
/// localized using the [Localizations] widget. /// [ScriptCategory.dense] scripts - for text styles that are smaller than the
/// /// title style - are one unit larger than they are for
/// The text styles provided by this theme are expected to have their /// [ScriptCategory.englishLike] scripts.
/// [TextStyle.inherit] property set to false, so that the [ThemeData] ScriptCategory get scriptCategory;
/// obtained from [Theme.of] no longer inherits text style properties and
/// contains a complete set of properties needed to style a [Text] widget.
///
/// See also: https://material.io/go/design-typography
TextTheme get localTextGeometry;
/// Formats [number] as a decimal, inserting locale-appropriate thousands /// Formats [number] as a decimal, inserting locale-appropriate thousands
/// separators as necessary. /// separators as necessary.
@ -652,6 +652,9 @@ class DefaultMaterialLocalizations implements MaterialLocalizations {
@override @override
String get modalBarrierDismissLabel => 'Dismiss'; String get modalBarrierDismissLabel => 'Dismiss';
@override
ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
TimeOfDayFormat timeOfDayFormat({ bool alwaysUse24HourFormat = false }) { TimeOfDayFormat timeOfDayFormat({ bool alwaysUse24HourFormat = false }) {
return alwaysUse24HourFormat return alwaysUse24HourFormat
@ -659,10 +662,6 @@ class DefaultMaterialLocalizations implements MaterialLocalizations {
: TimeOfDayFormat.h_colon_mm_space_a; : TimeOfDayFormat.h_colon_mm_space_a;
} }
/// Looks up text geometry defined in [MaterialTextGeometry].
@override
TextTheme get localTextGeometry => MaterialTextGeometry.englishLike;
@override @override
String get signedInLabel => 'Signed in'; String get signedInLabel => 'Signed in';

View File

@ -7,6 +7,7 @@ import 'package:flutter/widgets.dart';
import 'button_theme.dart'; import 'button_theme.dart';
import 'colors.dart'; import 'colors.dart';
import 'material_button.dart';
import 'raised_button.dart'; import 'raised_button.dart';
import 'theme.dart'; import 'theme.dart';
@ -48,31 +49,44 @@ const Duration _kElevationDuration = Duration(milliseconds: 75);
/// * [IconButton], to create buttons that just contain icons. /// * [IconButton], to create buttons that just contain icons.
/// * [InkWell], which implements the ink splash part of a flat button. /// * [InkWell], which implements the ink splash part of a flat button.
/// * <https://material.google.com/components/buttons.html> /// * <https://material.google.com/components/buttons.html>
class OutlineButton extends StatefulWidget { class OutlineButton extends MaterialButton {
/// Create a filled button. /// Create a filled button.
/// ///
/// The [highlightElevation], [borderWidth], and [clipBehavior] /// The [highlightElevation], [borderWidth], and [clipBehavior]
/// arguments must not be null. /// arguments must not be null.
const OutlineButton({ const OutlineButton({
Key key, Key key,
@required this.onPressed, @required VoidCallback onPressed,
this.textTheme, ButtonTextTheme textTheme,
this.textColor, Color textColor,
this.disabledTextColor, Color disabledTextColor,
this.color, Color color,
this.highlightColor, Color highlightColor,
this.splashColor, Color splashColor,
this.highlightElevation = 2.0, double highlightElevation,
this.borderSide, this.borderSide,
this.disabledBorderColor, this.disabledBorderColor,
this.highlightedBorderColor, this.highlightedBorderColor,
this.padding, EdgeInsetsGeometry padding,
this.shape, ShapeBorder shape,
this.clipBehavior = Clip.none, Clip clipBehavior = Clip.none,
this.child, Widget child,
}) : assert(highlightElevation != null && highlightElevation >= 0.0), }) : assert(highlightElevation == null || highlightElevation >= 0.0),
assert(clipBehavior != null), super(
super(key: key); key: key,
onPressed: onPressed,
textTheme: textTheme,
textColor: textColor,
disabledTextColor: disabledTextColor,
color: color,
highlightColor: highlightColor,
splashColor: splashColor,
highlightElevation: highlightElevation,
padding: padding,
shape: shape,
clipBehavior: clipBehavior,
child: child,
);
/// Create an outline button from a pair of widgets that serve as the button's /// Create an outline button from a pair of widgets that serve as the button's
/// [icon] and [label]. /// [icon] and [label].
@ -82,125 +96,25 @@ class OutlineButton extends StatefulWidget {
/// ///
/// The [highlightElevation], [icon], [label], and [clipBehavior] must not be /// The [highlightElevation], [icon], [label], and [clipBehavior] must not be
/// null. /// null.
OutlineButton.icon({ factory OutlineButton.icon({
Key key, Key key,
@required this.onPressed, @required VoidCallback onPressed,
this.textTheme, ButtonTextTheme textTheme,
this.textColor, Color textColor,
this.disabledTextColor, Color disabledTextColor,
this.color, Color color,
this.highlightColor, Color highlightColor,
this.splashColor, Color splashColor,
this.highlightElevation = 2.0, double highlightElevation,
this.borderSide, Color highlightedBorderColor,
this.disabledBorderColor, Color disabledBorderColor,
this.highlightedBorderColor, BorderSide borderSide,
this.shape, EdgeInsetsGeometry padding,
this.clipBehavior = Clip.none, ShapeBorder shape,
Clip clipBehavior,
@required Widget icon, @required Widget icon,
@required Widget label, @required Widget label,
}) : assert(highlightElevation != null && highlightElevation >= 0.0), }) = _OutlineButtonWithIcon;
assert(icon != null),
assert(label != null),
assert(clipBehavior != null),
padding = const EdgeInsetsDirectional.only(start: 12.0, end: 16.0),
child = Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
icon,
const SizedBox(width: 8.0),
label,
],
),
super(key: key);
/// Called when the button is tapped or otherwise activated.
///
/// If this is set to null, the button will be disabled, see [enabled].
final VoidCallback onPressed;
/// Defines the button's base colors, and the defaults for the button's minimum
/// size, internal padding, and shape.
///
/// Defaults to `ButtonTheme.of(context).textTheme`.
final ButtonTextTheme textTheme;
/// The color to use for this button's text.
///
/// The button's [Material.textStyle] will be the current theme's button
/// text style, [ThemeData.textTheme.button], configured with this color.
///
/// The default text color depends on the button theme's text theme,
/// [ButtonThemeData.textTheme].
///
/// See also:
///
/// * [disabledTextColor], the text color to use when the button has been
/// disabled.
final Color textColor;
/// The color to use for this button's text when the button is disabled.
///
/// The button's [Material.textStyle] will be the current theme's button
/// text style, [ThemeData.textTheme.button], configured with this color.
///
/// The default value is the theme's disabled color,
/// [ThemeData.disabledColor].
///
/// See also:
///
/// * [textColor], which specifies the color to use for this button's text
/// when the button is [enabled].
final Color disabledTextColor;
/// The button's opaque fill color when it's [enabled] and has been pressed.
///
/// If null this value defaults to white for light themes (see
/// [ThemeData.brightness]), and black for dark themes.
final Color color;
/// The splash color of the button's [InkWell].
///
/// The ink splash indicates that the button has been touched. It
/// appears on top of the button's child and spreads in an expanding
/// circle beginning where the touch occurred.
///
/// If [textTheme] is [ButtonTextTheme.primary], the default splash color is
/// is based on the theme's primary color [ThemeData.primaryColor],
/// otherwise it's the current theme's splash color, [ThemeData.splashColor].
///
/// The appearance of the splash can be configured with the theme's splash
/// factory, [ThemeData.splashFactory].
final Color splashColor;
/// The highlight color of the button's [InkWell].
///
/// The highlight indicates that the button is actively being pressed. It
/// appears on top of the button's child and quickly spreads to fill
/// the button, and then fades out.
///
/// If [textTheme] is [ButtonTextTheme.primary], the default highlight color is
/// transparent (in other words the highlight doesn't appear). Otherwise it's
/// the current theme's highlight color, [ThemeData.highlightColor].
final Color highlightColor;
/// The elevation of the button when it's [enabled] and has been pressed.
///
/// If null, this value defaults to 2.0.
///
/// The elevation of an outline button is always 0.0 unless its enabled
/// and has been pressed.
final double highlightElevation;
/// Defines the color of the border when the button is enabled but not
/// pressed, and the border outline's width and style in general.
///
/// If the border side's [BorderSide.style] is [BorderStyle.none], then
/// an outline is not drawn.
///
/// If null the default border's style is [BorderStyle.solid], its
/// [BorderSide.width] is 2.0, and its color is a light shade of grey.
final BorderSide borderSide;
/// The outline border's color when the button is [enabled] and pressed. /// The outline border's color when the button is [enabled] and pressed.
/// ///
@ -215,36 +129,38 @@ class OutlineButton extends StatefulWidget {
/// dark themes. /// dark themes.
final Color disabledBorderColor; final Color disabledBorderColor;
/// The internal padding for the button's [child]. /// Defines the color of the border when the button is enabled but not
/// pressed, and the border outline's width and style in general.
/// ///
/// Defaults to the value from the current [ButtonTheme], /// If the border side's [BorderSide.style] is [BorderStyle.none], then
/// [ButtonThemeData.padding]. /// an outline is not drawn.
final EdgeInsetsGeometry padding;
/// The shape of the button's [Material] and its outline.
/// ///
/// The button's highlight and splash are clipped to this shape. If the /// If null the default border's style is [BorderStyle.solid], its
/// button has a [highlightElevation], then its drop shadow is defined by this /// [BorderSide.width] is 2.0, and its color is a light shade of grey.
/// shape as well. final BorderSide borderSide;
final ShapeBorder shape;
/// {@macro flutter.widgets.Clip}
final Clip clipBehavior;
/// The button's label.
///
/// Often a [Text] widget in all caps.
final Widget child;
/// Whether the button is enabled or disabled.
///
/// Buttons are disabled by default. To enable a button, set its [onPressed]
/// property to a non-null value.
bool get enabled => onPressed != null;
@override @override
_OutlineButtonState createState() => _OutlineButtonState(); Widget build(BuildContext context) {
final ButtonThemeData buttonTheme = ButtonTheme.of(context);
return _OutlineButton(
onPressed: onPressed,
brightness: buttonTheme.getBrightness(this),
textTheme: textTheme,
textColor: buttonTheme.getTextColor(this),
disabledTextColor: buttonTheme.getDisabledTextColor(this),
color: color,
highlightColor: buttonTheme.getHighlightColor(this),
splashColor: buttonTheme.getSplashColor(this),
highlightElevation: buttonTheme.getHighlightElevation(this),
borderSide: borderSide,
disabledBorderColor: disabledBorderColor,
highlightedBorderColor: highlightedBorderColor ?? buttonTheme.colorScheme.primary,
padding: buttonTheme.getPadding(this),
shape: buttonTheme.getShape(this),
clipBehavior: clipBehavior,
child: child,
);
}
@override @override
void debugFillProperties(DiagnosticPropertiesBuilder properties) { void debugFillProperties(DiagnosticPropertiesBuilder properties) {
@ -256,7 +172,7 @@ class OutlineButton extends StatefulWidget {
properties.add(DiagnosticsProperty<Color>('color', color, defaultValue: null)); properties.add(DiagnosticsProperty<Color>('color', color, defaultValue: null));
properties.add(DiagnosticsProperty<Color>('highlightColor', highlightColor, defaultValue: null)); properties.add(DiagnosticsProperty<Color>('highlightColor', highlightColor, defaultValue: null));
properties.add(DiagnosticsProperty<Color>('splashColor', splashColor, defaultValue: null)); properties.add(DiagnosticsProperty<Color>('splashColor', splashColor, defaultValue: null));
properties.add(DiagnosticsProperty<double>('highlightElevation', highlightElevation, defaultValue: 2.0)); properties.add(DiagnosticsProperty<double>('highlightElevation', highlightElevation, defaultValue: null));
properties.add(DiagnosticsProperty<BorderSide>('borderSide', borderSide, defaultValue: null)); properties.add(DiagnosticsProperty<BorderSide>('borderSide', borderSide, defaultValue: null));
properties.add(DiagnosticsProperty<Color>('disabledBorderColor', disabledBorderColor, defaultValue: null)); properties.add(DiagnosticsProperty<Color>('disabledBorderColor', disabledBorderColor, defaultValue: null));
properties.add(DiagnosticsProperty<Color>('highlightedBorderColor', highlightedBorderColor, defaultValue: null)); properties.add(DiagnosticsProperty<Color>('highlightedBorderColor', highlightedBorderColor, defaultValue: null));
@ -265,7 +181,107 @@ class OutlineButton extends StatefulWidget {
} }
} }
class _OutlineButtonState extends State<OutlineButton> with SingleTickerProviderStateMixin { // The type of of OutlineButtons created with [OutlineButton.icon].
//
// This class only exists to give RaisedButtons created with [RaisedButton.icon]
// a distinct class for the sake of [ButtonTheme]. It can not be instantiated.
class _OutlineButtonWithIcon extends OutlineButton implements MaterialButtonWithIconMixin {
_OutlineButtonWithIcon({
Key key,
@required VoidCallback onPressed,
ButtonTextTheme textTheme,
Color textColor,
Color disabledTextColor,
Color color,
Color highlightColor,
Color splashColor,
double highlightElevation,
Color highlightedBorderColor,
Color disabledBorderColor,
BorderSide borderSide,
EdgeInsetsGeometry padding,
ShapeBorder shape,
Clip clipBehavior,
@required Widget icon,
@required Widget label,
}) : assert(highlightElevation == null || highlightElevation >= 0.0),
assert(icon != null),
assert(label != null),
super(
key: key,
onPressed: onPressed,
textTheme: textTheme,
textColor: textColor,
disabledTextColor: disabledTextColor,
color: color,
highlightColor: highlightColor,
splashColor: splashColor,
highlightElevation: highlightElevation,
disabledBorderColor: disabledBorderColor,
highlightedBorderColor: highlightedBorderColor,
borderSide: borderSide,
padding: padding,
shape: shape,
clipBehavior: clipBehavior,
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
icon,
const SizedBox(width: 8.0),
label,
],
),
);
}
class _OutlineButton extends StatefulWidget {
const _OutlineButton({
Key key,
@required this.onPressed,
this.brightness,
this.textTheme,
this.textColor,
this.disabledTextColor,
this.color,
this.highlightColor,
this.splashColor,
@required this.highlightElevation,
this.borderSide,
this.disabledBorderColor,
@required this.highlightedBorderColor,
this.padding,
this.shape,
this.clipBehavior,
this.child,
}) : assert(highlightElevation != null && highlightElevation >= 0.0),
assert(highlightedBorderColor != null),
super(key: key);
final VoidCallback onPressed;
final Brightness brightness;
final ButtonTextTheme textTheme;
final Color textColor;
final Color disabledTextColor;
final Color color;
final Color splashColor;
final Color highlightColor;
final double highlightElevation;
final BorderSide borderSide;
final Color disabledBorderColor;
final Color highlightedBorderColor;
final EdgeInsetsGeometry padding;
final ShapeBorder shape;
final Clip clipBehavior;
final Widget child;
bool get enabled => onPressed != null;
@override
_OutlineButtonState createState() => _OutlineButtonState();
}
class _OutlineButtonState extends State<_OutlineButton> with SingleTickerProviderStateMixin {
AnimationController _controller; AnimationController _controller;
Animation<double> _fillAnimation; Animation<double> _fillAnimation;
Animation<double> _elevationAnimation; Animation<double> _elevationAnimation;
@ -305,36 +321,8 @@ class _OutlineButtonState extends State<OutlineButton> with SingleTickerProvider
super.dispose(); super.dispose();
} }
ButtonTextTheme _getTextTheme(ButtonThemeData buttonTheme) { Color _getFillColor() {
return widget.textTheme ?? buttonTheme.textTheme; final bool themeIsDark = widget.brightness == Brightness.dark;
}
// TODO(hmuller): this method is the same as FlatButton
Color _getTextColor(ThemeData theme, ButtonThemeData buttonTheme) {
final Color color = widget.enabled ? widget.textColor : widget.disabledTextColor;
if (color != null)
return color;
final bool themeIsDark = theme.brightness == Brightness.dark;
switch (_getTextTheme(buttonTheme)) {
case ButtonTextTheme.normal:
return widget.enabled
? (themeIsDark ? Colors.white : Colors.black87)
: theme.disabledColor;
case ButtonTextTheme.accent:
return widget.enabled
? theme.accentColor
: theme.disabledColor;
case ButtonTextTheme.primary:
return widget.enabled
? theme.buttonColor
: (themeIsDark ? Colors.white30 : Colors.black38);
}
return null;
}
Color _getFillColor(ThemeData theme) {
final bool themeIsDark = theme.brightness == Brightness.dark;
final Color color = widget.color ?? (themeIsDark final Color color = widget.color ?? (themeIsDark
? const Color(0x00000000) ? const Color(0x00000000)
: const Color(0x00FFFFFF)); : const Color(0x00FFFFFF));
@ -345,35 +333,18 @@ class _OutlineButtonState extends State<OutlineButton> with SingleTickerProvider
return colorTween.evaluate(_fillAnimation); return colorTween.evaluate(_fillAnimation);
} }
// TODO(hmuller): this method is the same as FlatButton BorderSide _getOutline() {
Color _getSplashColor(ThemeData theme, ButtonThemeData buttonTheme) { final bool isDark = widget.brightness == Brightness.dark;
if (widget.splashColor != null)
return widget.splashColor;
switch (_getTextTheme(buttonTheme)) {
case ButtonTextTheme.normal:
case ButtonTextTheme.accent:
return theme.splashColor;
case ButtonTextTheme.primary:
return theme.brightness == Brightness.dark
? Colors.white12
: theme.primaryColor.withOpacity(0.12);
}
return Colors.transparent;
}
BorderSide _getOutline(ThemeData theme, ButtonThemeData buttonTheme) {
final bool themeIsDark = theme.brightness == Brightness.dark;
if (widget.borderSide?.style == BorderStyle.none) if (widget.borderSide?.style == BorderStyle.none)
return widget.borderSide; return widget.borderSide;
final Color color = widget.enabled final Color color = widget.enabled
? (_pressed ? (_pressed
? widget.highlightedBorderColor ?? theme.primaryColor ? widget.highlightedBorderColor
: (widget.borderSide?.color ?? : (widget.borderSide?.color ??
(themeIsDark ? Colors.grey[600] : Colors.grey[200]))) (isDark ? Colors.grey[600] : Colors.grey[200])))
: (widget.disabledBorderColor ?? : (widget.disabledBorderColor ??
(themeIsDark ? Colors.grey[800] : Colors.grey[100])); (isDark ? Colors.grey[800] : Colors.grey[100]));
return BorderSide( return BorderSide(
color: color, color: color,
@ -390,19 +361,14 @@ class _OutlineButtonState extends State<OutlineButton> with SingleTickerProvider
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final ButtonThemeData buttonTheme = ButtonTheme.of(context);
final Color textColor = _getTextColor(theme, buttonTheme);
final Color splashColor = _getSplashColor(theme, buttonTheme);
return AnimatedBuilder( return AnimatedBuilder(
animation: _controller, animation: _controller,
builder: (BuildContext context, Widget child) { builder: (BuildContext context, Widget child) {
return RaisedButton( return RaisedButton(
textColor: textColor, textColor: widget.textColor,
disabledTextColor: widget.disabledTextColor, disabledTextColor: widget.disabledTextColor,
color: _getFillColor(theme), color: _getFillColor(),
splashColor: splashColor, splashColor: widget.splashColor,
highlightColor: widget.highlightColor, highlightColor: widget.highlightColor,
disabledColor: Colors.transparent, disabledColor: Colors.transparent,
onPressed: widget.onPressed, onPressed: widget.onPressed,
@ -420,8 +386,8 @@ class _OutlineButtonState extends State<OutlineButton> with SingleTickerProvider
}, },
padding: widget.padding, padding: widget.padding,
shape: _OutlineBorder( shape: _OutlineBorder(
shape: widget.shape ?? buttonTheme.shape, shape: widget.shape,
side: _getOutline(theme, buttonTheme), side: _getOutline(),
), ),
clipBehavior: widget.clipBehavior, clipBehavior: widget.clipBehavior,
animationDuration: _kElevationDuration, animationDuration: _kElevationDuration,

View File

@ -7,8 +7,7 @@ import 'package:flutter/widgets.dart';
import 'button.dart'; import 'button.dart';
import 'button_theme.dart'; import 'button_theme.dart';
import 'colors.dart'; import 'material_button.dart';
import 'constants.dart';
import 'theme.dart'; import 'theme.dart';
import 'theme_data.dart'; import 'theme_data.dart';
@ -41,38 +40,57 @@ import 'theme_data.dart';
/// * [InkWell], which implements the ink splash part of a flat button. /// * [InkWell], which implements the ink splash part of a flat button.
/// * [RawMaterialButton], the widget this widget is based on. /// * [RawMaterialButton], the widget this widget is based on.
/// * <https://material.google.com/components/buttons.html> /// * <https://material.google.com/components/buttons.html>
class RaisedButton extends StatelessWidget { class RaisedButton extends MaterialButton {
/// Create a filled button. /// Create a filled button.
/// ///
/// The [elevation], [highlightElevation], [disabledElevation], and /// The [elevation], [highlightElevation], [disabledElevation], and
/// [clipBehavior] arguments must not be null. /// [clipBehavior] arguments must not be null.
const RaisedButton({ const RaisedButton({
Key key, Key key,
@required this.onPressed, @required VoidCallback onPressed,
this.onHighlightChanged, ValueChanged<bool> onHighlightChanged,
this.textTheme, ButtonTextTheme textTheme,
this.textColor, Color textColor,
this.disabledTextColor, Color disabledTextColor,
this.color, Color color,
this.disabledColor, Color disabledColor,
this.highlightColor, Color highlightColor,
this.splashColor, Color splashColor,
this.colorBrightness, Brightness colorBrightness,
this.elevation = 2.0, double elevation,
this.highlightElevation = 8.0, double highlightElevation,
this.disabledElevation = 0.0, double disabledElevation,
this.padding, EdgeInsetsGeometry padding,
this.shape, ShapeBorder shape,
this.clipBehavior = Clip.none, Clip clipBehavior = Clip.none,
this.materialTapTargetSize, MaterialTapTargetSize materialTapTargetSize,
this.animationDuration = kThemeChangeDuration, Duration animationDuration,
this.child, Widget child,
}) : assert(elevation != null), }) : assert(elevation == null || elevation >= 0.0),
assert(highlightElevation != null), assert(highlightElevation == null || highlightElevation >= 0.0),
assert(disabledElevation != null), assert(disabledElevation == null || disabledElevation >= 0.0),
assert(animationDuration != null), super(
assert(clipBehavior != null), key: key,
super(key: key); onPressed: onPressed,
onHighlightChanged: onHighlightChanged,
textTheme: textTheme,
textColor: textColor,
disabledTextColor: disabledTextColor,
color: color,
disabledColor: disabledColor,
highlightColor: highlightColor,
splashColor: splashColor,
colorBrightness: colorBrightness,
elevation: elevation,
highlightElevation: highlightElevation,
disabledElevation: disabledElevation,
padding: padding,
shape: shape,
clipBehavior: clipBehavior,
materialTapTargetSize: materialTapTargetSize,
animationDuration: animationDuration,
child: child,
);
/// Create a filled button from a pair of widgets that serve as the button's /// Create a filled button from a pair of widgets that serve as the button's
/// [icon] and [label]. /// [icon] and [label].
@ -82,325 +100,51 @@ class RaisedButton extends StatelessWidget {
/// ///
/// The [elevation], [highlightElevation], [disabledElevation], [icon], /// The [elevation], [highlightElevation], [disabledElevation], [icon],
/// [label], and [clipBehavior] arguments must not be null. /// [label], and [clipBehavior] arguments must not be null.
RaisedButton.icon({ factory RaisedButton.icon({
Key key, Key key,
@required this.onPressed, @required VoidCallback onPressed,
this.onHighlightChanged, ValueChanged<bool> onHighlightChanged,
this.textTheme, ButtonTextTheme textTheme,
this.textColor, Color textColor,
this.disabledTextColor, Color disabledTextColor,
this.color, Color color,
this.disabledColor, Color disabledColor,
this.highlightColor, Color highlightColor,
this.splashColor, Color splashColor,
this.colorBrightness, Brightness colorBrightness,
this.elevation = 2.0, double elevation,
this.highlightElevation = 8.0, double highlightElevation,
this.disabledElevation = 0.0, double disabledElevation,
this.shape, ShapeBorder shape,
this.clipBehavior = Clip.none, Clip clipBehavior,
this.materialTapTargetSize, MaterialTapTargetSize materialTapTargetSize,
this.animationDuration = kThemeChangeDuration, Duration animationDuration,
@required Widget icon, @required Widget icon,
@required Widget label, @required Widget label,
}) : assert(elevation != null), }) = _RaisedButtonWithIcon;
assert(highlightElevation != null),
assert(disabledElevation != null),
assert(icon != null),
assert(label != null),
assert(animationDuration != null),
assert(clipBehavior != null),
padding = const EdgeInsetsDirectional.only(start: 12.0, end: 16.0),
child = Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
icon,
const SizedBox(width: 8.0),
label,
],
),
super(key: key);
/// Called when the button is tapped or otherwise activated. @override
///
/// If this is set to null, the button will be disabled, see [enabled].
final VoidCallback onPressed;
/// Called by the underlying [InkWell] widget's [InkWell.onHighlightChanged]
/// callback.
final ValueChanged<bool> onHighlightChanged;
/// Defines the button's base colors, and the defaults for the button's minimum
/// size, internal padding, and shape.
///
/// Defaults to `ButtonTheme.of(context).textTheme`.
final ButtonTextTheme textTheme;
/// The color to use for this button's text.
///
/// The button's [Material.textStyle] will be the current theme's button
/// text style, [ThemeData.textTheme.button], configured with this color.
///
/// The default text color depends on the button theme's text theme,
/// [ButtonThemeData.textTheme].
///
/// See also:
/// * [disabledTextColor], the text color to use when the button has been
/// disabled.
final Color textColor;
/// The color to use for this button's text when the button is disabled.
///
/// The button's [Material.textStyle] will be the current theme's button
/// text style, [ThemeData.textTheme.button], configured with this color.
///
/// The default value is the theme's disabled color,
/// [ThemeData.disabledColor].
///
/// See also:
/// * [textColor] - The color to use for this button's text when the button is [enabled].
final Color disabledTextColor;
/// The button's fill color, displayed by its [Material], while it
/// is in its default (unpressed, [enabled]) state.
///
/// The default fill color is the theme's button color, [ThemeData.buttonColor].
///
/// Typically the default color will be overidden with a Material color,
/// for example:
///
/// ```dart
/// RaisedButton(
/// color: Colors.blue,
/// onPressed: _handleTap,
/// child: Text('DEMO'),
/// ),
/// ```
///
/// See also:
/// * [disabledColor] - the fill color of the button when the button is disabled.
final Color color;
/// The fill color of the button when the button is disabled.
///
/// The default value of this color is the theme's disabled color,
/// [ThemeData.disabledColor].
///
/// See also:
/// * [color] - the fill color of the button when the button is [enabled].
final Color disabledColor;
/// The splash color of the button's [InkWell].
///
/// The ink splash indicates that the button has been touched. It
/// appears on top of the button's child and spreads in an expanding
/// circle beginning where the touch occurred.
///
/// The default splash color is the current theme's splash color,
/// [ThemeData.splashColor].
///
/// The appearance of the splash can be configured with the theme's splash
/// factory, [ThemeData.splashFactory].
final Color splashColor;
/// The highlight color of the button's [InkWell].
///
/// The highlight indicates that the button is actively being pressed. It
/// appears on top of the button's child and quickly spreads to fill
/// the button, and then fades out.
///
/// If [textTheme] is [ButtonTextTheme.primary], the default highlight color is
/// transparent (in other words the highlight doesn't appear). Otherwise it's
/// the current theme's highlight color, [ThemeData.highlightColor].
final Color highlightColor;
/// The z-coordinate at which to place this button. This controls the size of
/// the shadow below the raised button.
///
/// Defaults to 2, the appropriate elevation for raised buttons.
///
/// See also:
///
/// * [FlatButton], a button with no elevation or fill color.
/// * [disabledElevation], the elevation when the button is disabled.
/// * [highlightElevation], the elevation when the button is pressed.
final double elevation;
/// The elevation for the button's [Material] when the button
/// is [enabled] but not pressed.
///
/// Defaults to 2.0.
///
/// See also:
///
/// * [highlightElevation], the default elevation.
/// * [disabledElevation], the elevation when the button is disabled.
/// The elevation for the button's [Material] when the button
/// is [enabled] and pressed.
///
/// This controls the size of the shadow below the button. When a tap
/// down gesture occurs within the button, its [InkWell] displays a
/// [highlightColor] "highlight".
///
/// Defaults to 8.0.
///
/// See also:
///
/// * [elevation], the default elevation.
/// * [disabledElevation], the elevation when the button is disabled.
final double highlightElevation;
/// The elevation for the button's [Material] when the button
/// is not [enabled].
///
/// Defaults to 0.0.
///
/// See also:
///
/// * [elevation], the default elevation.
/// * [highlightElevation], the elevation when the button is pressed.
final double disabledElevation;
/// The theme brightness to use for this button.
///
/// Defaults to the theme's brightness, [ThemeData.brightness].
final Brightness colorBrightness;
/// The button's label.
///
/// Often a [Text] widget in all caps.
final Widget child;
/// Whether the button is enabled or disabled.
///
/// Buttons are disabled by default. To enable a button, set its [onPressed]
/// property to a non-null value.
bool get enabled => onPressed != null;
/// The internal padding for the button's [child].
///
/// Defaults to the value from the current [ButtonTheme],
/// [ButtonThemeData.padding].
final EdgeInsetsGeometry padding;
/// The shape of the button's [Material].
///
/// The button's highlight and splash are clipped to this shape. If the
/// button has an elevation, then its drop shadow is defined by this
/// shape as well.
final ShapeBorder shape;
/// {@macro flutter.widgets.Clip}
final Clip clipBehavior;
/// Defines the duration of animated changes for [shape] and [elevation].
///
/// The default value is [kThemeChangeDuration].
final Duration animationDuration;
/// Configures the minimum size of the tap target.
///
/// Defaults to [ThemeData.materialTapTargetSize].
///
/// See also:
///
/// * [MaterialTapTargetSize], for a description of how this affects tap targets.
final MaterialTapTargetSize materialTapTargetSize;
Brightness _getBrightness(ThemeData theme) {
return colorBrightness ?? theme.brightness;
}
ButtonTextTheme _getTextTheme(ButtonThemeData buttonTheme) {
return textTheme ?? buttonTheme.textTheme;
}
Color _getFillColor(ThemeData theme, ButtonThemeData buttonTheme) {
final Color fillColor = enabled ? color : disabledColor;
if (fillColor != null)
return fillColor;
final bool themeIsDark = _getBrightness(theme) == Brightness.dark;
switch (_getTextTheme(buttonTheme)) {
case ButtonTextTheme.normal:
case ButtonTextTheme.accent:
return enabled
? theme.buttonColor
: theme.disabledColor;
case ButtonTextTheme.primary:
return enabled
? theme.buttonColor
: (themeIsDark ? Colors.white12 : Colors.black12);
}
return null;
}
Color _getTextColor(ThemeData theme, ButtonThemeData buttonTheme, Color fillColor) {
final Color color = enabled ? textColor : disabledTextColor;
if (color != null)
return color;
final bool themeIsDark = _getBrightness(theme) == Brightness.dark;
final bool fillIsDark = fillColor != null
? ThemeData.estimateBrightnessForColor(fillColor) == Brightness.dark
: themeIsDark;
switch (_getTextTheme(buttonTheme)) {
case ButtonTextTheme.normal:
return enabled
? (themeIsDark ? Colors.white : Colors.black87)
: theme.disabledColor;
case ButtonTextTheme.accent:
return enabled
? theme.accentColor
: theme.disabledColor;
case ButtonTextTheme.primary:
return enabled
? (fillIsDark ? Colors.white : Colors.black)
: (themeIsDark ? Colors.white30 : Colors.black38);
}
return null;
}
Color _getHighlightColor(ThemeData theme, ButtonThemeData buttonTheme) {
if (highlightColor != null)
return highlightColor;
switch (_getTextTheme(buttonTheme)) {
case ButtonTextTheme.normal:
case ButtonTextTheme.accent:
return theme.highlightColor;
case ButtonTextTheme.primary:
return Colors.transparent;
}
return Colors.transparent;
}
@override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
final ButtonThemeData buttonTheme = ButtonTheme.of(context); final ButtonThemeData buttonTheme = ButtonTheme.of(context);
final Color fillColor = _getFillColor(theme, buttonTheme);
final Color textColor = _getTextColor(theme, buttonTheme, fillColor);
return RawMaterialButton( return RawMaterialButton(
onPressed: onPressed, onPressed: onPressed,
onHighlightChanged: onHighlightChanged, onHighlightChanged: onHighlightChanged,
fillColor: fillColor, clipBehavior: clipBehavior ?? Clip.none,
textStyle: theme.textTheme.button.copyWith(color: textColor), fillColor: buttonTheme.getFillColor(this),
highlightColor: _getHighlightColor(theme, buttonTheme), textStyle: theme.textTheme.button.copyWith(color: buttonTheme.getTextColor(this)),
splashColor: splashColor ?? theme.splashColor, highlightColor: buttonTheme.getHighlightColor(this),
elevation: elevation, splashColor: buttonTheme.getSplashColor(this),
highlightElevation: highlightElevation, elevation: buttonTheme.getElevation(this),
disabledElevation: disabledElevation, highlightElevation: buttonTheme.getHighlightElevation(this),
padding: padding ?? buttonTheme.padding, disabledElevation: buttonTheme.getDisabledElevation(this),
constraints: buttonTheme.constraints, padding: buttonTheme.getPadding(this),
shape: shape ?? buttonTheme.shape, constraints: buttonTheme.getConstraints(this),
clipBehavior: clipBehavior, shape: buttonTheme.getShape(this),
animationDuration: animationDuration, animationDuration: buttonTheme.getAnimationDuration(this),
materialTapTargetSize: buttonTheme.getMaterialTapTargetSize(this),
child: child, child: child,
materialTapTargetSize: materialTapTargetSize ?? theme.materialTapTargetSize,
); );
} }
@ -422,3 +166,64 @@ class RaisedButton extends StatelessWidget {
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null)); properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
} }
} }
/// The type of of RaisedButtons created with [RaisedButton.icon].
///
/// This class only exists to give RaisedButtons created with [RaisedButton.icon]
/// a distinct class for the sake of [ButtonTheme]. It can not be instantiated.
class _RaisedButtonWithIcon extends RaisedButton implements MaterialButtonWithIconMixin {
_RaisedButtonWithIcon({
Key key,
@required VoidCallback onPressed,
ValueChanged<bool> onHighlightChanged,
ButtonTextTheme textTheme,
Color textColor,
Color disabledTextColor,
Color color,
Color disabledColor,
Color highlightColor,
Color splashColor,
Brightness colorBrightness,
double elevation,
double highlightElevation,
double disabledElevation,
ShapeBorder shape,
Clip clipBehavior = Clip.none,
MaterialTapTargetSize materialTapTargetSize,
Duration animationDuration,
@required Widget icon,
@required Widget label,
}) : assert(elevation == null || elevation >= 0.0),
assert(highlightElevation == null || highlightElevation >= 0.0),
assert(disabledElevation == null || disabledElevation >= 0.0),
assert(icon != null),
assert(label != null),
super(
key: key,
onPressed: onPressed,
onHighlightChanged: onHighlightChanged,
textTheme: textTheme,
textColor: textColor,
disabledTextColor: disabledTextColor,
color: color,
disabledColor: disabledColor,
highlightColor: highlightColor,
splashColor: splashColor,
colorBrightness: colorBrightness,
elevation: elevation,
highlightElevation: highlightElevation,
disabledElevation: disabledElevation,
shape: shape,
clipBehavior: clipBehavior,
materialTapTargetSize: materialTapTargetSize,
animationDuration: animationDuration,
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
icon,
const SizedBox(width: 8.0),
label,
],
),
);
}

View File

@ -12,8 +12,8 @@ import 'icons.dart';
import 'ink_well.dart'; import 'ink_well.dart';
import 'material.dart'; import 'material.dart';
import 'material_localizations.dart'; import 'material_localizations.dart';
import 'text_theme.dart';
import 'theme.dart'; import 'theme.dart';
import 'typography.dart';
// TODO(dragostis): Missing functionality: // TODO(dragostis): Missing functionality:
// * mobile horizontal mode with adding/removing steps // * mobile horizontal mode with adding/removing steps

View File

@ -0,0 +1,540 @@
// Copyright 2018 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/foundation.dart';
import 'package:flutter/painting.dart';
import 'typography.dart';
/// Material design text theme.
///
/// Definitions for the various typographical styles found in material design
/// (e.g., button, caption). Rather than creating a [TextTheme] directly,
/// you can obtain an instance as [Typography.black] or [Typography.white].
///
/// To obtain the current text theme, call [Theme.of] with the current
/// [BuildContext] and read the [ThemeData.textTheme] property.
///
/// The Material Design typography scheme was signficantly changed in the
/// current (2018) version of the specification
/// (https://material.io/design/typography).
///
/// The 2018 spec has thirteen text styles:
/// ```
/// NAME SIZE WEIGHT SPACING
/// headline1 96.0 light -1.5
/// headline2 60.0 light -0.5
/// headline3 48.0 normal 0.0
/// headline4 34.0 normal 0.25
/// headline5 24.0 normal 0.0
/// headline6 20.0 medium 0.15
/// subtitle1 16.0 normal 0.15
/// subtitle2 14.0 medium 0.1
/// body1 16.0 normal 0.5
/// body2 14.0 normal 0.25
/// button 14.0 medium 0.75
/// caption 12.0 normal 0.4
/// overline 10.0 normal 1.5
///
/// ```
/// Where "light" is `FontWeight.w300`, "normal" is `FontWeight.w400` and
/// "medium" is `FontWeight.w500`.
///
/// The [TextTheme] API is based on the original material (2014)
/// design spec, which used different text style names. For backwards
/// compatability's sake, this API continues to use the original
/// names. The table below should help with understanding the API in
/// terms of the 2018 material spec.
///
/// Each of the [TextTheme] text styles corresponds to one of the
/// styles from 2018 spec. By default, the font sizes, font weights
/// and letter spacings have not changed from their original,
/// 2014, values.
/// ```
/// NAME SIZE WEIGHT SPACING 2018 NAME
/// display4 112.0 thin 0.0 headline1
/// display3 56.0 normal 0.0 headline2
/// display2 45.0 normal 0.0 headline3
/// display1 34.0 normal 0.0 headline4
/// headline 24.0 normal 0.0 headline5
/// title 20.0 medium 0.0 headline6
/// subhead 16.0 normal 0.0 subtitle1
/// body2 14.0 medium 0.0 body1
/// body1 14.0 normal 0.0 body2
/// caption 12.0 normal 0.0 caption
/// button 14.0 medium 0.0 button
/// subtitle 14.0 medium 0.0 subtitle2
/// overline 10.0 normal 0.0 overline
/// ```
///
/// Where "thin" is `FontWeight.w100`, "normal" is `FontWeight.w400` and
/// "medium" is `FontWeight.w500`. Letter spacing for all of the original
/// text styles was 0.0.
///
/// To configure a [Theme] for the new sizes, weights, and letter spacings,
/// initialize its [ThemeData.typography] value with a [Typography] that
/// object that specifies the 2018 versions of the geometry themes:
/// [Typography.englishLike2018], [Typography.dense2018],
/// and [Typography.tall2018].
///
/// The following image [from the material design
/// specification](https://material.io/go/design-typography#typography-styles)
/// shows the recommended styles for each of the properties of a [TextTheme].
/// This image uses the `Roboto` font, which is the font used on Android. On
/// iOS, the [San Francisco
/// font](https://developer.apple.com/ios/human-interface-guidelines/visual-design/typography/)
/// is automatically used instead.
///
/// ![To see the image, visit the typography site referenced below.](https://storage.googleapis.com/material-design/publish/material_v_11/assets/0Bzhp5Z4wHba3alhXZ2pPWGk3Zjg/style_typography_styles_scale.png)
///
/// See also:
///
/// * [Typography], the class that generates [TextTheme]s appropriate for a platform.
/// * [Theme], for other aspects of a material design application that can be
/// globally adjusted, such as the color scheme.
/// * <http://material.google.com/style/typography.html>
@immutable
class TextTheme extends Diagnosticable {
/// Creates a text theme that uses the given values.
///
/// Rather than creating a new text theme, consider using [Typography.black]
/// or [Typography.white], which implement the typography styles in the
/// material design specification:
///
/// <https://material.google.com/style/typography.html#typography-styles>
///
/// If you do decide to create your own text theme, consider using one of
/// those predefined themes as a starting point for [copyWith] or [apply].
const TextTheme({
this.display4,
this.display3,
this.display2,
this.display1,
this.headline,
this.title,
this.subhead,
this.body2,
this.body1,
this.caption,
this.button,
this.subtitle,
this.overline,
});
/// Extremely large text.
///
/// The font size is 112 pixels.
final TextStyle display4;
/// Very, very large text.
///
/// Used for the date in the dialog shown by [showDatePicker].
final TextStyle display3;
/// Very large text.
final TextStyle display2;
/// Large text.
final TextStyle display1;
/// Used for large text in dialogs (e.g., the month and year in the dialog
/// shown by [showDatePicker]).
final TextStyle headline;
/// Used for the primary text in app bars and dialogs (e.g., [AppBar.title]
/// and [AlertDialog.title]).
final TextStyle title;
/// Used for the primary text in lists (e.g., [ListTile.title]).
final TextStyle subhead;
/// Used for emphasizing text that would otherwise be [body1].
final TextStyle body2;
/// Used for the default text style for [Material].
final TextStyle body1;
/// Used for auxiliary text associated with images.
final TextStyle caption;
/// Used for text on [RaisedButton] and [FlatButton].
final TextStyle button;
/// For medium emphasis text that's a little smaller than [subhead].
final TextStyle subtitle;
/// The smallest style,
///
/// Typically used for captions or to introduce a (larger) headline.
final TextStyle overline;
/// Creates a copy of this text theme but with the given fields replaced with
/// the new values.
///
/// Consider using [Typography.black] or [Typography.white], which implement
/// the typography styles in the material design specification, as a starting
/// point.
///
/// ## Sample code
///
/// ```dart
/// /// A Widget that sets the ambient theme's title text color for its
/// /// descendants, while leaving other ambient theme attributes alone.
/// class TitleColorThemeCopy extends StatelessWidget {
/// TitleColorThemeCopy({Key key, this.child, this.titleColor}) : super(key: key);
///
/// final Color titleColor;
/// final Widget child;
///
/// @override
/// Widget build(BuildContext context) {
/// final ThemeData theme = Theme.of(context);
/// return Theme(
/// data: theme.copyWith(
/// textTheme: theme.textTheme.copyWith(
/// title: theme.textTheme.title.copyWith(
/// color: titleColor,
/// ),
/// ),
/// ),
/// child: child,
/// );
/// }
/// }
/// ```
///
/// See also:
///
/// * [merge] is used instead of [copyWith] when you want to merge all
/// of the fields of a TextTheme instead of individual fields.
TextTheme copyWith({
TextStyle display4,
TextStyle display3,
TextStyle display2,
TextStyle display1,
TextStyle headline,
TextStyle title,
TextStyle subhead,
TextStyle body2,
TextStyle body1,
TextStyle caption,
TextStyle button,
TextStyle subtitle,
TextStyle overline,
}) {
return TextTheme(
display4: display4 ?? this.display4,
display3: display3 ?? this.display3,
display2: display2 ?? this.display2,
display1: display1 ?? this.display1,
headline: headline ?? this.headline,
title: title ?? this.title,
subhead: subhead ?? this.subhead,
body2: body2 ?? this.body2,
body1: body1 ?? this.body1,
caption: caption ?? this.caption,
button: button ?? this.button,
subtitle: subtitle ?? this.subtitle,
overline: overline ?? this.overline,
);
}
/// Creates a new [TextTheme] where each text style from this object has been
/// merged with the matching text style from the `other` object.
///
/// The merging is done by calling [TextStyle.merge] on each respective pair
/// of text styles from this and the [other] text themes and is subject to
/// the value of [TextStyle.inherit] flag. For more details, see the
/// documentation on [TextStyle.merge] and [TextStyle.inherit].
///
/// If this theme, or the `other` theme has members that are null, then the
/// non-null one (if any) is used. If the `other` theme is itself null, then
/// this [TextTheme] is returned unchanged. If values in both are set, then
/// the values are merged using [TextStyle.merge].
///
/// This is particularly useful if one [TextTheme] defines one set of
/// properties and another defines a different set, e.g. having colors
/// defined in one text theme and font sizes in another, or when one
/// [TextTheme] has only some fields defined, and you want to define the rest
/// by merging it with a default theme.
///
/// ## Sample code
///
/// ```dart
/// /// A Widget that sets the ambient theme's title text color for its
/// /// descendants, while leaving other ambient theme attributes alone.
/// class TitleColorTheme extends StatelessWidget {
/// TitleColorTheme({Key key, this.child, this.titleColor}) : super(key: key);
///
/// final Color titleColor;
/// final Widget child;
///
/// @override
/// Widget build(BuildContext context) {
/// ThemeData theme = Theme.of(context);
/// // This partialTheme is incomplete: it only has the title style
/// // defined. Just replacing theme.textTheme with partialTheme would
/// // set the title, but everything else would be null. This isn't very
/// // useful, so merge it with the existing theme to keep all of the
/// // preexisting definitions for the other styles.
/// TextTheme partialTheme = TextTheme(title: TextStyle(color: titleColor));
/// theme = theme.copyWith(textTheme: theme.textTheme.merge(partialTheme));
/// return Theme(data: theme, child: child);
/// }
/// }
/// ```
///
/// See also:
///
/// * [copyWith] is used instead of [merge] when you wish to override
/// individual fields in the [TextTheme] instead of merging all of the
/// fields of two [TextTheme]s.
TextTheme merge(TextTheme other) {
if (other == null)
return this;
return copyWith(
display4: display4?.merge(other.display4) ?? other.display4,
display3: display3?.merge(other.display3) ?? other.display3,
display2: display2?.merge(other.display2) ?? other.display2,
display1: display1?.merge(other.display1) ?? other.display1,
headline: headline?.merge(other.headline) ?? other.headline,
title: title?.merge(other.title) ?? other.title,
subhead: subhead?.merge(other.subhead) ?? other.subhead,
body2: body2?.merge(other.body2) ?? other.body2,
body1: body1?.merge(other.body1) ?? other.body1,
caption: caption?.merge(other.caption) ?? other.caption,
button: button?.merge(other.button) ?? other.button,
subtitle: subtitle?.merge(other.subtitle) ?? other.subtitle,
overline: overline?.merge(other.overline) ?? other.overline,
);
}
/// Creates a copy of this text theme but with the given field replaced in
/// each of the individual text styles.
///
/// The `displayColor` is applied to [display4], [display3], [display2],
/// [display1], and [caption]. The `bodyColor` is applied to the remaining
/// text styles.
///
/// Consider using [Typography.black] or [Typography.white], which implement
/// the typography styles in the material design specification, as a starting
/// point.
TextTheme apply({
String fontFamily,
double fontSizeFactor = 1.0,
double fontSizeDelta = 0.0,
Color displayColor,
Color bodyColor,
TextDecoration decoration,
Color decorationColor,
TextDecorationStyle decorationStyle,
}) {
return TextTheme(
display4: display4.apply(
color: displayColor,
decoration: decoration,
decorationColor: decorationColor,
decorationStyle: decorationStyle,
fontFamily: fontFamily,
fontSizeFactor: fontSizeFactor,
fontSizeDelta: fontSizeDelta,
),
display3: display3.apply(
color: displayColor,
decoration: decoration,
decorationColor: decorationColor,
decorationStyle: decorationStyle,
fontFamily: fontFamily,
fontSizeFactor: fontSizeFactor,
fontSizeDelta: fontSizeDelta,
),
display2: display2.apply(
color: displayColor,
decoration: decoration,
decorationColor: decorationColor,
decorationStyle: decorationStyle,
fontFamily: fontFamily,
fontSizeFactor: fontSizeFactor,
fontSizeDelta: fontSizeDelta,
),
display1: display1.apply(
color: displayColor,
decoration: decoration,
decorationColor: decorationColor,
decorationStyle: decorationStyle,
fontFamily: fontFamily,
fontSizeFactor: fontSizeFactor,
fontSizeDelta: fontSizeDelta,
),
headline: headline.apply(
color: displayColor,
decoration: decoration,
decorationColor: decorationColor,
decorationStyle: decorationStyle,
fontFamily: fontFamily,
fontSizeFactor: fontSizeFactor,
fontSizeDelta: fontSizeDelta,
),
title: title.apply(
color: displayColor,
decoration: decoration,
decorationColor: decorationColor,
decorationStyle: decorationStyle,
fontFamily: fontFamily,
fontSizeFactor: fontSizeFactor,
fontSizeDelta: fontSizeDelta,
),
subhead: subhead.apply(
color: displayColor,
decoration: decoration,
decorationColor: decorationColor,
decorationStyle: decorationStyle,
fontFamily: fontFamily,
fontSizeFactor: fontSizeFactor,
fontSizeDelta: fontSizeDelta,
),
body2: body2.apply(
color: displayColor,
decoration: decoration,
decorationColor: decorationColor,
decorationStyle: decorationStyle,
fontFamily: fontFamily,
fontSizeFactor: fontSizeFactor,
fontSizeDelta: fontSizeDelta,
),
body1: body1.apply(
color: displayColor,
decoration: decoration,
decorationColor: decorationColor,
decorationStyle: decorationStyle,
fontFamily: fontFamily,
fontSizeFactor: fontSizeFactor,
fontSizeDelta: fontSizeDelta,
),
caption: caption.apply(
color: displayColor,
decoration: decoration,
decorationColor: decorationColor,
decorationStyle: decorationStyle,
fontFamily: fontFamily,
fontSizeFactor: fontSizeFactor,
fontSizeDelta: fontSizeDelta,
),
button: button.apply(
color: bodyColor,
decoration: decoration,
decorationColor: decorationColor,
decorationStyle: decorationStyle,
fontFamily: fontFamily,
fontSizeFactor: fontSizeFactor,
fontSizeDelta: fontSizeDelta,
),
subtitle: subtitle.apply(
color: bodyColor,
decoration: decoration,
decorationColor: decorationColor,
decorationStyle: decorationStyle,
fontFamily: fontFamily,
fontSizeFactor: fontSizeFactor,
fontSizeDelta: fontSizeDelta,
),
overline: overline.apply(
color: bodyColor,
decoration: decoration,
decorationColor: decorationColor,
decorationStyle: decorationStyle,
fontFamily: fontFamily,
fontSizeFactor: fontSizeFactor,
fontSizeDelta: fontSizeDelta,
),
);
}
/// Linearly interpolate between two text themes.
///
/// The arguments must not be null.
///
/// {@macro flutter.material.themeData.lerp}
static TextTheme lerp(TextTheme a, TextTheme b, double t) {
assert(a != null);
assert(b != null);
assert(t != null);
return TextTheme(
display4: TextStyle.lerp(a.display4, b.display4, t),
display3: TextStyle.lerp(a.display3, b.display3, t),
display2: TextStyle.lerp(a.display2, b.display2, t),
display1: TextStyle.lerp(a.display1, b.display1, t),
headline: TextStyle.lerp(a.headline, b.headline, t),
title: TextStyle.lerp(a.title, b.title, t),
subhead: TextStyle.lerp(a.subhead, b.subhead, t),
body2: TextStyle.lerp(a.body2, b.body2, t),
body1: TextStyle.lerp(a.body1, b.body1, t),
caption: TextStyle.lerp(a.caption, b.caption, t),
button: TextStyle.lerp(a.button, b.button, t),
subtitle: TextStyle.lerp(a.subtitle, b.subtitle, t),
overline: TextStyle.lerp(a.overline, b.overline, t),
);
}
@override
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
if (other.runtimeType != runtimeType)
return false;
final TextTheme typedOther = other;
return display4 == typedOther.display4
&& display3 == typedOther.display3
&& display2 == typedOther.display2
&& display1 == typedOther.display1
&& headline == typedOther.headline
&& title == typedOther.title
&& subhead == typedOther.subhead
&& body2 == typedOther.body2
&& body1 == typedOther.body1
&& caption == typedOther.caption
&& button == typedOther.button
&& subtitle == typedOther.subtitle
&& overline == typedOther.overline;
}
@override
int get hashCode {
// The hashValues() function supports up to 20 arguments.
return hashValues(
display4,
display3,
display2,
display1,
headline,
title,
subhead,
body2,
body1,
caption,
button,
subtitle,
overline,
);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
final TextTheme defaultTheme = Typography(platform: defaultTargetPlatform).black;
properties.add(DiagnosticsProperty<TextStyle>('display4', display4, defaultValue: defaultTheme.display4));
properties.add(DiagnosticsProperty<TextStyle>('display3', display3, defaultValue: defaultTheme.display3));
properties.add(DiagnosticsProperty<TextStyle>('display2', display2, defaultValue: defaultTheme.display2));
properties.add(DiagnosticsProperty<TextStyle>('display1', display1, defaultValue: defaultTheme.display1));
properties.add(DiagnosticsProperty<TextStyle>('headline', headline, defaultValue: defaultTheme.headline));
properties.add(DiagnosticsProperty<TextStyle>('title', title, defaultValue: defaultTheme.title));
properties.add(DiagnosticsProperty<TextStyle>('subhead', subhead, defaultValue: defaultTheme.subhead));
properties.add(DiagnosticsProperty<TextStyle>('body2', body2, defaultValue: defaultTheme.body2));
properties.add(DiagnosticsProperty<TextStyle>('body1', body1, defaultValue: defaultTheme.body1));
properties.add(DiagnosticsProperty<TextStyle>('caption', caption, defaultValue: defaultTheme.caption));
properties.add(DiagnosticsProperty<TextStyle>('button', button, defaultValue: defaultTheme.button));
properties.add(DiagnosticsProperty<TextStyle>('subtitle)', subtitle, defaultValue: defaultTheme.subtitle));
properties.add(DiagnosticsProperty<TextStyle>('overline', overline, defaultValue: defaultTheme.overline));
}
}

View File

@ -124,18 +124,17 @@ class Theme extends StatelessWidget {
/// } /// }
/// ``` /// ```
static ThemeData of(BuildContext context, { bool shadowThemeOnly = false }) { static ThemeData of(BuildContext context, { bool shadowThemeOnly = false }) {
final _InheritedTheme inheritedTheme = final _InheritedTheme inheritedTheme = context.inheritFromWidgetOfExactType(_InheritedTheme);
context.inheritFromWidgetOfExactType(_InheritedTheme);
if (shadowThemeOnly) { if (shadowThemeOnly) {
if (inheritedTheme == null || inheritedTheme.theme.isMaterialAppTheme) if (inheritedTheme == null || inheritedTheme.theme.isMaterialAppTheme)
return null; return null;
return inheritedTheme.theme.data; return inheritedTheme.theme.data;
} }
final ThemeData colorTheme = (inheritedTheme != null) ? inheritedTheme.theme.data : _kFallbackTheme;
final MaterialLocalizations localizations = MaterialLocalizations.of(context); final MaterialLocalizations localizations = MaterialLocalizations.of(context);
final TextTheme geometryTheme = localizations?.localTextGeometry ?? MaterialTextGeometry.englishLike; final ScriptCategory category = localizations?.scriptCategory ?? ScriptCategory.englishLike;
return ThemeData.localize(colorTheme, geometryTheme); final ThemeData theme = inheritedTheme?.theme?.data ?? _kFallbackTheme;
return ThemeData.localize(theme, theme.typography.geometryThemeFor(category));
} }
@override @override

View File

@ -10,6 +10,7 @@ import 'package:flutter/widgets.dart';
import 'button_theme.dart'; import 'button_theme.dart';
import 'chip_theme.dart'; import 'chip_theme.dart';
import 'color_scheme.dart';
import 'colors.dart'; import 'colors.dart';
import 'ink_splash.dart'; import 'ink_splash.dart';
import 'ink_well.dart' show InteractiveInkFeatureFactory; import 'ink_well.dart' show InteractiveInkFeatureFactory;
@ -17,6 +18,7 @@ import 'input_decorator.dart';
import 'page_transitions_theme.dart'; import 'page_transitions_theme.dart';
import 'slider_theme.dart'; import 'slider_theme.dart';
import 'tab_bar_theme.dart'; import 'tab_bar_theme.dart';
import 'text_theme.dart';
import 'typography.dart'; import 'typography.dart';
export 'package:flutter/services.dart' show Brightness; export 'package:flutter/services.dart' show Brightness;
@ -147,8 +149,9 @@ class ThemeData extends Diagnosticable {
TargetPlatform platform, TargetPlatform platform,
MaterialTapTargetSize materialTapTargetSize, MaterialTapTargetSize materialTapTargetSize,
PageTransitionsTheme pageTransitionsTheme, PageTransitionsTheme pageTransitionsTheme,
ColorScheme colorScheme,
Typography typography,
}) { }) {
materialTapTargetSize ??= MaterialTapTargetSize.padded;
brightness ??= Brightness.light; brightness ??= Brightness.light;
final bool isDark = brightness == Brightness.dark; final bool isDark = brightness == Brightness.dark;
primarySwatch ??= Colors.blue; primarySwatch ??= Colors.blue;
@ -166,14 +169,22 @@ class ThemeData extends Diagnosticable {
bottomAppBarColor ??= isDark ? Colors.grey[800] : Colors.white; bottomAppBarColor ??= isDark ? Colors.grey[800] : Colors.white;
cardColor ??= isDark ? Colors.grey[800] : Colors.white; cardColor ??= isDark ? Colors.grey[800] : Colors.white;
dividerColor ??= isDark ? const Color(0x1FFFFFFF) : const Color(0x1F000000); dividerColor ??= isDark ? const Color(0x1FFFFFFF) : const Color(0x1F000000);
highlightColor ??= isDark ? _kDarkThemeHighlightColor : _kLightThemeHighlightColor;
splashColor ??= isDark ? _kDarkThemeSplashColor : _kLightThemeSplashColor; // Create a ColorScheme that is backwards compatible as possible
// with the existing default ThemeData color values.
colorScheme ??= ColorScheme.fromSwatch(
primarySwatch: primarySwatch,
primaryColorDark: primaryColorDark,
accentColor: accentColor,
cardColor: cardColor,
backgroundColor: backgroundColor,
errorColor: errorColor,
brightness: brightness,
);
splashFactory ??= InkSplash.splashFactory; splashFactory ??= InkSplash.splashFactory;
selectedRowColor ??= Colors.grey[100]; selectedRowColor ??= Colors.grey[100];
unselectedWidgetColor ??= isDark ? Colors.white70 : Colors.black54; unselectedWidgetColor ??= isDark ? Colors.white70 : Colors.black54;
disabledColor ??= isDark ? Colors.white30 : Colors.black38;
buttonColor ??= isDark ? primarySwatch[600] : Colors.grey[300];
buttonTheme ??= const ButtonThemeData();
// Spec doesn't specify a dark theme secondaryHeaderColor, this is a guess. // Spec doesn't specify a dark theme secondaryHeaderColor, this is a guess.
secondaryHeaderColor ??= isDark ? Colors.grey[700] : primarySwatch[50]; secondaryHeaderColor ??= isDark ? Colors.grey[700] : primarySwatch[50];
textSelectionColor ??= isDark ? accentColor : primarySwatch[200]; textSelectionColor ??= isDark ? accentColor : primarySwatch[200];
@ -191,18 +202,35 @@ class ThemeData extends Diagnosticable {
accentIconTheme ??= accentIsDark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black); accentIconTheme ??= accentIsDark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black);
iconTheme ??= isDark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black87); iconTheme ??= isDark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black87);
platform ??= defaultTargetPlatform; platform ??= defaultTargetPlatform;
final Typography typography = Typography(platform: platform); typography ??= Typography(platform: platform);
final TextTheme defaultTextTheme = isDark ? typography.white : typography.black; final TextTheme defaultTextTheme = isDark ? typography.white : typography.black;
textTheme = defaultTextTheme.merge(textTheme); textTheme = defaultTextTheme.merge(textTheme);
final TextTheme defaultPrimaryTextTheme = primaryIsDark ? typography.white : typography.black; final TextTheme defaultPrimaryTextTheme = primaryIsDark ? typography.white : typography.black;
primaryTextTheme = defaultPrimaryTextTheme.merge(primaryTextTheme); primaryTextTheme = defaultPrimaryTextTheme.merge(primaryTextTheme);
final TextTheme defaultAccentTextTheme = accentIsDark ? typography.white : typography.black; final TextTheme defaultAccentTextTheme = accentIsDark ? typography.white : typography.black;
accentTextTheme = defaultAccentTextTheme.merge(accentTextTheme); accentTextTheme = defaultAccentTextTheme.merge(accentTextTheme);
materialTapTargetSize ??= MaterialTapTargetSize.padded;
if (fontFamily != null) { if (fontFamily != null) {
textTheme = textTheme.apply(fontFamily: fontFamily); textTheme = textTheme.apply(fontFamily: fontFamily);
primaryTextTheme = primaryTextTheme.apply(fontFamily: fontFamily); primaryTextTheme = primaryTextTheme.apply(fontFamily: fontFamily);
accentTextTheme = accentTextTheme.apply(fontFamily: fontFamily); accentTextTheme = accentTextTheme.apply(fontFamily: fontFamily);
} }
// Used as the default color (fill color) for RaisedButtons. Computing the
// default for ButtonThemeData for the sake of backwards compatibility.
buttonColor ??= isDark ? primarySwatch[600] : Colors.grey[300];
buttonTheme ??= ButtonThemeData(
colorScheme: colorScheme,
buttonColor: buttonColor,
disabledColor: disabledColor,
highlightColor: highlightColor,
splashColor: splashColor,
materialTapTargetSize: materialTapTargetSize,
);
disabledColor ??= isDark ? Colors.white30 : Colors.black38;
highlightColor ??= isDark ? _kDarkThemeHighlightColor : _kLightThemeHighlightColor;
splashColor ??= isDark ? _kDarkThemeSplashColor : _kLightThemeSplashColor;
sliderTheme ??= SliderThemeData.fromPrimaryColors( sliderTheme ??= SliderThemeData.fromPrimaryColors(
primaryColor: primaryColor, primaryColor: primaryColor,
primaryColorLight: primaryColorLight, primaryColorLight: primaryColorLight,
@ -235,9 +263,9 @@ class ThemeData extends Diagnosticable {
selectedRowColor: selectedRowColor, selectedRowColor: selectedRowColor,
unselectedWidgetColor: unselectedWidgetColor, unselectedWidgetColor: unselectedWidgetColor,
disabledColor: disabledColor, disabledColor: disabledColor,
buttonTheme: buttonTheme,
buttonColor: buttonColor, buttonColor: buttonColor,
toggleableActiveColor: toggleableActiveColor, toggleableActiveColor: toggleableActiveColor,
buttonTheme: buttonTheme,
secondaryHeaderColor: secondaryHeaderColor, secondaryHeaderColor: secondaryHeaderColor,
textSelectionColor: textSelectionColor, textSelectionColor: textSelectionColor,
cursorColor: cursorColor, cursorColor: cursorColor,
@ -260,6 +288,8 @@ class ThemeData extends Diagnosticable {
platform: platform, platform: platform,
materialTapTargetSize: materialTapTargetSize, materialTapTargetSize: materialTapTargetSize,
pageTransitionsTheme: pageTransitionsTheme, pageTransitionsTheme: pageTransitionsTheme,
colorScheme: colorScheme,
typography: typography,
); );
} }
@ -278,9 +308,9 @@ class ThemeData extends Diagnosticable {
@required this.primaryColorBrightness, @required this.primaryColorBrightness,
@required this.primaryColorLight, @required this.primaryColorLight,
@required this.primaryColorDark, @required this.primaryColorDark,
@required this.canvasColor,
@required this.accentColor, @required this.accentColor,
@required this.accentColorBrightness, @required this.accentColorBrightness,
@required this.canvasColor,
@required this.scaffoldBackgroundColor, @required this.scaffoldBackgroundColor,
@required this.bottomAppBarColor, @required this.bottomAppBarColor,
@required this.cardColor, @required this.cardColor,
@ -291,8 +321,8 @@ class ThemeData extends Diagnosticable {
@required this.selectedRowColor, @required this.selectedRowColor,
@required this.unselectedWidgetColor, @required this.unselectedWidgetColor,
@required this.disabledColor, @required this.disabledColor,
@required this.buttonColor,
@required this.buttonTheme, @required this.buttonTheme,
@required this.buttonColor,
@required this.secondaryHeaderColor, @required this.secondaryHeaderColor,
@required this.textSelectionColor, @required this.textSelectionColor,
@required this.cursorColor, @required this.cursorColor,
@ -316,6 +346,8 @@ class ThemeData extends Diagnosticable {
@required this.platform, @required this.platform,
@required this.materialTapTargetSize, @required this.materialTapTargetSize,
@required this.pageTransitionsTheme, @required this.pageTransitionsTheme,
@required this.colorScheme,
@required this.typography,
}) : assert(brightness != null), }) : assert(brightness != null),
assert(primaryColor != null), assert(primaryColor != null),
assert(primaryColorBrightness != null), assert(primaryColorBrightness != null),
@ -357,7 +389,13 @@ class ThemeData extends Diagnosticable {
assert(chipTheme != null), assert(chipTheme != null),
assert(platform != null), assert(platform != null),
assert(materialTapTargetSize != null), assert(materialTapTargetSize != null),
assert(pageTransitionsTheme != null); assert(pageTransitionsTheme != null),
assert(colorScheme != null),
assert(typography != null);
// Warning: make sure these properties are in the exact same order as in
// hashValues() and in the raw constructor and in the order of fields in
// the class and in the lerp() method.
/// A default light blue theme. /// A default light blue theme.
/// ///
@ -407,6 +445,9 @@ class ThemeData extends Diagnosticable {
/// A darker version of the [primaryColor]. /// A darker version of the [primaryColor].
final Color primaryColorDark; final Color primaryColorDark;
/// The default color of [MaterialType.canvas] [Material].
final Color canvasColor;
/// The foreground color for widgets (knobs, text, overscroll edge effect, etc). /// The foreground color for widgets (knobs, text, overscroll edge effect, etc).
final Color accentColor; final Color accentColor;
@ -415,9 +456,6 @@ class ThemeData extends Diagnosticable {
/// action button). /// action button).
final Brightness accentColorBrightness; final Brightness accentColorBrightness;
/// The default color of [MaterialType.canvas] [Material].
final Color canvasColor;
/// The default color of the [Material] that underlies the [Scaffold]. The /// The default color of the [Material] that underlies the [Scaffold]. The
/// background color for a typical material app or a page within the app. /// background color for a typical material app or a page within the app.
final Color scaffoldBackgroundColor; final Color scaffoldBackgroundColor;
@ -467,17 +505,13 @@ class ThemeData extends Diagnosticable {
/// checked or unchecked). /// checked or unchecked).
final Color disabledColor; final Color disabledColor;
/// The default fill color of the [Material] used in [RaisedButton]s.
final Color buttonColor;
/// The color used to highlight the active states of toggleable widgets like
/// [Switch], [Radio], and [Checkbox].
final Color toggleableActiveColor;
/// Defines the default configuration of button widgets, like [RaisedButton] /// Defines the default configuration of button widgets, like [RaisedButton]
/// and [FlatButton]. /// and [FlatButton].
final ButtonThemeData buttonTheme; final ButtonThemeData buttonTheme;
/// The default fill color of the [Material] used in [RaisedButton]s.
final Color buttonColor;
/// The color of the header of a [PaginatedDataTable] when there are selected rows. /// The color of the header of a [PaginatedDataTable] when there are selected rows.
// According to the spec for data tables: // According to the spec for data tables:
// https://material.google.com/components/data-tables.html#data-tables-tables-within-cards // https://material.google.com/components/data-tables.html#data-tables-tables-within-cards
@ -510,6 +544,10 @@ class ThemeData extends Diagnosticable {
/// The color to use for input validation errors, e.g. in [TextField] fields. /// The color to use for input validation errors, e.g. in [TextField] fields.
final Color errorColor; final Color errorColor;
/// The color used to highlight the active states of toggleable widgets like
/// [Switch], [Radio], and [Checkbox].
final Color toggleableActiveColor;
/// Text with a color that contrasts with the card and canvas colors. /// Text with a color that contrasts with the card and canvas colors.
final TextTheme textTheme; final TextTheme textTheme;
@ -569,6 +607,20 @@ class ThemeData extends Diagnosticable {
/// builder is not found, a builder whose platform is null is used. /// builder is not found, a builder whose platform is null is used.
final PageTransitionsTheme pageTransitionsTheme; final PageTransitionsTheme pageTransitionsTheme;
/// A set of thirteen colors that can be used to configure the
/// color properties of most components.
///
/// This property was added much later than the theme's set of highly
/// specific colors, like [cardColor], [buttonColor], [canvasColor] etc.
/// New components can be defined exclusively in terms of [colorScheme].
/// Existing components will gradually migrate to it, to the extent
/// that is possible without significant backwards compatibility breaks.
final ColorScheme colorScheme;
/// The color and geometry [TextTheme] values used to configure [textTheme],
/// [primaryTextTheme], and [accentTextTheme].
final Typography typography;
/// Creates a copy of this theme but with the given fields replaced with the new values. /// Creates a copy of this theme but with the given fields replaced with the new values.
ThemeData copyWith({ ThemeData copyWith({
Brightness brightness, Brightness brightness,
@ -589,8 +641,8 @@ class ThemeData extends Diagnosticable {
Color selectedRowColor, Color selectedRowColor,
Color unselectedWidgetColor, Color unselectedWidgetColor,
Color disabledColor, Color disabledColor,
Color buttonColor,
ButtonThemeData buttonTheme, ButtonThemeData buttonTheme,
Color buttonColor,
Color secondaryHeaderColor, Color secondaryHeaderColor,
Color textSelectionColor, Color textSelectionColor,
Color cursorColor, Color cursorColor,
@ -614,6 +666,8 @@ class ThemeData extends Diagnosticable {
TargetPlatform platform, TargetPlatform platform,
MaterialTapTargetSize materialTapTargetSize, MaterialTapTargetSize materialTapTargetSize,
PageTransitionsTheme pageTransitionsTheme, PageTransitionsTheme pageTransitionsTheme,
ColorScheme colorScheme,
Typography typography,
}) { }) {
return ThemeData.raw( return ThemeData.raw(
brightness: brightness ?? this.brightness, brightness: brightness ?? this.brightness,
@ -659,6 +713,8 @@ class ThemeData extends Diagnosticable {
platform: platform ?? this.platform, platform: platform ?? this.platform,
materialTapTargetSize: materialTapTargetSize ?? this.materialTapTargetSize, materialTapTargetSize: materialTapTargetSize ?? this.materialTapTargetSize,
pageTransitionsTheme: pageTransitionsTheme ?? this.pageTransitionsTheme, pageTransitionsTheme: pageTransitionsTheme ?? this.pageTransitionsTheme,
colorScheme: colorScheme ?? this.colorScheme,
typography: typography ?? this.typography,
); );
} }
@ -736,6 +792,9 @@ class ThemeData extends Diagnosticable {
assert(a != null); assert(a != null);
assert(b != null); assert(b != null);
assert(t != null); assert(t != null);
// Warning: make sure these properties are in the exact same order as in
// hashValues() and in the raw constructor and in the order of fields in
// the class and in the lerp() method.
return ThemeData.raw( return ThemeData.raw(
brightness: t < 0.5 ? a.brightness : b.brightness, brightness: t < 0.5 ? a.brightness : b.brightness,
primaryColor: Color.lerp(a.primaryColor, b.primaryColor, t), primaryColor: Color.lerp(a.primaryColor, b.primaryColor, t),
@ -755,9 +814,8 @@ class ThemeData extends Diagnosticable {
selectedRowColor: Color.lerp(a.selectedRowColor, b.selectedRowColor, t), selectedRowColor: Color.lerp(a.selectedRowColor, b.selectedRowColor, t),
unselectedWidgetColor: Color.lerp(a.unselectedWidgetColor, b.unselectedWidgetColor, t), unselectedWidgetColor: Color.lerp(a.unselectedWidgetColor, b.unselectedWidgetColor, t),
disabledColor: Color.lerp(a.disabledColor, b.disabledColor, t), disabledColor: Color.lerp(a.disabledColor, b.disabledColor, t),
buttonColor: Color.lerp(a.buttonColor, b.buttonColor, t),
toggleableActiveColor: Color.lerp(a.toggleableActiveColor, b.toggleableActiveColor, t),
buttonTheme: t < 0.5 ? a.buttonTheme : b.buttonTheme, buttonTheme: t < 0.5 ? a.buttonTheme : b.buttonTheme,
buttonColor: Color.lerp(a.buttonColor, b.buttonColor, t),
secondaryHeaderColor: Color.lerp(a.secondaryHeaderColor, b.secondaryHeaderColor, t), secondaryHeaderColor: Color.lerp(a.secondaryHeaderColor, b.secondaryHeaderColor, t),
textSelectionColor: Color.lerp(a.textSelectionColor, b.textSelectionColor, t), textSelectionColor: Color.lerp(a.textSelectionColor, b.textSelectionColor, t),
cursorColor: Color.lerp(a.cursorColor, b.cursorColor, t), cursorColor: Color.lerp(a.cursorColor, b.cursorColor, t),
@ -767,6 +825,7 @@ class ThemeData extends Diagnosticable {
indicatorColor: Color.lerp(a.indicatorColor, b.indicatorColor, t), indicatorColor: Color.lerp(a.indicatorColor, b.indicatorColor, t),
hintColor: Color.lerp(a.hintColor, b.hintColor, t), hintColor: Color.lerp(a.hintColor, b.hintColor, t),
errorColor: Color.lerp(a.errorColor, b.errorColor, t), errorColor: Color.lerp(a.errorColor, b.errorColor, t),
toggleableActiveColor: Color.lerp(a.toggleableActiveColor, b.toggleableActiveColor, t),
textTheme: TextTheme.lerp(a.textTheme, b.textTheme, t), textTheme: TextTheme.lerp(a.textTheme, b.textTheme, t),
primaryTextTheme: TextTheme.lerp(a.primaryTextTheme, b.primaryTextTheme, t), primaryTextTheme: TextTheme.lerp(a.primaryTextTheme, b.primaryTextTheme, t),
accentTextTheme: TextTheme.lerp(a.accentTextTheme, b.accentTextTheme, t), accentTextTheme: TextTheme.lerp(a.accentTextTheme, b.accentTextTheme, t),
@ -780,6 +839,8 @@ class ThemeData extends Diagnosticable {
platform: t < 0.5 ? a.platform : b.platform, platform: t < 0.5 ? a.platform : b.platform,
materialTapTargetSize: t < 0.5 ? a.materialTapTargetSize : b.materialTapTargetSize, materialTapTargetSize: t < 0.5 ? a.materialTapTargetSize : b.materialTapTargetSize,
pageTransitionsTheme: t < 0.5 ? a.pageTransitionsTheme : b.pageTransitionsTheme, pageTransitionsTheme: t < 0.5 ? a.pageTransitionsTheme : b.pageTransitionsTheme,
colorScheme: ColorScheme.lerp(a.colorScheme, b.colorScheme, t),
typography: Typography.lerp(a.typography, b.typography, t),
); );
} }
@ -809,9 +870,8 @@ class ThemeData extends Diagnosticable {
(otherData.selectedRowColor == selectedRowColor) && (otherData.selectedRowColor == selectedRowColor) &&
(otherData.unselectedWidgetColor == unselectedWidgetColor) && (otherData.unselectedWidgetColor == unselectedWidgetColor) &&
(otherData.disabledColor == disabledColor) && (otherData.disabledColor == disabledColor) &&
(otherData.buttonColor == buttonColor) &&
(otherData.toggleableActiveColor == toggleableActiveColor) &&
(otherData.buttonTheme == buttonTheme) && (otherData.buttonTheme == buttonTheme) &&
(otherData.buttonColor == buttonColor) &&
(otherData.secondaryHeaderColor == secondaryHeaderColor) && (otherData.secondaryHeaderColor == secondaryHeaderColor) &&
(otherData.textSelectionColor == textSelectionColor) && (otherData.textSelectionColor == textSelectionColor) &&
(otherData.cursorColor == cursorColor) && (otherData.cursorColor == cursorColor) &&
@ -821,6 +881,7 @@ class ThemeData extends Diagnosticable {
(otherData.indicatorColor == indicatorColor) && (otherData.indicatorColor == indicatorColor) &&
(otherData.hintColor == hintColor) && (otherData.hintColor == hintColor) &&
(otherData.errorColor == errorColor) && (otherData.errorColor == errorColor) &&
(otherData.toggleableActiveColor == toggleableActiveColor) &&
(otherData.textTheme == textTheme) && (otherData.textTheme == textTheme) &&
(otherData.primaryTextTheme == primaryTextTheme) && (otherData.primaryTextTheme == primaryTextTheme) &&
(otherData.accentTextTheme == accentTextTheme) && (otherData.accentTextTheme == accentTextTheme) &&
@ -833,7 +894,9 @@ class ThemeData extends Diagnosticable {
(otherData.chipTheme == chipTheme) && (otherData.chipTheme == chipTheme) &&
(otherData.platform == platform) && (otherData.platform == platform) &&
(otherData.materialTapTargetSize == materialTapTargetSize) && (otherData.materialTapTargetSize == materialTapTargetSize) &&
(otherData.pageTransitionsTheme == pageTransitionsTheme); (otherData.pageTransitionsTheme == pageTransitionsTheme) &&
(otherData.colorScheme == colorScheme) &&
(otherData.typography == typography);
} }
@override @override
@ -860,9 +923,11 @@ class ThemeData extends Diagnosticable {
splashFactory, splashFactory,
selectedRowColor, selectedRowColor,
unselectedWidgetColor, unselectedWidgetColor,
buttonColor, disabledColor,
buttonTheme, buttonTheme,
hashValues( hashValues(
buttonColor,
toggleableActiveColor,
secondaryHeaderColor, secondaryHeaderColor,
textSelectionColor, textSelectionColor,
cursorColor, cursorColor,
@ -872,7 +937,6 @@ class ThemeData extends Diagnosticable {
indicatorColor, indicatorColor,
hintColor, hintColor,
errorColor, errorColor,
toggleableActiveColor,
textTheme, textTheme,
primaryTextTheme, primaryTextTheme,
accentTextTheme, accentTextTheme,
@ -881,12 +945,14 @@ class ThemeData extends Diagnosticable {
primaryIconTheme, primaryIconTheme,
accentIconTheme, accentIconTheme,
sliderTheme, sliderTheme,
tabBarTheme,
hashValues( hashValues(
tabBarTheme,
chipTheme, chipTheme,
platform, platform,
materialTapTargetSize, materialTapTargetSize,
pageTransitionsTheme, pageTransitionsTheme,
colorScheme,
typography,
), ),
), ),
); );
@ -936,6 +1002,8 @@ class ThemeData extends Diagnosticable {
properties.add(DiagnosticsProperty<ChipThemeData>('chipTheme', chipTheme)); properties.add(DiagnosticsProperty<ChipThemeData>('chipTheme', chipTheme));
properties.add(DiagnosticsProperty<MaterialTapTargetSize>('materialTapTargetSize', materialTapTargetSize)); properties.add(DiagnosticsProperty<MaterialTapTargetSize>('materialTapTargetSize', materialTapTargetSize));
properties.add(DiagnosticsProperty<PageTransitionsTheme>('pageTransitionsTheme', pageTransitionsTheme)); properties.add(DiagnosticsProperty<PageTransitionsTheme>('pageTransitionsTheme', pageTransitionsTheme));
properties.add(DiagnosticsProperty<ColorScheme>('colorScheme', colorScheme, defaultValue: defaultData.colorScheme));
properties.add(DiagnosticsProperty<Typography>('typography', typography, defaultValue: defaultData.typography));
} }
} }

View File

@ -16,10 +16,10 @@ import 'dialog.dart';
import 'feedback.dart'; import 'feedback.dart';
import 'flat_button.dart'; import 'flat_button.dart';
import 'material_localizations.dart'; import 'material_localizations.dart';
import 'text_theme.dart';
import 'theme.dart'; import 'theme.dart';
import 'theme_data.dart'; import 'theme_data.dart';
import 'time.dart'; import 'time.dart';
import 'typography.dart';
const Duration _kDialAnimateDuration = Duration(milliseconds: 200); const Duration _kDialAnimateDuration = Duration(milliseconds: 200);
const double _kTwoPi = 2 * math.pi; const double _kTwoPi = 2 * math.pi;

File diff suppressed because it is too large Load Diff

View File

@ -64,4 +64,56 @@ void main() {
final Finder buttonBar = find.byType(ButtonBar); final Finder buttonBar = find.byType(ButtonBar);
expect(tester.getBottomRight(buttonBar).dy - tester.getTopRight(buttonBar).dy, 26.0); expect(tester.getBottomRight(buttonBar).dy - tester.getTopRight(buttonBar).dy, 26.0);
}); });
testWidgets('ButtonBar FlatButton inherits Theme accentColor', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/22789
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(accentColor: const Color(1)),
home: Builder(
builder: (BuildContext context) {
return Center(
child: ButtonTheme.bar(
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('button'),
onPressed: () {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog( // puts its actions in a ButtonBar
actions: <Widget>[
FlatButton(
onPressed: () { },
child: const Text('enabled'),
),
],
);
},
);
},
),
],
),
),
);
},
),
),
);
expect(tester.widget<RawMaterialButton>(find.byType(RawMaterialButton)).textStyle.color, const Color(1));
// Show the dialog
await tester.tap(find.text('button'));
await tester.pumpAndSettle();
final Finder dialogButton = find.ancestor(
of: find.text('enabled'),
matching: find.byType(RawMaterialButton),
);
expect(tester.widget<RawMaterialButton>(dialogButton).textStyle.color, const Color(1));
});
} }

View File

@ -36,9 +36,12 @@ void main() {
testWidgets('ButtonTheme defaults', (WidgetTester tester) async { testWidgets('ButtonTheme defaults', (WidgetTester tester) async {
ButtonTextTheme textTheme; ButtonTextTheme textTheme;
ButtonBarLayoutBehavior layoutBehavior;
BoxConstraints constraints; BoxConstraints constraints;
EdgeInsets padding; EdgeInsets padding;
ShapeBorder shape; ShapeBorder shape;
bool alignedDropdown;
ColorScheme colorScheme;
await tester.pumpWidget( await tester.pumpWidget(
ButtonTheme( ButtonTheme(
@ -49,13 +52,16 @@ void main() {
constraints = theme.constraints; constraints = theme.constraints;
padding = theme.padding; padding = theme.padding;
shape = theme.shape; shape = theme.shape;
layoutBehavior = theme.layoutBehavior;
colorScheme = theme.colorScheme;
alignedDropdown = theme.alignedDropdown;
return Container( return Container(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: const Directionality( child: Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: FlatButton( child: FlatButton(
onPressed: null, onPressed: () { },
child: Text('b'), // intrinsic width < minimum width child: const Text('b'), // intrinsic width < minimum width
), ),
), ),
); );
@ -65,12 +71,14 @@ void main() {
); );
expect(textTheme, ButtonTextTheme.normal); expect(textTheme, ButtonTextTheme.normal);
expect(layoutBehavior, ButtonBarLayoutBehavior.padded);
expect(constraints, const BoxConstraints(minWidth: 88.0, minHeight: 36.0)); expect(constraints, const BoxConstraints(minWidth: 88.0, minHeight: 36.0));
expect(padding, const EdgeInsets.symmetric(horizontal: 16.0)); expect(padding, const EdgeInsets.symmetric(horizontal: 16.0));
expect(shape, const RoundedRectangleBorder( expect(shape, const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(2.0)), borderRadius: BorderRadius.all(Radius.circular(2.0)),
)); ));
expect(alignedDropdown, false);
expect(colorScheme, ThemeData.light().colorScheme);
expect(tester.widget<Material>(find.byType(Material)).shape, shape); expect(tester.widget<Material>(find.byType(Material)).shape, shape);
expect(tester.getSize(find.byType(Material)), const Size(88.0, 36.0)); expect(tester.getSize(find.byType(Material)), const Size(88.0, 36.0));
}); });
@ -78,26 +86,33 @@ void main() {
test('ButtonThemeData.copyWith', () { test('ButtonThemeData.copyWith', () {
ButtonThemeData theme = const ButtonThemeData().copyWith(); ButtonThemeData theme = const ButtonThemeData().copyWith();
expect(theme.textTheme, ButtonTextTheme.normal); expect(theme.textTheme, ButtonTextTheme.normal);
expect(theme.layoutBehavior, ButtonBarLayoutBehavior.padded);
expect(theme.constraints, const BoxConstraints(minWidth: 88.0, minHeight: 36.0)); expect(theme.constraints, const BoxConstraints(minWidth: 88.0, minHeight: 36.0));
expect(theme.padding, const EdgeInsets.symmetric(horizontal: 16.0)); expect(theme.padding, const EdgeInsets.symmetric(horizontal: 16.0));
expect(theme.shape, const RoundedRectangleBorder( expect(theme.shape, const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(2.0)), borderRadius: BorderRadius.all(Radius.circular(2.0)),
)); ));
expect(theme.alignedDropdown, false); expect(theme.alignedDropdown, false);
expect(theme.colorScheme, null);
theme = const ButtonThemeData().copyWith( theme = const ButtonThemeData().copyWith(
textTheme: ButtonTextTheme.primary, textTheme: ButtonTextTheme.primary,
layoutBehavior: ButtonBarLayoutBehavior.constrained,
minWidth: 100.0, minWidth: 100.0,
height: 200.0, height: 200.0,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
shape: const StadiumBorder(), shape: const StadiumBorder(),
alignedDropdown: true, alignedDropdown: true,
colorScheme: const ColorScheme.dark(),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
); );
expect(theme.textTheme, ButtonTextTheme.primary); expect(theme.textTheme, ButtonTextTheme.primary);
expect(theme.layoutBehavior, ButtonBarLayoutBehavior.constrained);
expect(theme.constraints, const BoxConstraints(minWidth: 100.0, minHeight: 200.0)); expect(theme.constraints, const BoxConstraints(minWidth: 100.0, minHeight: 200.0));
expect(theme.padding, EdgeInsets.zero); expect(theme.padding, EdgeInsets.zero);
expect(theme.shape, const StadiumBorder()); expect(theme.shape, const StadiumBorder());
expect(theme.alignedDropdown, true); expect(theme.alignedDropdown, true);
expect(theme.colorScheme, const ColorScheme.dark());
}); });
testWidgets('Theme buttonTheme defaults', (WidgetTester tester) async { testWidgets('Theme buttonTheme defaults', (WidgetTester tester) async {
@ -107,10 +122,12 @@ void main() {
EdgeInsets padding; EdgeInsets padding;
ShapeBorder shape; ShapeBorder shape;
const Color disabledColor = Color(0xFF00FF00);
await tester.pumpWidget( await tester.pumpWidget(
Theme( Theme(
data: lightTheme.copyWith( data: lightTheme.copyWith(
disabledColor: const Color(0xFF00FF00), // disabled RaisedButton fill color disabledColor: disabledColor, // disabled RaisedButton fill color
buttonTheme: const ButtonThemeData(disabledColor: disabledColor),
textTheme: lightTheme.textTheme.copyWith( textTheme: lightTheme.textTheme.copyWith(
button: lightTheme.textTheme.button.copyWith( button: lightTheme.textTheme.button.copyWith(
// The button's height will match because there's no // The button's height will match because there's no
@ -149,7 +166,7 @@ void main() {
)); ));
expect(tester.widget<Material>(find.byType(Material)).shape, shape); expect(tester.widget<Material>(find.byType(Material)).shape, shape);
expect(tester.widget<Material>(find.byType(Material)).color, const Color(0xFF00FF00)); expect(tester.widget<Material>(find.byType(Material)).color, disabledColor);
expect(tester.getSize(find.byType(Material)), const Size(88.0, 48.0)); expect(tester.getSize(find.byType(Material)), const Size(88.0, 48.0));
}); });
@ -169,6 +186,7 @@ void main() {
minWidth: 100.0, minWidth: 100.0,
height: 200.0, height: 200.0,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
buttonColor: const Color(0xFF00FF00), // enabled RaisedButton fill color
shape: const RoundedRectangleBorder(), shape: const RoundedRectangleBorder(),
child: Builder( child: Builder(
builder: (BuildContext context) { builder: (BuildContext context) {

View File

@ -330,7 +330,7 @@ void main() {
expect(text.text.style.color, Colors.black38); expect(text.text.style.color, Colors.black38);
}); });
testWidgets('Disabled MaterialButton has same semantic size as enabled and exposes disabled semantics', (WidgetTester tester) async { testWidgets('Disabled MaterialButton has same semantic size as enabled and exposes disabled semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester); final SemanticsTester semantics = SemanticsTester(tester);
final Rect expectedButtonSize = Rect.fromLTRB(0.0, 0.0, 116.0, 48.0); final Rect expectedButtonSize = Rect.fromLTRB(0.0, 0.0, 116.0, 48.0);
@ -409,6 +409,73 @@ void main() {
semantics.dispose(); semantics.dispose();
}); });
testWidgets('MaterialButton minWidth and height parameters', (WidgetTester tester) async {
Widget buildFrame({ double minWidth, double height, EdgeInsets padding = EdgeInsets.zero, Widget child }) {
return Directionality(
textDirection: TextDirection.ltr,
child: Center(
child: MaterialButton(
padding: padding,
minWidth: minWidth,
height: height,
onPressed: null,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
child: child,
),
),
);
}
await tester.pumpWidget(buildFrame(minWidth: 8.0, height: 24.0));
expect(tester.getSize(find.byType(MaterialButton)), const Size(8.0, 24.0));
await tester.pumpWidget(buildFrame(minWidth: 8.0));
// Default minHeight constraint is 36, see RawMaterialButton.
expect(tester.getSize(find.byType(MaterialButton)), const Size(8.0, 36.0));
await tester.pumpWidget(buildFrame(height: 8.0));
// Default minWidth constraint is 88, see RawMaterialButton.
expect(tester.getSize(find.byType(MaterialButton)), const Size(88.0, 8.0));
await tester.pumpWidget(buildFrame());
expect(tester.getSize(find.byType(MaterialButton)), const Size(88.0, 36.0));
await tester.pumpWidget(buildFrame(padding: const EdgeInsets.all(4.0)));
expect(tester.getSize(find.byType(MaterialButton)), const Size(88.0, 36.0));
// Size is defined by the padding.
await tester.pumpWidget(
buildFrame(
minWidth: 0.0,
height: 0.0,
padding: const EdgeInsets.all(4.0),
),
);
expect(tester.getSize(find.byType(MaterialButton)), const Size(8.0, 8.0));
// Size is defined by the padded child.
await tester.pumpWidget(
buildFrame(
minWidth: 0.0,
height: 0.0,
padding: const EdgeInsets.all(4.0),
child: const SizedBox(width: 8.0, height: 8.0),
),
);
expect(tester.getSize(find.byType(MaterialButton)), const Size(16.0, 16.0));
// Size is defined by the minWidth, height constraints.
await tester.pumpWidget(
buildFrame(
minWidth: 18.0,
height: 18.0,
padding: const EdgeInsets.all(4.0),
child: const SizedBox(width: 8.0, height: 8.0),
),
);
expect(tester.getSize(find.byType(MaterialButton)), const Size(18.0, 18.0));
});
testWidgets('MaterialButton size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async { testWidgets('MaterialButton size is configurable by ThemeData.materialTapTargetSize', (WidgetTester tester) async {
final Key key1 = UniqueKey(); final Key key1 = UniqueKey();
await tester.pumpWidget( await tester.pumpWidget(

View File

@ -89,7 +89,7 @@ void main() {
// Expect that the button is disabled and painted with the disabled border color. // Expect that the button is disabled and painted with the disabled border color.
expect(tester.widget<OutlineButton>(outlineButton).enabled, false); expect(tester.widget<OutlineButton>(outlineButton).enabled, false);
expect( expect(
outlineButton, outlineButton, //find.byType(OutlineButton),
paints paints
..clipPath(pathMatcher: coversSameAreaAs(clipPath, areaToCompare: clipRect.inflate(10.0))) ..clipPath(pathMatcher: coversSameAreaAs(clipPath, areaToCompare: clipRect.inflate(10.0)))
..path(color: disabledBorderColor, strokeWidth: borderWidth)); ..path(color: disabledBorderColor, strokeWidth: borderWidth));

View File

@ -915,7 +915,7 @@ void main() {
); );
final Text helperText = tester.widget(find.text('helper text')); final Text helperText = tester.widget(find.text('helper text'));
expect(helperText.style.color, themeData.hintColor); expect(helperText.style.color, themeData.hintColor);
expect(helperText.style.fontSize, MaterialTextGeometry.englishLike.caption.fontSize); expect(helperText.style.fontSize, Typography.englishLike2014.caption.fontSize);
}); });
testWidgets('TextField with specified helperStyle', (WidgetTester tester) async { testWidgets('TextField with specified helperStyle', (WidgetTester tester) async {

View File

@ -0,0 +1,186 @@
// Copyright 2018 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';
const ShapeBorder defaultButtonShape = RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2.0)));
const EdgeInsets defaultButtonPadding = EdgeInsets.only(left: 16.0, right: 16.0);
const BoxConstraints defaultButtonConstraints = BoxConstraints(minWidth: 88.0, minHeight: 36.0);
const Duration defaultButtonDuration = Duration(milliseconds: 200);
void main() {
group('RaisedButton', () {
testWidgets('theme: ThemeData.light(), enabled: true', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeData.light(),
home: Center(
child: RaisedButton(
onPressed: () { }, // button.enabled == true
child: const Text('button'),
)
),
),
);
final RawMaterialButton raw = tester.widget<RawMaterialButton>(find.byType(RawMaterialButton));
expect(raw.textStyle.color, const Color(0xdd000000));
expect(raw.fillColor, const Color(0xffe0e0e0));
expect(raw.highlightColor, const Color(0x29000000)); // Was Color(0x66bcbcbc)
expect(raw.splashColor, const Color(0x1f000000)); // Was Color(0x66c8c8c8)
expect(raw.elevation, 2.0);
expect(raw.highlightElevation, 8.0);
expect(raw.disabledElevation, 0.0);
expect(raw.constraints, defaultButtonConstraints);
expect(raw.padding, defaultButtonPadding);
expect(raw.shape, defaultButtonShape);
expect(raw.animationDuration, defaultButtonDuration);
expect(raw.materialTapTargetSize, MaterialTapTargetSize.padded);
});
testWidgets('theme: ThemeData.light(), enabled: false', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeData.light(),
home: const Center(
child: RaisedButton(
onPressed: null, // button.enabled == false
child: Text('button'),
)
),
),
);
final RawMaterialButton raw = tester.widget<RawMaterialButton>(find.byType(RawMaterialButton));
expect(raw.textStyle.color, const Color(0x61000000));
expect(raw.fillColor, const Color(0x61000000));
// highlightColor, disabled button can't be pressed
// splashColor, disabled button doesn't splash
expect(raw.elevation, 2.0);
expect(raw.highlightElevation, 8.0);
expect(raw.disabledElevation, 0.0);
expect(raw.constraints, defaultButtonConstraints);
expect(raw.padding, defaultButtonPadding);
expect(raw.shape, defaultButtonShape);
expect(raw.animationDuration, defaultButtonDuration);
expect(raw.materialTapTargetSize, MaterialTapTargetSize.padded);
});
});
group('FlatButton', () {
testWidgets('theme: ThemeData.light(), enabled: true', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeData.light(),
home: Center(
child: FlatButton(
onPressed: () { }, // button.enabled == true
child: const Text('button'),
)
),
),
);
final RawMaterialButton raw = tester.widget<RawMaterialButton>(find.byType(RawMaterialButton));
expect(raw.textStyle.color, const Color(0xdd000000));
expect(raw.fillColor, null);
expect(raw.highlightColor, const Color(0x29000000)); // Was Color(0x66bcbcbc)
expect(raw.splashColor, const Color(0x1f000000)); // Was Color(0x66c8c8c8)
expect(raw.elevation, 0.0);
expect(raw.highlightElevation, 0.0);
expect(raw.disabledElevation, 0.0);
expect(raw.constraints, defaultButtonConstraints);
expect(raw.padding, defaultButtonPadding);
expect(raw.shape, defaultButtonShape);
expect(raw.animationDuration, defaultButtonDuration);
expect(raw.materialTapTargetSize, MaterialTapTargetSize.padded);
});
testWidgets('theme: ThemeData.light(), enabled: false', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeData.light(),
home: const Center(
child: FlatButton(
onPressed: null, // button.enabled == false
child: Text('button'),
)
),
),
);
final RawMaterialButton raw = tester.widget<RawMaterialButton>(find.byType(RawMaterialButton));
expect(raw.textStyle.color, const Color(0x61000000));
expect(raw.fillColor, null);
// highlightColor, disabled button can't be pressed
// splashColor, disabled button doesn't splash
expect(raw.elevation, 0.0);
expect(raw.highlightElevation, 0.0);
expect(raw.disabledElevation, 0.0);
expect(raw.constraints, defaultButtonConstraints);
expect(raw.padding, defaultButtonPadding);
expect(raw.shape, defaultButtonShape);
expect(raw.animationDuration, const Duration(milliseconds: 200));
expect(raw.materialTapTargetSize, MaterialTapTargetSize.padded);
});
});
group('OutlineButton', () {
testWidgets('theme: ThemeData.light(), enabled: true', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeData.light(),
home: Center(
child: OutlineButton(
onPressed: () { }, // button.enabled == true
child: const Text('button'),
)
),
),
);
final RawMaterialButton raw = tester.widget<RawMaterialButton>(find.byType(RawMaterialButton));
expect(raw.textStyle.color, const Color(0xdd000000));
expect(raw.fillColor, const Color(0x00ffffff));
expect(raw.highlightColor, const Color(0x29000000)); // Was Color(0x66bcbcbc)
expect(raw.splashColor, const Color(0x1f000000)); // Was Color(0x66c8c8c8)
expect(raw.elevation, 0.0);
expect(raw.highlightElevation, 0.0);
expect(raw.disabledElevation, 0.0);
expect(raw.constraints, defaultButtonConstraints);
expect(raw.padding, defaultButtonPadding);
// animationDuration can't be configed by the theme/constructor
expect(raw.materialTapTargetSize, MaterialTapTargetSize.padded);
});
testWidgets('theme: ThemeData.light(), enabled: false', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeData.light(),
home: const Center(
child: OutlineButton(
onPressed: null, // button.enabled == false
child: Text('button'),
)
),
),
);
final RawMaterialButton raw = tester.widget<RawMaterialButton>(find.byType(RawMaterialButton));
expect(raw.textStyle.color, const Color(0x61000000));
expect(raw.fillColor, const Color(0x00000000));
// highlightColor, disabled button can't be pressed
// splashColor, disabled button doesn't splash
expect(raw.elevation, 0.0);
expect(raw.highlightElevation, 0.0);
expect(raw.disabledElevation, 0.0);
expect(raw.constraints, defaultButtonConstraints);
expect(raw.padding, defaultButtonPadding);
// animationDuration can't be configed by the theme/constructor
expect(raw.materialTapTargetSize, MaterialTapTargetSize.padded);
});
});
}

View File

@ -9,6 +9,8 @@ import 'package:flutter/src/foundation/diagnostics.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
const TextTheme defaultGeometryTheme = Typography.englishLike2014;
test('ThemeDataTween control test', () { test('ThemeDataTween control test', () {
final ThemeData light = ThemeData.light(); final ThemeData light = ThemeData.light();
final ThemeData dark = ThemeData.dark(); final ThemeData dark = ThemeData.dark();
@ -55,7 +57,7 @@ void main() {
) )
); );
expect(Theme.of(capturedContext), equals(ThemeData.localize(ThemeData.fallback(), MaterialTextGeometry.englishLike))); expect(Theme.of(capturedContext), equals(ThemeData.localize(ThemeData.fallback(), defaultGeometryTheme)));
expect(Theme.of(capturedContext, shadowThemeOnly: true), isNull); expect(Theme.of(capturedContext, shadowThemeOnly: true), isNull);
}); });
@ -65,20 +67,20 @@ void main() {
// Same input, same output. // Same input, same output.
expect( expect(
ThemeData.localize(light, MaterialTextGeometry.englishLike), ThemeData.localize(light, defaultGeometryTheme),
same(ThemeData.localize(light, MaterialTextGeometry.englishLike)), same(ThemeData.localize(light, defaultGeometryTheme)),
); );
// Different text geometry, different output. // Different text geometry, different output.
expect( expect(
ThemeData.localize(light, MaterialTextGeometry.englishLike), ThemeData.localize(light, defaultGeometryTheme),
isNot(same(ThemeData.localize(light, MaterialTextGeometry.tall))), isNot(same(ThemeData.localize(light, Typography.tall2014))),
); );
// Different base theme, different output. // Different base theme, different output.
expect( expect(
ThemeData.localize(light, MaterialTextGeometry.englishLike), ThemeData.localize(light, defaultGeometryTheme),
isNot(same(ThemeData.localize(dark, MaterialTextGeometry.englishLike))), isNot(same(ThemeData.localize(dark, defaultGeometryTheme))),
); );
}); });
@ -408,7 +410,7 @@ void main() {
} }
} }
expect(theme.textTheme.display4.debugLabel, '(englishLike display4).merge(blackMountainView display4)'); expect(theme.textTheme.display4.debugLabel, '(englishLike display4 2014).merge(blackMountainView display4)');
}); });
} }

View File

@ -74,6 +74,8 @@ void main() {
expect(textTheme.body1, isTextFont); expect(textTheme.body1, isTextFont);
expect(textTheme.caption, isTextFont); expect(textTheme.caption, isTextFont);
expect(textTheme.button, isTextFont); expect(textTheme.button, isTextFont);
expect(textTheme.subtitle, isTextFont);
expect(textTheme.overline, isTextFont);
} }
}); });
} }

View File

@ -138,11 +138,11 @@ It is converted to an enum value because the `material_en.arb` file
has this value labeled as `"x-flutter-type": "icuShortTimePattern"`. has this value labeled as `"x-flutter-type": "icuShortTimePattern"`.
The value of `scriptCategory` is based on the The value of `scriptCategory` is based on the
[Language categories reference](https://material.io/go/design-typography#typography-language-categories-reference) [Language categories reference](https://material.io/design/typography/language-support.html#language-categories-reference)
section in the Material spec. The `scriptCategory` value is used when looking up section in the Material spec. The `scriptCategory` value is used when looking up
the `TextTheme`, see the the `TextTheme`, see the
[MaterialTextGeometry](https://docs.flutter.io/flutter/material/MaterialTextGeometry/forScriptCategory.html) [MaterialTextGeometry](https://docs.flutter.io/flutter/material/MaterialTextGeometry/localizedFor.html)
class. method.
### Generated file localizations.dart: all of the localizations as a Map ### Generated file localizations.dart: all of the localizations as a Map

View File

@ -171,7 +171,7 @@ class MaterialLocalizationAr extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'عدد الصفوف في الصفحة:'; String get rowsPerPageTitle => r'عدد الصفوف في الصفحة:';
@override @override
String get scriptCategory => r'tall'; ScriptCategory get scriptCategory => ScriptCategory.tall;
@override @override
String get searchFieldLabel => r'بحث'; String get searchFieldLabel => r'بحث';
@ -372,7 +372,7 @@ class MaterialLocalizationBg extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Редове на страница:'; String get rowsPerPageTitle => r'Редове на страница:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Търсене'; String get searchFieldLabel => r'Търсене';
@ -573,7 +573,7 @@ class MaterialLocalizationBs extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Redaka po stranici:'; String get rowsPerPageTitle => r'Redaka po stranici:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Pretražite'; String get searchFieldLabel => r'Pretražite';
@ -774,7 +774,7 @@ class MaterialLocalizationCa extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Files per pàgina:'; String get rowsPerPageTitle => r'Files per pàgina:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Cerca'; String get searchFieldLabel => r'Cerca';
@ -975,7 +975,7 @@ class MaterialLocalizationCs extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Počet řádků na stránku:'; String get rowsPerPageTitle => r'Počet řádků na stránku:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Hledat'; String get searchFieldLabel => r'Hledat';
@ -1176,7 +1176,7 @@ class MaterialLocalizationDa extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Rækker pr. side:'; String get rowsPerPageTitle => r'Rækker pr. side:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Søg'; String get searchFieldLabel => r'Søg';
@ -1377,7 +1377,7 @@ class MaterialLocalizationDe extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Zeilen pro Seite:'; String get rowsPerPageTitle => r'Zeilen pro Seite:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Suchen'; String get searchFieldLabel => r'Suchen';
@ -1608,7 +1608,7 @@ class MaterialLocalizationEl extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Σειρές ανά σελίδα:'; String get rowsPerPageTitle => r'Σειρές ανά σελίδα:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Αναζήτηση'; String get searchFieldLabel => r'Αναζήτηση';
@ -1809,7 +1809,7 @@ class MaterialLocalizationEn extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Rows per page:'; String get rowsPerPageTitle => r'Rows per page:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Search'; String get searchFieldLabel => r'Search';
@ -2313,7 +2313,7 @@ class MaterialLocalizationEs extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Filas por página:'; String get rowsPerPageTitle => r'Filas por página:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Buscar'; String get searchFieldLabel => r'Buscar';
@ -4137,7 +4137,7 @@ class MaterialLocalizationEt extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Ridu lehe kohta:'; String get rowsPerPageTitle => r'Ridu lehe kohta:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Otsing'; String get searchFieldLabel => r'Otsing';
@ -4338,7 +4338,7 @@ class MaterialLocalizationFa extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'ردیف در هر صفحه:'; String get rowsPerPageTitle => r'ردیف در هر صفحه:';
@override @override
String get scriptCategory => r'tall'; ScriptCategory get scriptCategory => ScriptCategory.tall;
@override @override
String get searchFieldLabel => r'جستجو'; String get searchFieldLabel => r'جستجو';
@ -4539,7 +4539,7 @@ class MaterialLocalizationFi extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Riviä/sivu:'; String get rowsPerPageTitle => r'Riviä/sivu:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Haku'; String get searchFieldLabel => r'Haku';
@ -4740,7 +4740,7 @@ class MaterialLocalizationFil extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Mga row bawat page:'; String get rowsPerPageTitle => r'Mga row bawat page:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Maghanap'; String get searchFieldLabel => r'Maghanap';
@ -4941,7 +4941,7 @@ class MaterialLocalizationFr extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Lignes par page :'; String get rowsPerPageTitle => r'Lignes par page :';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Rechercher'; String get searchFieldLabel => r'Rechercher';
@ -5169,7 +5169,7 @@ class MaterialLocalizationGsw extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Zeilen pro Seite:'; String get rowsPerPageTitle => r'Zeilen pro Seite:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Suchen'; String get searchFieldLabel => r'Suchen';
@ -5370,7 +5370,7 @@ class MaterialLocalizationHe extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'שורות בכל דף:'; String get rowsPerPageTitle => r'שורות בכל דף:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'חיפוש'; String get searchFieldLabel => r'חיפוש';
@ -5571,7 +5571,7 @@ class MaterialLocalizationHi extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'हर पेज में पंक्तियों की संख्या:'; String get rowsPerPageTitle => r'हर पेज में पंक्तियों की संख्या:';
@override @override
String get scriptCategory => r'dense'; ScriptCategory get scriptCategory => ScriptCategory.dense;
@override @override
String get searchFieldLabel => r'खोजें'; String get searchFieldLabel => r'खोजें';
@ -5772,7 +5772,7 @@ class MaterialLocalizationHr extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Redaka po stranici:'; String get rowsPerPageTitle => r'Redaka po stranici:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Pretražite'; String get searchFieldLabel => r'Pretražite';
@ -5973,7 +5973,7 @@ class MaterialLocalizationHu extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Oldalankénti sorszám:'; String get rowsPerPageTitle => r'Oldalankénti sorszám:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Keresés'; String get searchFieldLabel => r'Keresés';
@ -6174,7 +6174,7 @@ class MaterialLocalizationId extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Baris per halaman:'; String get rowsPerPageTitle => r'Baris per halaman:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Telusuri'; String get searchFieldLabel => r'Telusuri';
@ -6375,7 +6375,7 @@ class MaterialLocalizationIt extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Righe per pagina:'; String get rowsPerPageTitle => r'Righe per pagina:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Cerca'; String get searchFieldLabel => r'Cerca';
@ -6576,7 +6576,7 @@ class MaterialLocalizationJa extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'ページあたりの行数:'; String get rowsPerPageTitle => r'ページあたりの行数:';
@override @override
String get scriptCategory => r'dense'; ScriptCategory get scriptCategory => ScriptCategory.dense;
@override @override
String get searchFieldLabel => r'検索'; String get searchFieldLabel => r'検索';
@ -6777,7 +6777,7 @@ class MaterialLocalizationKm extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'ជួរមុខ:'; String get rowsPerPageTitle => r'ជួរមុខ:';
@override @override
String get scriptCategory => r'dense'; ScriptCategory get scriptCategory => ScriptCategory.dense;
@override @override
String get searchFieldLabel => r'ស្វែងរក'; String get searchFieldLabel => r'ស្វែងរក';
@ -6978,7 +6978,7 @@ class MaterialLocalizationKo extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'페이지당 행 수:'; String get rowsPerPageTitle => r'페이지당 행 수:';
@override @override
String get scriptCategory => r'dense'; ScriptCategory get scriptCategory => ScriptCategory.dense;
@override @override
String get searchFieldLabel => r'검색'; String get searchFieldLabel => r'검색';
@ -7179,7 +7179,7 @@ class MaterialLocalizationLt extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Eilučių puslapyje:'; String get rowsPerPageTitle => r'Eilučių puslapyje:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Paieška'; String get searchFieldLabel => r'Paieška';
@ -7380,7 +7380,7 @@ class MaterialLocalizationLv extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Rindas lapā:'; String get rowsPerPageTitle => r'Rindas lapā:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Meklēt'; String get searchFieldLabel => r'Meklēt';
@ -7581,7 +7581,7 @@ class MaterialLocalizationMs extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Baris setiap halaman:'; String get rowsPerPageTitle => r'Baris setiap halaman:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Cari'; String get searchFieldLabel => r'Cari';
@ -7782,7 +7782,7 @@ class MaterialLocalizationNb extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Rader per side:'; String get rowsPerPageTitle => r'Rader per side:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Søk'; String get searchFieldLabel => r'Søk';
@ -7983,7 +7983,7 @@ class MaterialLocalizationNl extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Rijen per pagina:'; String get rowsPerPageTitle => r'Rijen per pagina:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Zoeken'; String get searchFieldLabel => r'Zoeken';
@ -8184,7 +8184,7 @@ class MaterialLocalizationPl extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Wiersze na stronie:'; String get rowsPerPageTitle => r'Wiersze na stronie:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Szukaj'; String get searchFieldLabel => r'Szukaj';
@ -8385,7 +8385,7 @@ class MaterialLocalizationPs extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'د هرې پاڼې پاڼې:'; String get rowsPerPageTitle => r'د هرې پاڼې پاڼې:';
@override @override
String get scriptCategory => r'tall'; ScriptCategory get scriptCategory => ScriptCategory.tall;
@override @override
String get searchFieldLabel => r'لټون'; String get searchFieldLabel => r'لټون';
@ -8586,7 +8586,7 @@ class MaterialLocalizationPt extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Linhas por página:'; String get rowsPerPageTitle => r'Linhas por página:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Pesquisa'; String get searchFieldLabel => r'Pesquisa';
@ -8871,7 +8871,7 @@ class MaterialLocalizationRo extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Rânduri pe pagină:'; String get rowsPerPageTitle => r'Rânduri pe pagină:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Căutați'; String get searchFieldLabel => r'Căutați';
@ -9072,7 +9072,7 @@ class MaterialLocalizationRu extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Строк на странице:'; String get rowsPerPageTitle => r'Строк на странице:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Поиск'; String get searchFieldLabel => r'Поиск';
@ -9273,7 +9273,7 @@ class MaterialLocalizationSk extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Počet riadkov na stránku:'; String get rowsPerPageTitle => r'Počet riadkov na stránku:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Hľadať'; String get searchFieldLabel => r'Hľadať';
@ -9474,7 +9474,7 @@ class MaterialLocalizationSl extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Vrstice na stran:'; String get rowsPerPageTitle => r'Vrstice na stran:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Iskanje'; String get searchFieldLabel => r'Iskanje';
@ -9675,7 +9675,7 @@ class MaterialLocalizationSr extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Редова по страници:'; String get rowsPerPageTitle => r'Редова по страници:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Претражите'; String get searchFieldLabel => r'Претражите';
@ -10053,7 +10053,7 @@ class MaterialLocalizationSv extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Rader per sida:'; String get rowsPerPageTitle => r'Rader per sida:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Sök'; String get searchFieldLabel => r'Sök';
@ -10254,7 +10254,7 @@ class MaterialLocalizationTh extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'แถวต่อหน้า:'; String get rowsPerPageTitle => r'แถวต่อหน้า:';
@override @override
String get scriptCategory => r'tall'; ScriptCategory get scriptCategory => ScriptCategory.tall;
@override @override
String get searchFieldLabel => r'ค้นหา'; String get searchFieldLabel => r'ค้นหา';
@ -10455,7 +10455,7 @@ class MaterialLocalizationTl extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Mga row bawat page:'; String get rowsPerPageTitle => r'Mga row bawat page:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Maghanap'; String get searchFieldLabel => r'Maghanap';
@ -10656,7 +10656,7 @@ class MaterialLocalizationTr extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Sayfa başına satır sayısı:'; String get rowsPerPageTitle => r'Sayfa başına satır sayısı:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Ara'; String get searchFieldLabel => r'Ara';
@ -10857,7 +10857,7 @@ class MaterialLocalizationUk extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Рядків на сторінці:'; String get rowsPerPageTitle => r'Рядків на сторінці:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Пошук'; String get searchFieldLabel => r'Пошук';
@ -11058,7 +11058,7 @@ class MaterialLocalizationUr extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'قطاریں فی صفحہ:'; String get rowsPerPageTitle => r'قطاریں فی صفحہ:';
@override @override
String get scriptCategory => r'tall'; ScriptCategory get scriptCategory => ScriptCategory.tall;
@override @override
String get searchFieldLabel => r'تلاش'; String get searchFieldLabel => r'تلاش';
@ -11259,7 +11259,7 @@ class MaterialLocalizationVi extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'Số hàng mỗi trang:'; String get rowsPerPageTitle => r'Số hàng mỗi trang:';
@override @override
String get scriptCategory => r'English-like'; ScriptCategory get scriptCategory => ScriptCategory.englishLike;
@override @override
String get searchFieldLabel => r'Tìm kiếm'; String get searchFieldLabel => r'Tìm kiếm';
@ -11460,7 +11460,7 @@ class MaterialLocalizationZh extends GlobalMaterialLocalizations {
String get rowsPerPageTitle => r'每页行数:'; String get rowsPerPageTitle => r'每页行数:';
@override @override
String get scriptCategory => r'dense'; ScriptCategory get scriptCategory => ScriptCategory.dense;
@override @override
String get searchFieldLabel => r'搜索'; String get searchFieldLabel => r'搜索';

View File

@ -1,7 +1,8 @@
{ {
"scriptCategory": "English-like", "scriptCategory": "English-like",
"@scriptCategory": { "@scriptCategory": {
"description": "The name of the language's script category (see https://material.io/go/design-typography#typography-language-categories-reference)." "description": "The name of the language's script category (see https://material.io/design/typography/language-support.html#language-categories-reference).",
"x-flutter-type": "scriptCategory"
}, },
"timeOfDayFormat": "h:mm a", "timeOfDayFormat": "h:mm a",

View File

@ -502,18 +502,8 @@ abstract class GlobalMaterialLocalizations implements MaterialLocalizations {
).replaceFirst(r'$remainingCount', formatDecimal(remainingCount)); ).replaceFirst(r'$remainingCount', formatDecimal(remainingCount));
} }
/// The script category used by [localTextGeometry]. Must be one of the strings
/// declared in [MaterialTextGeometry].
//
// TODO(ianh): make this return a TextTheme from MaterialTextGeometry.
// TODO(ianh): drop the constructor on MaterialTextGeometry.
// TODO(ianh): drop the strings on MaterialTextGeometry.
@protected
String get scriptCategory;
/// Looks up text geometry defined in [MaterialTextGeometry].
@override @override
TextTheme get localTextGeometry => MaterialTextGeometry.forScriptCategory(scriptCategory); ScriptCategory get scriptCategory;
/// A [LocalizationsDelegate] that uses [GlobalMaterialLocalizations.load] /// A [LocalizationsDelegate] that uses [GlobalMaterialLocalizations.load]
/// to create an instance of this class. /// to create an instance of this class.