mirror of
https://github.com/flutter/flutter
synced 2024-10-13 19:52:53 +00:00
Add Density API to ThemeData, implement for buttons. (#43547)
* Add a density attribute to ThemeData * Simplify tests * Review changes (Hans)
This commit is contained in:
parent
1ac17c14fc
commit
185da9b0af
|
@ -52,6 +52,7 @@ class RawMaterialButton extends StatefulWidget {
|
||||||
this.highlightElevation = 8.0,
|
this.highlightElevation = 8.0,
|
||||||
this.disabledElevation = 0.0,
|
this.disabledElevation = 0.0,
|
||||||
this.padding = EdgeInsets.zero,
|
this.padding = EdgeInsets.zero,
|
||||||
|
this.visualDensity = const VisualDensity(),
|
||||||
this.constraints = const BoxConstraints(minWidth: 88.0, minHeight: 36.0),
|
this.constraints = const BoxConstraints(minWidth: 88.0, minHeight: 36.0),
|
||||||
this.shape = const RoundedRectangleBorder(),
|
this.shape = const RoundedRectangleBorder(),
|
||||||
this.animationDuration = kThemeChangeDuration,
|
this.animationDuration = kThemeChangeDuration,
|
||||||
|
@ -207,6 +208,16 @@ class RawMaterialButton extends StatefulWidget {
|
||||||
/// The internal padding for the button's [child].
|
/// The internal padding for the button's [child].
|
||||||
final EdgeInsetsGeometry padding;
|
final EdgeInsetsGeometry padding;
|
||||||
|
|
||||||
|
/// Defines how compact the button's layout will be.
|
||||||
|
///
|
||||||
|
/// {@macro flutter.material.themedata.visualDensity}
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ThemeData.visualDensity], which specifies the [visualDensity] for all widgets
|
||||||
|
/// within a [Theme].
|
||||||
|
final VisualDensity visualDensity;
|
||||||
|
|
||||||
/// Defines the button's size.
|
/// Defines the button's size.
|
||||||
///
|
///
|
||||||
/// Typically used to constrain the button's minimum size.
|
/// Typically used to constrain the button's minimum size.
|
||||||
|
@ -354,9 +365,22 @@ class _RawMaterialButtonState extends State<RawMaterialButton> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final Color effectiveTextColor = MaterialStateProperty.resolveAs<Color>(widget.textStyle?.color, _states);
|
final Color effectiveTextColor = MaterialStateProperty.resolveAs<Color>(widget.textStyle?.color, _states);
|
||||||
final ShapeBorder effectiveShape = MaterialStateProperty.resolveAs<ShapeBorder>(widget.shape, _states);
|
final ShapeBorder effectiveShape = MaterialStateProperty.resolveAs<ShapeBorder>(widget.shape, _states);
|
||||||
|
final Offset densityAdjustment = widget.visualDensity.baseSizeAdjustment;
|
||||||
|
final BoxConstraints effectiveConstraints = widget.constraints.copyWith(
|
||||||
|
minWidth: widget.constraints.minWidth != null ? (widget.constraints.minWidth + densityAdjustment.dx).clamp(0.0, double.infinity) : null,
|
||||||
|
minHeight: widget.constraints.minWidth != null ? (widget.constraints.minHeight + densityAdjustment.dy).clamp(0.0, double.infinity) : null,
|
||||||
|
);
|
||||||
|
final EdgeInsetsGeometry padding = widget.padding.add(
|
||||||
|
EdgeInsets.only(
|
||||||
|
left: densityAdjustment.dx,
|
||||||
|
top: densityAdjustment.dy,
|
||||||
|
right: densityAdjustment.dx,
|
||||||
|
bottom: densityAdjustment.dy,
|
||||||
|
),
|
||||||
|
).clamp(EdgeInsets.zero, EdgeInsetsGeometry.infinity);
|
||||||
|
|
||||||
final Widget result = ConstrainedBox(
|
final Widget result = ConstrainedBox(
|
||||||
constraints: widget.constraints,
|
constraints: effectiveConstraints,
|
||||||
child: Material(
|
child: Material(
|
||||||
elevation: _effectiveElevation,
|
elevation: _effectiveElevation,
|
||||||
textStyle: widget.textStyle?.copyWith(color: effectiveTextColor),
|
textStyle: widget.textStyle?.copyWith(color: effectiveTextColor),
|
||||||
|
@ -383,7 +407,7 @@ class _RawMaterialButtonState extends State<RawMaterialButton> {
|
||||||
child: IconTheme.merge(
|
child: IconTheme.merge(
|
||||||
data: IconThemeData(color: effectiveTextColor),
|
data: IconThemeData(color: effectiveTextColor),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: widget.padding,
|
padding: padding,
|
||||||
child: Center(
|
child: Center(
|
||||||
widthFactor: 1.0,
|
widthFactor: 1.0,
|
||||||
heightFactor: 1.0,
|
heightFactor: 1.0,
|
||||||
|
@ -397,7 +421,12 @@ class _RawMaterialButtonState extends State<RawMaterialButton> {
|
||||||
Size minSize;
|
Size minSize;
|
||||||
switch (widget.materialTapTargetSize) {
|
switch (widget.materialTapTargetSize) {
|
||||||
case MaterialTapTargetSize.padded:
|
case MaterialTapTargetSize.padded:
|
||||||
minSize = const Size(48.0, 48.0);
|
minSize = Size(
|
||||||
|
kMinInteractiveDimension + densityAdjustment.dx,
|
||||||
|
kMinInteractiveDimension + densityAdjustment.dy,
|
||||||
|
);
|
||||||
|
assert(minSize.width >= 0.0);
|
||||||
|
assert(minSize.height >= 0.0);
|
||||||
break;
|
break;
|
||||||
case MaterialTapTargetSize.shrinkWrap:
|
case MaterialTapTargetSize.shrinkWrap:
|
||||||
minSize = Size.zero;
|
minSize = Size.zero;
|
||||||
|
|
|
@ -115,6 +115,7 @@ class FlatButton extends MaterialButton {
|
||||||
Color splashColor,
|
Color splashColor,
|
||||||
Brightness colorBrightness,
|
Brightness colorBrightness,
|
||||||
EdgeInsetsGeometry padding,
|
EdgeInsetsGeometry padding,
|
||||||
|
VisualDensity visualDensity,
|
||||||
ShapeBorder shape,
|
ShapeBorder shape,
|
||||||
Clip clipBehavior = Clip.none,
|
Clip clipBehavior = Clip.none,
|
||||||
FocusNode focusNode,
|
FocusNode focusNode,
|
||||||
|
@ -139,6 +140,7 @@ class FlatButton extends MaterialButton {
|
||||||
splashColor: splashColor,
|
splashColor: splashColor,
|
||||||
colorBrightness: colorBrightness,
|
colorBrightness: colorBrightness,
|
||||||
padding: padding,
|
padding: padding,
|
||||||
|
visualDensity: visualDensity,
|
||||||
shape: shape,
|
shape: shape,
|
||||||
clipBehavior: clipBehavior,
|
clipBehavior: clipBehavior,
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
|
@ -199,6 +201,7 @@ class FlatButton extends MaterialButton {
|
||||||
highlightElevation: buttonTheme.getHighlightElevation(this),
|
highlightElevation: buttonTheme.getHighlightElevation(this),
|
||||||
disabledElevation: buttonTheme.getDisabledElevation(this),
|
disabledElevation: buttonTheme.getDisabledElevation(this),
|
||||||
padding: buttonTheme.getPadding(this),
|
padding: buttonTheme.getPadding(this),
|
||||||
|
visualDensity: visualDensity ?? theme.visualDensity,
|
||||||
constraints: buttonTheme.getConstraints(this),
|
constraints: buttonTheme.getConstraints(this),
|
||||||
shape: buttonTheme.getShape(this),
|
shape: buttonTheme.getShape(this),
|
||||||
clipBehavior: clipBehavior,
|
clipBehavior: clipBehavior,
|
||||||
|
|
|
@ -69,6 +69,7 @@ class MaterialButton extends StatelessWidget {
|
||||||
this.highlightElevation,
|
this.highlightElevation,
|
||||||
this.disabledElevation,
|
this.disabledElevation,
|
||||||
this.padding,
|
this.padding,
|
||||||
|
this.visualDensity,
|
||||||
this.shape,
|
this.shape,
|
||||||
this.clipBehavior = Clip.none,
|
this.clipBehavior = Clip.none,
|
||||||
this.focusNode,
|
this.focusNode,
|
||||||
|
@ -311,6 +312,16 @@ class MaterialButton extends StatelessWidget {
|
||||||
/// [ButtonThemeData.padding].
|
/// [ButtonThemeData.padding].
|
||||||
final EdgeInsetsGeometry padding;
|
final EdgeInsetsGeometry padding;
|
||||||
|
|
||||||
|
/// Defines how compact the button's layout will be.
|
||||||
|
///
|
||||||
|
/// {@macro flutter.material.themedata.visualDensity}
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ThemeData.visualDensity], which specifies the [density] for all widgets
|
||||||
|
/// within a [Theme].
|
||||||
|
final VisualDensity visualDensity;
|
||||||
|
|
||||||
/// The shape of the button's [Material].
|
/// The shape of the button's [Material].
|
||||||
///
|
///
|
||||||
/// The button's highlight and splash are clipped to this shape. If the
|
/// The button's highlight and splash are clipped to this shape. If the
|
||||||
|
@ -387,6 +398,7 @@ class MaterialButton extends StatelessWidget {
|
||||||
hoverElevation: buttonTheme.getHoverElevation(this),
|
hoverElevation: buttonTheme.getHoverElevation(this),
|
||||||
highlightElevation: buttonTheme.getHighlightElevation(this),
|
highlightElevation: buttonTheme.getHighlightElevation(this),
|
||||||
padding: buttonTheme.getPadding(this),
|
padding: buttonTheme.getPadding(this),
|
||||||
|
visualDensity: visualDensity ?? theme.visualDensity,
|
||||||
constraints: buttonTheme.getConstraints(this).copyWith(
|
constraints: buttonTheme.getConstraints(this).copyWith(
|
||||||
minWidth: minWidth,
|
minWidth: minWidth,
|
||||||
minHeight: height,
|
minHeight: height,
|
||||||
|
@ -416,6 +428,7 @@ class MaterialButton extends StatelessWidget {
|
||||||
properties.add(ColorProperty('splashColor', splashColor, defaultValue: null));
|
properties.add(ColorProperty('splashColor', splashColor, defaultValue: null));
|
||||||
properties.add(DiagnosticsProperty<Brightness>('colorBrightness', colorBrightness, defaultValue: null));
|
properties.add(DiagnosticsProperty<Brightness>('colorBrightness', colorBrightness, defaultValue: null));
|
||||||
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
|
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
|
||||||
|
properties.add(DiagnosticsProperty<VisualDensity>('visualDensity', visualDensity, defaultValue: null));
|
||||||
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
|
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
|
||||||
properties.add(DiagnosticsProperty<FocusNode>('focusNode', focusNode, defaultValue: null));
|
properties.add(DiagnosticsProperty<FocusNode>('focusNode', focusNode, defaultValue: null));
|
||||||
properties.add(DiagnosticsProperty<MaterialTapTargetSize>('materialTapTargetSize', materialTapTargetSize, defaultValue: null));
|
properties.add(DiagnosticsProperty<MaterialTapTargetSize>('materialTapTargetSize', materialTapTargetSize, defaultValue: null));
|
||||||
|
|
|
@ -11,6 +11,7 @@ import 'material_button.dart';
|
||||||
import 'material_state.dart';
|
import 'material_state.dart';
|
||||||
import 'raised_button.dart';
|
import 'raised_button.dart';
|
||||||
import 'theme.dart';
|
import 'theme.dart';
|
||||||
|
import 'theme_data.dart';
|
||||||
|
|
||||||
// The total time to make the button's fill color opaque and change
|
// The total time to make the button's fill color opaque and change
|
||||||
// its elevation. Only applies when highlightElevation > 0.0.
|
// its elevation. Only applies when highlightElevation > 0.0.
|
||||||
|
@ -75,6 +76,7 @@ class OutlineButton extends MaterialButton {
|
||||||
this.disabledBorderColor,
|
this.disabledBorderColor,
|
||||||
this.highlightedBorderColor,
|
this.highlightedBorderColor,
|
||||||
EdgeInsetsGeometry padding,
|
EdgeInsetsGeometry padding,
|
||||||
|
VisualDensity visualDensity,
|
||||||
ShapeBorder shape,
|
ShapeBorder shape,
|
||||||
Clip clipBehavior = Clip.none,
|
Clip clipBehavior = Clip.none,
|
||||||
FocusNode focusNode,
|
FocusNode focusNode,
|
||||||
|
@ -97,6 +99,7 @@ class OutlineButton extends MaterialButton {
|
||||||
splashColor: splashColor,
|
splashColor: splashColor,
|
||||||
highlightElevation: highlightElevation,
|
highlightElevation: highlightElevation,
|
||||||
padding: padding,
|
padding: padding,
|
||||||
|
visualDensity: visualDensity,
|
||||||
shape: shape,
|
shape: shape,
|
||||||
clipBehavior: clipBehavior,
|
clipBehavior: clipBehavior,
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
|
@ -129,6 +132,7 @@ class OutlineButton extends MaterialButton {
|
||||||
Color disabledBorderColor,
|
Color disabledBorderColor,
|
||||||
BorderSide borderSide,
|
BorderSide borderSide,
|
||||||
EdgeInsetsGeometry padding,
|
EdgeInsetsGeometry padding,
|
||||||
|
VisualDensity visualDensity,
|
||||||
ShapeBorder shape,
|
ShapeBorder shape,
|
||||||
Clip clipBehavior,
|
Clip clipBehavior,
|
||||||
FocusNode focusNode,
|
FocusNode focusNode,
|
||||||
|
@ -187,6 +191,7 @@ class OutlineButton extends MaterialButton {
|
||||||
disabledBorderColor: disabledBorderColor,
|
disabledBorderColor: disabledBorderColor,
|
||||||
highlightedBorderColor: highlightedBorderColor ?? buttonTheme.colorScheme.primary,
|
highlightedBorderColor: highlightedBorderColor ?? buttonTheme.colorScheme.primary,
|
||||||
padding: buttonTheme.getPadding(this),
|
padding: buttonTheme.getPadding(this),
|
||||||
|
visualDensity: visualDensity,
|
||||||
shape: buttonTheme.getShape(this),
|
shape: buttonTheme.getShape(this),
|
||||||
clipBehavior: clipBehavior,
|
clipBehavior: clipBehavior,
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
|
@ -225,6 +230,7 @@ class _OutlineButtonWithIcon extends OutlineButton with MaterialButtonWithIconMi
|
||||||
Color disabledBorderColor,
|
Color disabledBorderColor,
|
||||||
BorderSide borderSide,
|
BorderSide borderSide,
|
||||||
EdgeInsetsGeometry padding,
|
EdgeInsetsGeometry padding,
|
||||||
|
VisualDensity visualDensity,
|
||||||
ShapeBorder shape,
|
ShapeBorder shape,
|
||||||
Clip clipBehavior = Clip.none,
|
Clip clipBehavior = Clip.none,
|
||||||
FocusNode focusNode,
|
FocusNode focusNode,
|
||||||
|
@ -253,6 +259,7 @@ class _OutlineButtonWithIcon extends OutlineButton with MaterialButtonWithIconMi
|
||||||
highlightedBorderColor: highlightedBorderColor,
|
highlightedBorderColor: highlightedBorderColor,
|
||||||
borderSide: borderSide,
|
borderSide: borderSide,
|
||||||
padding: padding,
|
padding: padding,
|
||||||
|
visualDensity: visualDensity,
|
||||||
shape: shape,
|
shape: shape,
|
||||||
clipBehavior: clipBehavior,
|
clipBehavior: clipBehavior,
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
|
@ -287,6 +294,7 @@ class _OutlineButton extends StatefulWidget {
|
||||||
this.disabledBorderColor,
|
this.disabledBorderColor,
|
||||||
@required this.highlightedBorderColor,
|
@required this.highlightedBorderColor,
|
||||||
this.padding,
|
this.padding,
|
||||||
|
this.visualDensity,
|
||||||
this.shape,
|
this.shape,
|
||||||
this.clipBehavior = Clip.none,
|
this.clipBehavior = Clip.none,
|
||||||
this.focusNode,
|
this.focusNode,
|
||||||
|
@ -314,6 +322,7 @@ class _OutlineButton extends StatefulWidget {
|
||||||
final Color disabledBorderColor;
|
final Color disabledBorderColor;
|
||||||
final Color highlightedBorderColor;
|
final Color highlightedBorderColor;
|
||||||
final EdgeInsetsGeometry padding;
|
final EdgeInsetsGeometry padding;
|
||||||
|
final VisualDensity visualDensity;
|
||||||
final ShapeBorder shape;
|
final ShapeBorder shape;
|
||||||
final Clip clipBehavior;
|
final Clip clipBehavior;
|
||||||
final FocusNode focusNode;
|
final FocusNode focusNode;
|
||||||
|
@ -435,6 +444,8 @@ class _OutlineButtonState extends State<_OutlineButton> with SingleTickerProvide
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final ThemeData theme = Theme.of(context);
|
||||||
|
|
||||||
return AnimatedBuilder(
|
return AnimatedBuilder(
|
||||||
animation: _controller,
|
animation: _controller,
|
||||||
builder: (BuildContext context, Widget child) {
|
builder: (BuildContext context, Widget child) {
|
||||||
|
@ -456,6 +467,7 @@ class _OutlineButtonState extends State<_OutlineButton> with SingleTickerProvide
|
||||||
highlightElevation: _getHighlightElevation(),
|
highlightElevation: _getHighlightElevation(),
|
||||||
onHighlightChanged: _handleHighlightChanged,
|
onHighlightChanged: _handleHighlightChanged,
|
||||||
padding: widget.padding,
|
padding: widget.padding,
|
||||||
|
visualDensity: widget.visualDensity ?? theme.visualDensity,
|
||||||
shape: _OutlineBorder(
|
shape: _OutlineBorder(
|
||||||
shape: widget.shape,
|
shape: widget.shape,
|
||||||
side: _getOutline(),
|
side: _getOutline(),
|
||||||
|
|
|
@ -126,6 +126,7 @@ class RaisedButton extends MaterialButton {
|
||||||
double highlightElevation,
|
double highlightElevation,
|
||||||
double disabledElevation,
|
double disabledElevation,
|
||||||
EdgeInsetsGeometry padding,
|
EdgeInsetsGeometry padding,
|
||||||
|
VisualDensity visualDensity,
|
||||||
ShapeBorder shape,
|
ShapeBorder shape,
|
||||||
Clip clipBehavior = Clip.none,
|
Clip clipBehavior = Clip.none,
|
||||||
FocusNode focusNode,
|
FocusNode focusNode,
|
||||||
|
@ -161,6 +162,7 @@ class RaisedButton extends MaterialButton {
|
||||||
highlightElevation: highlightElevation,
|
highlightElevation: highlightElevation,
|
||||||
disabledElevation: disabledElevation,
|
disabledElevation: disabledElevation,
|
||||||
padding: padding,
|
padding: padding,
|
||||||
|
visualDensity: visualDensity,
|
||||||
shape: shape,
|
shape: shape,
|
||||||
clipBehavior: clipBehavior,
|
clipBehavior: clipBehavior,
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
|
@ -227,6 +229,7 @@ class RaisedButton extends MaterialButton {
|
||||||
highlightElevation: buttonTheme.getHighlightElevation(this),
|
highlightElevation: buttonTheme.getHighlightElevation(this),
|
||||||
disabledElevation: buttonTheme.getDisabledElevation(this),
|
disabledElevation: buttonTheme.getDisabledElevation(this),
|
||||||
padding: buttonTheme.getPadding(this),
|
padding: buttonTheme.getPadding(this),
|
||||||
|
visualDensity: visualDensity ?? theme.visualDensity,
|
||||||
constraints: buttonTheme.getConstraints(this),
|
constraints: buttonTheme.getConstraints(this),
|
||||||
shape: buttonTheme.getShape(this),
|
shape: buttonTheme.getShape(this),
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// 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.
|
||||||
|
|
||||||
import 'dart:ui' show Color, hashList;
|
import 'dart:ui' show Color, hashList, lerpDouble;
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
@ -200,6 +200,7 @@ class ThemeData extends Diagnosticable {
|
||||||
/// more discussion on how to pick the right colors.
|
/// more discussion on how to pick the right colors.
|
||||||
factory ThemeData({
|
factory ThemeData({
|
||||||
Brightness brightness,
|
Brightness brightness,
|
||||||
|
VisualDensity visualDensity,
|
||||||
MaterialColor primarySwatch,
|
MaterialColor primarySwatch,
|
||||||
Color primaryColor,
|
Color primaryColor,
|
||||||
Brightness primaryColorBrightness,
|
Brightness primaryColorBrightness,
|
||||||
|
@ -266,6 +267,7 @@ class ThemeData extends Diagnosticable {
|
||||||
}) {
|
}) {
|
||||||
brightness ??= Brightness.light;
|
brightness ??= Brightness.light;
|
||||||
final bool isDark = brightness == Brightness.dark;
|
final bool isDark = brightness == Brightness.dark;
|
||||||
|
visualDensity ??= const VisualDensity();
|
||||||
primarySwatch ??= Colors.blue;
|
primarySwatch ??= Colors.blue;
|
||||||
primaryColor ??= isDark ? Colors.grey[900] : primarySwatch;
|
primaryColor ??= isDark ? Colors.grey[900] : primarySwatch;
|
||||||
primaryColorBrightness ??= estimateBrightnessForColor(primaryColor);
|
primaryColorBrightness ??= estimateBrightnessForColor(primaryColor);
|
||||||
|
@ -372,6 +374,7 @@ class ThemeData extends Diagnosticable {
|
||||||
|
|
||||||
return ThemeData.raw(
|
return ThemeData.raw(
|
||||||
brightness: brightness,
|
brightness: brightness,
|
||||||
|
visualDensity: visualDensity,
|
||||||
primaryColor: primaryColor,
|
primaryColor: primaryColor,
|
||||||
primaryColorBrightness: primaryColorBrightness,
|
primaryColorBrightness: primaryColorBrightness,
|
||||||
primaryColorLight: primaryColorLight,
|
primaryColorLight: primaryColorLight,
|
||||||
|
@ -448,6 +451,7 @@ class ThemeData extends Diagnosticable {
|
||||||
// operator == and in the hashValues method and in the order of fields
|
// operator == and in the hashValues method and in the order of fields
|
||||||
// in this class, and in the lerp() method.
|
// in this class, and in the lerp() method.
|
||||||
@required this.brightness,
|
@required this.brightness,
|
||||||
|
@required this.visualDensity,
|
||||||
@required this.primaryColor,
|
@required this.primaryColor,
|
||||||
@required this.primaryColorBrightness,
|
@required this.primaryColorBrightness,
|
||||||
@required this.primaryColorLight,
|
@required this.primaryColorLight,
|
||||||
|
@ -510,6 +514,7 @@ class ThemeData extends Diagnosticable {
|
||||||
@required this.dividerTheme,
|
@required this.dividerTheme,
|
||||||
@required this.buttonBarTheme,
|
@required this.buttonBarTheme,
|
||||||
}) : assert(brightness != null),
|
}) : assert(brightness != null),
|
||||||
|
assert(visualDensity != null),
|
||||||
assert(primaryColor != null),
|
assert(primaryColor != null),
|
||||||
assert(primaryColorBrightness != null),
|
assert(primaryColorBrightness != null),
|
||||||
assert(primaryColorLight != null),
|
assert(primaryColorLight != null),
|
||||||
|
@ -669,6 +674,35 @@ class ThemeData extends Diagnosticable {
|
||||||
/// dark, use Colors.white or the accentColor for a contrasting color.
|
/// dark, use Colors.white or the accentColor for a contrasting color.
|
||||||
final Brightness brightness;
|
final Brightness brightness;
|
||||||
|
|
||||||
|
/// The density value for specifying the compactness of various UI components.
|
||||||
|
///
|
||||||
|
/// {@template flutter.material.themedata.visualDensity}
|
||||||
|
/// Density, in the context of a UI, is the vertical and horizontal
|
||||||
|
/// "compactness" of the elements in the UI. It is unitless, since it means
|
||||||
|
/// different things to different UI elements. For buttons, it affects the
|
||||||
|
/// spacing around the centered label of the button. For lists, it affects the
|
||||||
|
/// distance between baselines of entries in the list.
|
||||||
|
///
|
||||||
|
/// Typically, density values are integral, but any value in range may be
|
||||||
|
/// used. The range includes values from [VisualDensity.minimumDensity] (which
|
||||||
|
/// is -4), to [VisualDensity.maximumDensity] (which is 4), inclusive, where
|
||||||
|
/// negative values indicate a denser, more compact, UI, and positive values
|
||||||
|
/// indicate a less dense, more expanded, UI. If a component doesn't support
|
||||||
|
/// the value given, it will clamp to the the nearest supported value.
|
||||||
|
///
|
||||||
|
/// The default for visual densities is zero for both vertical and horizontal
|
||||||
|
/// densities, which corresponds to the default visual density of components
|
||||||
|
/// in the Material Design specification.
|
||||||
|
///
|
||||||
|
/// As a rule of thumb, a change of 1 or -1 in density corresponds to 4
|
||||||
|
/// logical pixels. However, this is not a strict relationship since
|
||||||
|
/// components interpret the density values appropriately for their needs.
|
||||||
|
///
|
||||||
|
/// A larger value translates to a spacing increase (less dense), and a
|
||||||
|
/// smaller value translates to a spacing decrease (more dense).
|
||||||
|
/// {@endtemplate}
|
||||||
|
final VisualDensity visualDensity;
|
||||||
|
|
||||||
/// The background color for major parts of the app (toolbars, tab bars, etc)
|
/// The background color for major parts of the app (toolbars, tab bars, etc)
|
||||||
///
|
///
|
||||||
/// The theme's [colorScheme] property contains [ColorScheme.primary], as
|
/// The theme's [colorScheme] property contains [ColorScheme.primary], as
|
||||||
|
@ -984,6 +1018,7 @@ class ThemeData extends Diagnosticable {
|
||||||
/// 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,
|
||||||
|
VisualDensity visualDensity,
|
||||||
Color primaryColor,
|
Color primaryColor,
|
||||||
Brightness primaryColorBrightness,
|
Brightness primaryColorBrightness,
|
||||||
Color primaryColorLight,
|
Color primaryColorLight,
|
||||||
|
@ -1049,6 +1084,7 @@ class ThemeData extends Diagnosticable {
|
||||||
cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault();
|
cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault();
|
||||||
return ThemeData.raw(
|
return ThemeData.raw(
|
||||||
brightness: brightness ?? this.brightness,
|
brightness: brightness ?? this.brightness,
|
||||||
|
visualDensity: visualDensity ?? this.visualDensity,
|
||||||
primaryColor: primaryColor ?? this.primaryColor,
|
primaryColor: primaryColor ?? this.primaryColor,
|
||||||
primaryColorBrightness: primaryColorBrightness ?? this.primaryColorBrightness,
|
primaryColorBrightness: primaryColorBrightness ?? this.primaryColorBrightness,
|
||||||
primaryColorLight: primaryColorLight ?? this.primaryColorLight,
|
primaryColorLight: primaryColorLight ?? this.primaryColorLight,
|
||||||
|
@ -1192,6 +1228,7 @@ class ThemeData extends Diagnosticable {
|
||||||
// the class and in the lerp() method.
|
// 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,
|
||||||
|
visualDensity: VisualDensity.lerp(a.visualDensity, b.visualDensity, t),
|
||||||
primaryColor: Color.lerp(a.primaryColor, b.primaryColor, t),
|
primaryColor: Color.lerp(a.primaryColor, b.primaryColor, t),
|
||||||
primaryColorBrightness: t < 0.5 ? a.primaryColorBrightness : b.primaryColorBrightness,
|
primaryColorBrightness: t < 0.5 ? a.primaryColorBrightness : b.primaryColorBrightness,
|
||||||
primaryColorLight: Color.lerp(a.primaryColorLight, b.primaryColorLight, t),
|
primaryColorLight: Color.lerp(a.primaryColorLight, b.primaryColorLight, t),
|
||||||
|
@ -1265,6 +1302,7 @@ class ThemeData extends Diagnosticable {
|
||||||
// hashValues() and in the raw constructor and in the order of fields in
|
// hashValues() and in the raw constructor and in the order of fields in
|
||||||
// the class and in the lerp() method.
|
// the class and in the lerp() method.
|
||||||
return (otherData.brightness == brightness) &&
|
return (otherData.brightness == brightness) &&
|
||||||
|
(otherData.visualDensity == visualDensity) &&
|
||||||
(otherData.primaryColor == primaryColor) &&
|
(otherData.primaryColor == primaryColor) &&
|
||||||
(otherData.primaryColorBrightness == primaryColorBrightness) &&
|
(otherData.primaryColorBrightness == primaryColorBrightness) &&
|
||||||
(otherData.primaryColorLight == primaryColorLight) &&
|
(otherData.primaryColorLight == primaryColorLight) &&
|
||||||
|
@ -1333,6 +1371,7 @@ class ThemeData extends Diagnosticable {
|
||||||
// and in the order of fields in the class and in the lerp() method.
|
// and in the order of fields in the class and in the lerp() method.
|
||||||
final List<Object> values = <Object>[
|
final List<Object> values = <Object>[
|
||||||
brightness,
|
brightness,
|
||||||
|
visualDensity,
|
||||||
primaryColor,
|
primaryColor,
|
||||||
primaryColorBrightness,
|
primaryColorBrightness,
|
||||||
primaryColorLight,
|
primaryColorLight,
|
||||||
|
@ -1404,6 +1443,7 @@ class ThemeData extends Diagnosticable {
|
||||||
final ThemeData defaultData = ThemeData.fallback();
|
final ThemeData defaultData = ThemeData.fallback();
|
||||||
properties.add(EnumProperty<TargetPlatform>('platform', platform, defaultValue: defaultTargetPlatform));
|
properties.add(EnumProperty<TargetPlatform>('platform', platform, defaultValue: defaultTargetPlatform));
|
||||||
properties.add(EnumProperty<Brightness>('brightness', brightness, defaultValue: defaultData.brightness));
|
properties.add(EnumProperty<Brightness>('brightness', brightness, defaultValue: defaultData.brightness));
|
||||||
|
properties.add(DiagnosticsProperty<VisualDensity>('visualDensity', visualDensity, defaultValue: defaultData.visualDensity));
|
||||||
properties.add(ColorProperty('primaryColor', primaryColor, defaultValue: defaultData.primaryColor));
|
properties.add(ColorProperty('primaryColor', primaryColor, defaultValue: defaultData.primaryColor));
|
||||||
properties.add(EnumProperty<Brightness>('primaryColorBrightness', primaryColorBrightness, defaultValue: defaultData.primaryColorBrightness));
|
properties.add(EnumProperty<Brightness>('primaryColorBrightness', primaryColorBrightness, defaultValue: defaultData.primaryColorBrightness));
|
||||||
properties.add(ColorProperty('accentColor', accentColor, defaultValue: defaultData.accentColor));
|
properties.add(ColorProperty('accentColor', accentColor, defaultValue: defaultData.accentColor));
|
||||||
|
@ -1635,3 +1675,167 @@ class _FifoCache<K, V> {
|
||||||
return _cache[key] = loader();
|
return _cache[key] = loader();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Defines the visual density of user interface components.
|
||||||
|
///
|
||||||
|
/// Density, in the context of a UI, is the vertical and horizontal
|
||||||
|
/// "compactness" of the components in the UI. It is unitless, since it means
|
||||||
|
/// different things to different UI components.
|
||||||
|
///
|
||||||
|
/// The default for visual densities is zero for both vertical and horizontal
|
||||||
|
/// densities, which corresponds to the default visual density of components in
|
||||||
|
/// the Material Design specification. It does not affect text sizes, icon
|
||||||
|
/// sizes, or padding values.
|
||||||
|
///
|
||||||
|
/// For example, for buttons, it affects the spacing around the child of the
|
||||||
|
/// button. For lists, it affects the distance between baselines of entries in
|
||||||
|
/// the list. For chips, it only affects the vertical size, not the horizontal
|
||||||
|
/// size.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ThemeData.visualDensity], where this property is used to specify the base
|
||||||
|
/// horizontal density of Material components.
|
||||||
|
/// * [Material design guidance on density](https://material.io/design/layout/applying-density.html).
|
||||||
|
class VisualDensity extends Diagnosticable {
|
||||||
|
/// A const constructor for [VisualDensity].
|
||||||
|
///
|
||||||
|
/// All of the arguments must be non-null, and [horizontal] and [vertical]
|
||||||
|
/// must be in the interval between [minimumDensity] and [maximumDensity],
|
||||||
|
/// inclusive.
|
||||||
|
const VisualDensity({
|
||||||
|
this.horizontal = 0.0,
|
||||||
|
this.vertical = 0.0,
|
||||||
|
}) : assert(horizontal != null),
|
||||||
|
assert(vertical != null),
|
||||||
|
assert(vertical <= maximumDensity),
|
||||||
|
assert(vertical >= minimumDensity),
|
||||||
|
assert(horizontal <= maximumDensity),
|
||||||
|
assert(horizontal >= minimumDensity);
|
||||||
|
|
||||||
|
/// The minimum allowed density.
|
||||||
|
static const double minimumDensity = -4.0;
|
||||||
|
|
||||||
|
/// The maximum allowed density.
|
||||||
|
static const double maximumDensity = 4.0;
|
||||||
|
|
||||||
|
/// The default profile for [VisualDensity] in [ThemeData].
|
||||||
|
///
|
||||||
|
/// This default value represents a visual density that is less dense than
|
||||||
|
/// either [comfortable] or [compact], and corresponds to density values of
|
||||||
|
/// zero in both axes.
|
||||||
|
static const VisualDensity standard = VisualDensity();
|
||||||
|
|
||||||
|
/// The profile for a "comfortable" interpretation of [VisualDensity].
|
||||||
|
///
|
||||||
|
/// Individual components will interpret the density value independently,
|
||||||
|
/// making themselves more visually dense than [standard] and less dense than
|
||||||
|
/// [compact] to different degrees based on the Material Design specification
|
||||||
|
/// of the "comfortable" setting for their particular use case.
|
||||||
|
///
|
||||||
|
/// It corresponds to a density value of -1 in both axes.
|
||||||
|
static const VisualDensity comfortable = VisualDensity(horizontal: -1.0, vertical: -1.0);
|
||||||
|
|
||||||
|
/// The profile for a "compact" interpretation of [VisualDensity].
|
||||||
|
///
|
||||||
|
/// Individual components will interpret the density value independently,
|
||||||
|
/// making themselves more visually dense than [standard] and [comfortable] to
|
||||||
|
/// different degrees based on the Material Design specification of the
|
||||||
|
/// "comfortable" setting for their particular use case.
|
||||||
|
///
|
||||||
|
/// It corresponds to a density value of -2 in both axes.
|
||||||
|
static const VisualDensity compact = VisualDensity(horizontal: -2.0, vertical: -2.0);
|
||||||
|
|
||||||
|
/// Copy the current [VisualDensity] with the given values replacing the
|
||||||
|
/// current values.
|
||||||
|
VisualDensity copyWith({
|
||||||
|
double horizontal,
|
||||||
|
double vertical,
|
||||||
|
}) {
|
||||||
|
return VisualDensity(
|
||||||
|
horizontal: horizontal ?? this.horizontal,
|
||||||
|
vertical: vertical ?? this.vertical,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The horizontal visual density of UI components.
|
||||||
|
///
|
||||||
|
/// This property affects only the horizontal spacing between and within
|
||||||
|
/// components, to allow for different UI visual densities. It does not affect
|
||||||
|
/// text sizes, icon sizes, or padding values. The default value is 0.0,
|
||||||
|
/// corresponding to the metrics specified in the Material Design
|
||||||
|
/// specification. The value can range from [minimumDensity] to
|
||||||
|
/// [maximumDensity], inclusive.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ThemeData.visualDensity], where this property is used to specify the base
|
||||||
|
/// horizontal density of Material components.
|
||||||
|
/// * [Material design guidance on density](https://material.io/design/layout/applying-density.html).
|
||||||
|
final double horizontal;
|
||||||
|
|
||||||
|
/// The vertical visual density of UI components.
|
||||||
|
///
|
||||||
|
/// This property affects only the vertical spacing between and within
|
||||||
|
/// components, to allow for different UI visual densities. It does not affect
|
||||||
|
/// text sizes, icon sizes, or padding values. The default value is 0.0,
|
||||||
|
/// corresponding to the metrics specified in the Material Design
|
||||||
|
/// specification. The value can range from [minimumDensity] to
|
||||||
|
/// [maximumDensity], inclusive.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ThemeData.visualDensity], where this property is used to specify the base
|
||||||
|
/// vertical density of Material components.
|
||||||
|
/// * [Material design guidance on density](https://material.io/design/layout/applying-density.html).
|
||||||
|
final double vertical;
|
||||||
|
|
||||||
|
/// The base adjustment in logical pixels of the visual density of UI components.
|
||||||
|
///
|
||||||
|
/// The input density values are multiplied by a constant to arrive at a base
|
||||||
|
/// size adjustment that fits material design guidelines.
|
||||||
|
///
|
||||||
|
/// Individual components may adjust this value based upon their own
|
||||||
|
/// individual interpretation of density.
|
||||||
|
Offset get baseSizeAdjustment {
|
||||||
|
// The number of logical pixels represented by an increase or decrease in
|
||||||
|
// density by one. The Material Design guidelines say to increment/decrement
|
||||||
|
// sized in terms of four pixel increments.
|
||||||
|
const double interval = 4.0;
|
||||||
|
|
||||||
|
return Offset(horizontal, vertical) * interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Linearly interpolate between two densities.
|
||||||
|
static VisualDensity lerp(VisualDensity a, VisualDensity b, double t) {
|
||||||
|
return VisualDensity(
|
||||||
|
horizontal: lerpDouble(a.horizontal, b.horizontal, t),
|
||||||
|
vertical: lerpDouble(a.horizontal, b.horizontal, t),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) {
|
||||||
|
if (other.runtimeType != runtimeType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final VisualDensity typedOther = other;
|
||||||
|
return horizontal == typedOther.horizontal
|
||||||
|
&& vertical == typedOther.vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => hashValues(horizontal, vertical);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
|
super.debugFillProperties(properties);
|
||||||
|
properties.add(DoubleProperty('horizontal', horizontal, defaultValue: 0.0));
|
||||||
|
properties.add(DoubleProperty('vertical', vertical, defaultValue: 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toStringShort() {
|
||||||
|
return '${super.toStringShort()}(h: ${debugFormatDouble(horizontal)}, v: ${debugFormatDouble(vertical)})';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,18 @@ abstract class EdgeInsetsGeometry {
|
||||||
double get _start;
|
double get _start;
|
||||||
double get _top;
|
double get _top;
|
||||||
|
|
||||||
|
/// An [EdgeInsetsGeometry] with infinite offsets in each direction.
|
||||||
|
///
|
||||||
|
/// Can be used as an infinite upper bound for [clamp].
|
||||||
|
static const EdgeInsetsGeometry infinity = _MixedEdgeInsets.fromLRSETB(
|
||||||
|
double.infinity,
|
||||||
|
double.infinity,
|
||||||
|
double.infinity,
|
||||||
|
double.infinity,
|
||||||
|
double.infinity,
|
||||||
|
double.infinity,
|
||||||
|
);
|
||||||
|
|
||||||
/// Whether every dimension is non-negative.
|
/// Whether every dimension is non-negative.
|
||||||
bool get isNonNegative {
|
bool get isNonNegative {
|
||||||
return _left >= 0.0
|
return _left >= 0.0
|
||||||
|
@ -146,6 +158,19 @@ abstract class EdgeInsetsGeometry {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the a new [EdgeInsetsGeometry] object with all values greater than
|
||||||
|
/// or equal to `min`, and less than or equal to `max`.
|
||||||
|
EdgeInsetsGeometry clamp(EdgeInsetsGeometry min, EdgeInsetsGeometry max) {
|
||||||
|
return _MixedEdgeInsets.fromLRSETB(
|
||||||
|
_left.clamp(min._left, max._left),
|
||||||
|
_right.clamp(min._right, max._right),
|
||||||
|
_start.clamp(min._start, max._start),
|
||||||
|
_end.clamp(min._end, max._end),
|
||||||
|
_top.clamp(min._top, max._top),
|
||||||
|
_bottom.clamp(min._bottom, max._bottom),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the [EdgeInsetsGeometry] object with each dimension negated.
|
/// Returns the [EdgeInsetsGeometry] object with each dimension negated.
|
||||||
///
|
///
|
||||||
/// This is the same as multiplying the object by -1.0.
|
/// This is the same as multiplying the object by -1.0.
|
||||||
|
|
|
@ -690,6 +690,53 @@ void main() {
|
||||||
await tester.longPress(flatButton);
|
await tester.longPress(flatButton);
|
||||||
expect(didLongPressButton, isTrue);
|
expect(didLongPressButton, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('FlatButton responds to density changes.', (WidgetTester tester) async {
|
||||||
|
const Key key = Key('test');
|
||||||
|
|
||||||
|
Future<void> buildTest(VisualDensity visualDensity, {bool useText = false}) async {
|
||||||
|
return await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Directionality(
|
||||||
|
textDirection: TextDirection.rtl,
|
||||||
|
child: Center(
|
||||||
|
child: FlatButton(
|
||||||
|
visualDensity: visualDensity,
|
||||||
|
key: key,
|
||||||
|
onPressed: () {},
|
||||||
|
child: useText ? const Text('Text') : Container(width: 100, height: 100, color: const Color(0xffff0000)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity());
|
||||||
|
final RenderBox box = tester.renderObject(find.byKey(key));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(132, 100)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: 3.0, vertical: 3.0));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(156, 124)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: -3.0, vertical: -3.0));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(108, 100)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(), useText: true);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(88, 48)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: 3.0, vertical: 3.0), useText: true);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(112, 60)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: -3.0, vertical: -3.0), useText: true);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(76, 36)));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TextStyle _iconStyle(WidgetTester tester, IconData icon) {
|
TextStyle _iconStyle(WidgetTester tester, IconData icon) {
|
||||||
|
|
|
@ -728,4 +728,51 @@ void main() {
|
||||||
);
|
);
|
||||||
expect(tester.widget<Material>(rawButtonMaterial).shape, const StadiumBorder());
|
expect(tester.widget<Material>(rawButtonMaterial).shape, const StadiumBorder());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('MaterialButton responds to density changes.', (WidgetTester tester) async {
|
||||||
|
const Key key = Key('test');
|
||||||
|
|
||||||
|
Future<void> buildTest(VisualDensity visualDensity, {bool useText = false}) async {
|
||||||
|
return await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Directionality(
|
||||||
|
textDirection: TextDirection.rtl,
|
||||||
|
child: Center(
|
||||||
|
child: MaterialButton(
|
||||||
|
visualDensity: visualDensity,
|
||||||
|
key: key,
|
||||||
|
onPressed: () {},
|
||||||
|
child: useText ? const Text('Text') : Container(width: 100, height: 100, color: const Color(0xffff0000)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity());
|
||||||
|
final RenderBox box = tester.renderObject(find.byKey(key));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(132, 100)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: 3.0, vertical: 3.0));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(156, 124)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: -3.0, vertical: -3.0));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(108, 100)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(), useText: true);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(88, 48)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: 3.0, vertical: 3.0), useText: true);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(112, 60)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: -3.0, vertical: -3.0), useText: true);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(76, 36)));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -978,6 +978,53 @@ void main() {
|
||||||
await tester.longPress(outlineButton);
|
await tester.longPress(outlineButton);
|
||||||
expect(didLongPressButton, isTrue);
|
expect(didLongPressButton, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('OutlineButton responds to density changes.', (WidgetTester tester) async {
|
||||||
|
const Key key = Key('test');
|
||||||
|
|
||||||
|
Future<void> buildTest(VisualDensity visualDensity, {bool useText = false}) async {
|
||||||
|
return await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Directionality(
|
||||||
|
textDirection: TextDirection.rtl,
|
||||||
|
child: Center(
|
||||||
|
child: OutlineButton(
|
||||||
|
visualDensity: visualDensity,
|
||||||
|
key: key,
|
||||||
|
onPressed: () {},
|
||||||
|
child: useText ? const Text('Text') : Container(width: 100, height: 100, color: const Color(0xffff0000)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity());
|
||||||
|
final RenderBox box = tester.renderObject(find.byKey(key));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(132, 100)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: 3.0, vertical: 3.0));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(156, 124)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: -3.0, vertical: -3.0));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(108, 100)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(), useText: true);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(88, 48)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: 3.0, vertical: 3.0), useText: true);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(112, 60)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: -3.0, vertical: -3.0), useText: true);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(76, 36)));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
PhysicalModelLayer _findPhysicalLayer(Element element) {
|
PhysicalModelLayer _findPhysicalLayer(Element element) {
|
||||||
|
|
|
@ -557,6 +557,53 @@ void main() {
|
||||||
paintsExactlyCountTimes(#clipPath, 0),
|
paintsExactlyCountTimes(#clipPath, 0),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('RaisedButton responds to density changes.', (WidgetTester tester) async {
|
||||||
|
const Key key = Key('test');
|
||||||
|
|
||||||
|
Future<void> buildTest(VisualDensity visualDensity, {bool useText = false}) async {
|
||||||
|
return await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Directionality(
|
||||||
|
textDirection: TextDirection.rtl,
|
||||||
|
child: Center(
|
||||||
|
child: RaisedButton(
|
||||||
|
visualDensity: visualDensity,
|
||||||
|
key: key,
|
||||||
|
onPressed: () {},
|
||||||
|
child: useText ? const Text('Text') : Container(width: 100, height: 100, color: const Color(0xffff0000)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity());
|
||||||
|
final RenderBox box = tester.renderObject(find.byKey(key));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(132, 100)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: 3.0, vertical: 3.0));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(156, 124)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: -3.0, vertical: -3.0));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(108, 100)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(), useText: true);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(88, 48)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: 3.0, vertical: 3.0), useText: true);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(112, 60)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: -3.0, vertical: -3.0), useText: true);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(76, 36)));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TextStyle _iconStyle(WidgetTester tester, IconData icon) {
|
TextStyle _iconStyle(WidgetTester tester, IconData icon) {
|
||||||
|
|
|
@ -286,7 +286,7 @@ void main() {
|
||||||
expect(find.byKey(key).hitTestable(), findsOneWidget);
|
expect(find.byKey(key).hitTestable(), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('$RawMaterialButton can be expanded by parent constraints', (WidgetTester tester) async {
|
testWidgets('RawMaterialButton can be expanded by parent constraints', (WidgetTester tester) async {
|
||||||
const Key key = Key('test');
|
const Key key = Key('test');
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
@ -306,7 +306,7 @@ void main() {
|
||||||
expect(tester.getSize(find.byKey(key)), const Size(800.0, 48.0));
|
expect(tester.getSize(find.byKey(key)), const Size(800.0, 48.0));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('$RawMaterialButton handles focus', (WidgetTester tester) async {
|
testWidgets('RawMaterialButton handles focus', (WidgetTester tester) async {
|
||||||
final FocusNode focusNode = FocusNode(debugLabel: 'Button Focus');
|
final FocusNode focusNode = FocusNode(debugLabel: 'Button Focus');
|
||||||
const Key key = Key('test');
|
const Key key = Key('test');
|
||||||
const Color focusColor = Color(0xff00ff00);
|
const Color focusColor = Color(0xff00ff00);
|
||||||
|
@ -334,7 +334,7 @@ void main() {
|
||||||
expect(box, paints..rect(color: focusColor));
|
expect(box, paints..rect(color: focusColor));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('$RawMaterialButton loses focus when disabled.', (WidgetTester tester) async {
|
testWidgets('RawMaterialButton loses focus when disabled.', (WidgetTester tester) async {
|
||||||
final FocusNode focusNode = FocusNode(debugLabel: 'RawMaterialButton');
|
final FocusNode focusNode = FocusNode(debugLabel: 'RawMaterialButton');
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
@ -368,7 +368,7 @@ void main() {
|
||||||
expect(focusNode.hasPrimaryFocus, isFalse);
|
expect(focusNode.hasPrimaryFocus, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("Disabled $RawMaterialButton can't be traversed to when disabled.", (WidgetTester tester) async {
|
testWidgets("Disabled RawMaterialButton can't be traversed to when disabled.", (WidgetTester tester) async {
|
||||||
final FocusNode focusNode1 = FocusNode(debugLabel: '$RawMaterialButton 1');
|
final FocusNode focusNode1 = FocusNode(debugLabel: '$RawMaterialButton 1');
|
||||||
final FocusNode focusNode2 = FocusNode(debugLabel: '$RawMaterialButton 2');
|
final FocusNode focusNode2 = FocusNode(debugLabel: '$RawMaterialButton 2');
|
||||||
|
|
||||||
|
@ -406,7 +406,7 @@ void main() {
|
||||||
expect(focusNode2.hasPrimaryFocus, isFalse);
|
expect(focusNode2.hasPrimaryFocus, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('$RawMaterialButton handles hover', (WidgetTester tester) async {
|
testWidgets('RawMaterialButton handles hover', (WidgetTester tester) async {
|
||||||
const Key key = Key('test');
|
const Key key = Key('test');
|
||||||
const Color hoverColor = Color(0xff00ff00);
|
const Color hoverColor = Color(0xff00ff00);
|
||||||
|
|
||||||
|
@ -510,4 +510,51 @@ void main() {
|
||||||
await tester.longPress(rawMaterialButton);
|
await tester.longPress(rawMaterialButton);
|
||||||
expect(didLongPressButton, isTrue);
|
expect(didLongPressButton, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('RawMaterialButton responds to density changes.', (WidgetTester tester) async {
|
||||||
|
const Key key = Key('test');
|
||||||
|
|
||||||
|
Future<void> buildTest(VisualDensity visualDensity, {bool useText = false}) async {
|
||||||
|
return await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Directionality(
|
||||||
|
textDirection: TextDirection.rtl,
|
||||||
|
child: Center(
|
||||||
|
child: RawMaterialButton(
|
||||||
|
visualDensity: visualDensity,
|
||||||
|
key: key,
|
||||||
|
onPressed: () {},
|
||||||
|
child: useText ? const Text('Text') : Container(width: 100, height: 100, color: const Color(0xffff0000)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity());
|
||||||
|
final RenderBox box = tester.renderObject(find.byKey(key));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(100, 100)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: 3.0, vertical: 3.0));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(124, 124)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: -3.0, vertical: -3.0));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(100, 100)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(), useText: true);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(88, 48)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: 3.0, vertical: 3.0), useText: true);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(100, 60)));
|
||||||
|
|
||||||
|
await buildTest(const VisualDensity(horizontal: -3.0, vertical: -3.0), useText: true);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(box.size, equals(const Size(76, 36)));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,6 +201,7 @@ void main() {
|
||||||
|
|
||||||
final ThemeData theme = ThemeData.raw(
|
final ThemeData theme = ThemeData.raw(
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
|
visualDensity: const VisualDensity(),
|
||||||
primaryColor: Colors.black,
|
primaryColor: Colors.black,
|
||||||
primaryColorBrightness: Brightness.dark,
|
primaryColorBrightness: Brightness.dark,
|
||||||
primaryColorLight: Colors.black,
|
primaryColorLight: Colors.black,
|
||||||
|
@ -279,6 +280,7 @@ void main() {
|
||||||
|
|
||||||
final ThemeData otherTheme = ThemeData.raw(
|
final ThemeData otherTheme = ThemeData.raw(
|
||||||
brightness: Brightness.light,
|
brightness: Brightness.light,
|
||||||
|
visualDensity: const VisualDensity(),
|
||||||
primaryColor: Colors.white,
|
primaryColor: Colors.white,
|
||||||
primaryColorBrightness: Brightness.light,
|
primaryColorBrightness: Brightness.light,
|
||||||
primaryColorLight: Colors.white,
|
primaryColorLight: Colors.white,
|
||||||
|
|
Loading…
Reference in a new issue