[Material] Create theme for Dividers to enable customization of thickness (#38621)
This commit is contained in:
parent
95b510618e
commit
13844aa62a
@ -50,6 +50,7 @@ export 'src/material/debug.dart';
|
|||||||
export 'src/material/dialog.dart';
|
export 'src/material/dialog.dart';
|
||||||
export 'src/material/dialog_theme.dart';
|
export 'src/material/dialog_theme.dart';
|
||||||
export 'src/material/divider.dart';
|
export 'src/material/divider.dart';
|
||||||
|
export 'src/material/divider_theme.dart';
|
||||||
export 'src/material/drawer.dart';
|
export 'src/material/drawer.dart';
|
||||||
export 'src/material/drawer_header.dart';
|
export 'src/material/drawer_header.dart';
|
||||||
export 'src/material/dropdown.dart';
|
export 'src/material/dropdown.dart';
|
||||||
|
@ -5,18 +5,18 @@
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter/painting.dart';
|
import 'package:flutter/painting.dart';
|
||||||
|
|
||||||
|
import 'divider_theme.dart';
|
||||||
import 'theme.dart';
|
import 'theme.dart';
|
||||||
|
|
||||||
// Examples can assume:
|
// Examples can assume:
|
||||||
// BuildContext context;
|
// BuildContext context;
|
||||||
|
|
||||||
/// A one device pixel thick horizontal line, with padding on either
|
/// A thin horizontal line, with padding on either side.
|
||||||
/// side.
|
|
||||||
///
|
///
|
||||||
/// In the material design language, this represents a divider. Dividers can be
|
/// In the material design language, this represents a divider. Dividers can be
|
||||||
/// used in lists, [Drawer]s, and elsewhere to separate content.
|
/// used in lists, [Drawer]s, and elsewhere to separate content.
|
||||||
///
|
///
|
||||||
/// To create a one-pixel divider between [ListTile] items, consider using
|
/// To create a divider between [ListTile] items, consider using
|
||||||
/// [ListTile.divideTiles], which is optimized for this case.
|
/// [ListTile.divideTiles], which is optimized for this case.
|
||||||
///
|
///
|
||||||
/// The box's total height is controlled by [height]. The appropriate
|
/// The box's total height is controlled by [height]. The appropriate
|
||||||
@ -30,36 +30,56 @@ import 'theme.dart';
|
|||||||
class Divider extends StatelessWidget {
|
class Divider extends StatelessWidget {
|
||||||
/// Creates a material design divider.
|
/// Creates a material design divider.
|
||||||
///
|
///
|
||||||
/// The height must be positive.
|
/// The [height], [thickness], [indent], and [endIndent] must be null or
|
||||||
|
/// non-negative.
|
||||||
const Divider({
|
const Divider({
|
||||||
Key key,
|
Key key,
|
||||||
this.height = 16.0,
|
this.height,
|
||||||
this.indent = 0.0,
|
this.thickness,
|
||||||
this.endIndent = 0.0,
|
this.indent,
|
||||||
|
this.endIndent,
|
||||||
this.color,
|
this.color,
|
||||||
}) : assert(height >= 0.0),
|
}) : assert(height == null || height >= 0.0),
|
||||||
|
assert(thickness == null || thickness >= 0.0),
|
||||||
|
assert(indent == null || indent >= 0.0),
|
||||||
|
assert(endIndent == null || endIndent >= 0.0),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
|
|
||||||
/// The divider's height extent.
|
/// The divider's height extent.
|
||||||
///
|
///
|
||||||
/// The divider itself is always drawn as one device pixel thick horizontal
|
/// The divider itself is always drawn as a horizontal line that is centered
|
||||||
/// line that is centered within the height specified by this value.
|
/// within the height specified by this value.
|
||||||
///
|
///
|
||||||
/// A divider with a [height] of 0.0 is always drawn as a line with a height
|
/// If this is null, then the [DividerThemeData.space] is used. If that is
|
||||||
/// of exactly one device pixel, without any padding around it.
|
/// also null, then this defaults to 16.0.
|
||||||
final double height;
|
final double height;
|
||||||
|
|
||||||
/// The amount of empty space to the left of the divider.
|
/// The thickness of the line drawn within the divider.
|
||||||
|
///
|
||||||
|
/// A divider with a [thickness] of 0.0 is always drawn as a line with a
|
||||||
|
/// height of exactly one device pixel.
|
||||||
|
///
|
||||||
|
/// If this is null, then the [DividerThemeData.dividerThickness] is used. If
|
||||||
|
/// that is also null, then this defaults to 0.0.
|
||||||
|
final double thickness;
|
||||||
|
|
||||||
|
/// The amount of empty space to the leading edge of the divider.
|
||||||
|
///
|
||||||
|
/// If this is null, then the [DividerThemeData.indent] is used. If that is
|
||||||
|
/// also null, then this defaults to 0.0.
|
||||||
final double indent;
|
final double indent;
|
||||||
|
|
||||||
/// The amount of empty space to the right of the divider.
|
/// The amount of empty space to the trailing edge of the divider.
|
||||||
|
///
|
||||||
|
/// If this is null, then the [DividerThemeData.endIndent] is used. If that is
|
||||||
|
/// also null, then this defaults to 0.0.
|
||||||
final double endIndent;
|
final double endIndent;
|
||||||
|
|
||||||
/// The color to use when painting the line.
|
/// The color to use when painting the line.
|
||||||
///
|
///
|
||||||
/// Defaults to the current theme's divider color, given by
|
/// If this is null, then the [DividerThemeData.color] is used. If that is
|
||||||
/// [ThemeData.dividerColor].
|
/// also null, then [ThemeData.dividerColor] is used.
|
||||||
///
|
///
|
||||||
/// {@tool sample}
|
/// {@tool sample}
|
||||||
///
|
///
|
||||||
@ -76,7 +96,7 @@ class Divider extends StatelessWidget {
|
|||||||
/// [ThemeData.dividerColor] specified in the ambient [Theme].
|
/// [ThemeData.dividerColor] specified in the ambient [Theme].
|
||||||
///
|
///
|
||||||
/// The `width` argument can be used to override the default width of the
|
/// The `width` argument can be used to override the default width of the
|
||||||
/// divider border, which is usually 0.0 (a hairline border).
|
/// divider border, which defaults to 0.0 (a hairline border).
|
||||||
///
|
///
|
||||||
/// {@tool sample}
|
/// {@tool sample}
|
||||||
///
|
///
|
||||||
@ -96,25 +116,30 @@ class Divider extends StatelessWidget {
|
|||||||
/// )
|
/// )
|
||||||
/// ```
|
/// ```
|
||||||
/// {@end-tool}
|
/// {@end-tool}
|
||||||
static BorderSide createBorderSide(BuildContext context, { Color color, double width = 0.0 }) {
|
static BorderSide createBorderSide(BuildContext context, { Color color, double width }) {
|
||||||
assert(width != null);
|
|
||||||
return BorderSide(
|
return BorderSide(
|
||||||
color: color ?? Theme.of(context).dividerColor,
|
color: color ?? DividerTheme.of(context).color ?? Theme.of(context).dividerColor,
|
||||||
width: width,
|
width: width ?? DividerTheme.of(context).thickness ?? 0.0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final DividerThemeData dividerTheme = DividerTheme.of(context);
|
||||||
|
final double height = this.height ?? dividerTheme.space ?? 16.0;
|
||||||
|
final double thickness = this.thickness ?? dividerTheme.thickness ?? 0.0;
|
||||||
|
final double indent = this.indent ?? dividerTheme.indent ?? 0.0;
|
||||||
|
final double endIndent = this.endIndent ?? dividerTheme.endIndent ?? 0.0;
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: height,
|
height: height,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 0.0,
|
height: thickness,
|
||||||
margin: EdgeInsetsDirectional.only(start: indent, end: endIndent),
|
margin: EdgeInsetsDirectional.only(start: indent, end: endIndent),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(
|
border: Border(
|
||||||
bottom: createBorderSide(context, color: color),
|
bottom: createBorderSide(context, color: color, width: thickness),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -123,8 +148,7 @@ class Divider extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A one device pixel thick vertical line, with padding on either
|
/// A thin vertical line, with padding on either side.
|
||||||
/// side.
|
|
||||||
///
|
///
|
||||||
/// In the material design language, this represents a divider. Vertical
|
/// In the material design language, this represents a divider. Vertical
|
||||||
/// dividers can be used in horizontally scrolling lists, such as a
|
/// dividers can be used in horizontally scrolling lists, such as a
|
||||||
@ -138,37 +162,57 @@ class Divider extends StatelessWidget {
|
|||||||
/// * [ListView.separated], which can be used to generate vertical dividers.
|
/// * [ListView.separated], which can be used to generate vertical dividers.
|
||||||
/// * <https://material.io/design/components/dividers.html>
|
/// * <https://material.io/design/components/dividers.html>
|
||||||
class VerticalDivider extends StatelessWidget {
|
class VerticalDivider extends StatelessWidget {
|
||||||
/// Creates a material design divider.
|
/// Creates a material design vertical divider.
|
||||||
///
|
///
|
||||||
/// The width must be positive.
|
/// The [width], [thickness], [indent], and [endIndent] must be null or
|
||||||
|
/// non-negative.
|
||||||
const VerticalDivider({
|
const VerticalDivider({
|
||||||
Key key,
|
Key key,
|
||||||
this.width = 16.0,
|
this.width,
|
||||||
this.indent = 0.0,
|
this.thickness,
|
||||||
this.endIndent = 0.0,
|
this.indent,
|
||||||
|
this.endIndent,
|
||||||
this.color,
|
this.color,
|
||||||
}) : assert(width >= 0.0),
|
}) : assert(width == null || width >= 0.0),
|
||||||
|
assert(thickness == null || thickness >= 0.0),
|
||||||
|
assert(indent == null || indent >= 0.0),
|
||||||
|
assert(endIndent == null || endIndent >= 0.0),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
/// The divider's width.
|
/// The divider's width.
|
||||||
///
|
///
|
||||||
/// The divider itself is always drawn as one device pixel thick
|
/// The divider itself is always drawn as a vertical line that is centered
|
||||||
/// line that is centered within the width specified by this value.
|
/// within the width specified by this value.
|
||||||
///
|
///
|
||||||
/// A divider with a [width] of 0.0 is always drawn as a line with a width
|
/// If this is null, then the [DividerThemeData.space] is used. If that is
|
||||||
/// of exactly one device pixel, without any padding around it.
|
/// also null, then this defaults to 16.0.
|
||||||
final double width;
|
final double width;
|
||||||
|
|
||||||
|
/// The thickness of the line drawn within the divider.
|
||||||
|
///
|
||||||
|
/// A divider with a [thickness] of 0.0 is always drawn as a line with a
|
||||||
|
/// width of exactly one device pixel.
|
||||||
|
///
|
||||||
|
/// If this is null, then the [DividerThemeData.thickness] is used which
|
||||||
|
/// defaults to 0.0.
|
||||||
|
final double thickness;
|
||||||
|
|
||||||
/// The amount of empty space on top of the divider.
|
/// The amount of empty space on top of the divider.
|
||||||
|
///
|
||||||
|
/// If this is null, then the [DividerThemeData.indent] is used. If that is
|
||||||
|
/// also null, then this defaults to 0.0.
|
||||||
final double indent;
|
final double indent;
|
||||||
|
|
||||||
/// The amount of empty space under the divider.
|
/// The amount of empty space under the divider.
|
||||||
|
///
|
||||||
|
/// If this is null, then the [DividerThemeData.endIndent] is used. If that is
|
||||||
|
/// also null, then this defaults to 0.0.
|
||||||
final double endIndent;
|
final double endIndent;
|
||||||
|
|
||||||
/// The color to use when painting the line.
|
/// The color to use when painting the line.
|
||||||
///
|
///
|
||||||
/// Defaults to the current theme's divider color, given by
|
/// If this is null, then the [DividerThemeData.color] is used. If that is
|
||||||
/// [ThemeData.dividerColor].
|
/// also null, then [ThemeData.dividerColor] is used.
|
||||||
///
|
///
|
||||||
/// {@tool sample}
|
/// {@tool sample}
|
||||||
///
|
///
|
||||||
@ -182,15 +226,21 @@ class VerticalDivider extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final DividerThemeData dividerTheme = DividerTheme.of(context);
|
||||||
|
final double width = this.width ?? dividerTheme.space ?? 16.0;
|
||||||
|
final double thickness = this.thickness ?? dividerTheme.thickness ?? 0.0;
|
||||||
|
final double indent = this.indent ?? dividerTheme.indent ?? 0.0;
|
||||||
|
final double endIndent = this.endIndent ?? dividerTheme.endIndent ?? 0.0;
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: width,
|
width: width,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 0.0,
|
width: thickness,
|
||||||
margin: EdgeInsetsDirectional.only(top: indent, bottom: endIndent),
|
margin: EdgeInsetsDirectional.only(top: indent, bottom: endIndent),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(
|
border: Border(
|
||||||
left: Divider.createBorderSide(context, color: color),
|
left: Divider.createBorderSide(context, color: color, width: thickness),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
168
packages/flutter/lib/src/material/divider_theme.dart
Normal file
168
packages/flutter/lib/src/material/divider_theme.dart
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
// Copyright 2019 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 'dart:ui' show lerpDouble;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
import 'theme.dart';
|
||||||
|
|
||||||
|
/// Defines the visual properties of [Divider], [VerticalDivider], dividers
|
||||||
|
/// between [ListTile]s, and dividers between rows in [DataTable]s.
|
||||||
|
///
|
||||||
|
/// Descendant widgets obtain the current [DividerThemeData] object using
|
||||||
|
/// `DividerTheme.of(context)`. Instances of [DividerThemeData]
|
||||||
|
/// can be customized with [DividerThemeData.copyWith].
|
||||||
|
///
|
||||||
|
/// Typically a [DividerThemeData] is specified as part of the overall
|
||||||
|
/// [Theme] with [ThemeData.dividerTheme].
|
||||||
|
///
|
||||||
|
/// All [DividerThemeData] properties are `null` by default. When null,
|
||||||
|
/// the widgets will provide their own defaults.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ThemeData], which describes the overall theme information for the
|
||||||
|
/// application.
|
||||||
|
class DividerThemeData extends Diagnosticable {
|
||||||
|
|
||||||
|
/// Creates a theme that can be used for [DividerTheme] or
|
||||||
|
/// [ThemeData.dividerTheme].
|
||||||
|
const DividerThemeData({
|
||||||
|
this.color,
|
||||||
|
this.space,
|
||||||
|
this.thickness,
|
||||||
|
this.indent,
|
||||||
|
this.endIndent,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// The color of [Divider]s and [VerticalDivider]s, also
|
||||||
|
/// used between [ListTile]s, between rows in [DataTable]s, and so forth.
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
/// The [Divider]'s width or the [VerticalDivider]'s height.
|
||||||
|
///
|
||||||
|
/// This represents the amount of horizontal or vertical space the divider
|
||||||
|
/// takes up.
|
||||||
|
final double space;
|
||||||
|
|
||||||
|
/// The thickness of the line drawn within the divider.
|
||||||
|
final double thickness;
|
||||||
|
|
||||||
|
/// The amount of empty space at the leading edge of [Divider] or top edge of
|
||||||
|
/// [VerticalDivider].
|
||||||
|
final double indent;
|
||||||
|
|
||||||
|
/// The amount of empty space at the trailing edge of [Divider] or bottom edge
|
||||||
|
/// of [VerticalDivider].
|
||||||
|
final double endIndent;
|
||||||
|
|
||||||
|
/// Creates a copy of this object with the given fields replaced with the
|
||||||
|
/// new values.
|
||||||
|
DividerThemeData copyWith({
|
||||||
|
Color color,
|
||||||
|
double space,
|
||||||
|
double thickness,
|
||||||
|
double indent,
|
||||||
|
double endIndent,
|
||||||
|
}) {
|
||||||
|
return DividerThemeData(
|
||||||
|
color: color ?? this.color,
|
||||||
|
space: space ?? this.space,
|
||||||
|
thickness: thickness ?? this.thickness,
|
||||||
|
indent: indent ?? this.indent,
|
||||||
|
endIndent: endIndent ?? this.endIndent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Linearly interpolate between two Divider themes.
|
||||||
|
///
|
||||||
|
/// The argument `t` must not be null.
|
||||||
|
///
|
||||||
|
/// {@macro dart.ui.shadow.lerp}
|
||||||
|
static DividerThemeData lerp(DividerThemeData a, DividerThemeData b, double t) {
|
||||||
|
assert(t != null);
|
||||||
|
return DividerThemeData(
|
||||||
|
color: Color.lerp(a?.color, b?.color, t),
|
||||||
|
space: lerpDouble(a?.space, b?.space, t),
|
||||||
|
thickness: lerpDouble(a?.thickness, b?.thickness, t),
|
||||||
|
indent: lerpDouble(a?.indent, b?.indent, t),
|
||||||
|
endIndent: lerpDouble(a?.endIndent, b?.endIndent, t),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return hashValues(
|
||||||
|
color,
|
||||||
|
space,
|
||||||
|
thickness,
|
||||||
|
indent,
|
||||||
|
endIndent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(dynamic other) {
|
||||||
|
if (identical(this, other))
|
||||||
|
return true;
|
||||||
|
if (other.runtimeType != runtimeType)
|
||||||
|
return false;
|
||||||
|
final DividerThemeData typedOther = other;
|
||||||
|
return typedOther.color == color
|
||||||
|
&& typedOther.space == space
|
||||||
|
&& typedOther.thickness == thickness
|
||||||
|
&& typedOther.indent == indent
|
||||||
|
&& typedOther.endIndent == endIndent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
|
super.debugFillProperties(properties);
|
||||||
|
properties.add(ColorProperty('color', color, defaultValue: null));
|
||||||
|
properties.add(DoubleProperty('space', space, defaultValue: null));
|
||||||
|
properties.add(DoubleProperty('thickness', thickness, defaultValue: null));
|
||||||
|
properties.add(DoubleProperty('indent', indent, defaultValue: null));
|
||||||
|
properties.add(DoubleProperty('endIndent', endIndent, defaultValue: null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An inherited widget that defines the configuration for
|
||||||
|
/// [Divider]s, [VerticalDividers]s, dividers between [ListTile]s, and dividers
|
||||||
|
/// between rows in [DataTable]s in this widget's subtree.
|
||||||
|
class DividerTheme extends InheritedWidget {
|
||||||
|
/// Creates a divider theme that controls the configurations for
|
||||||
|
/// [Divider]s, [VerticalDividers]s, dividers between [ListTile]s, and dividers
|
||||||
|
/// between rows in [DataTable]s in its widget subtree.
|
||||||
|
const DividerTheme({
|
||||||
|
Key key,
|
||||||
|
@required this.data,
|
||||||
|
Widget child,
|
||||||
|
}) : assert(data != null),
|
||||||
|
super(key: key, child: child);
|
||||||
|
|
||||||
|
/// The properties for descendant [Divider]s, [VerticalDividers]s, dividers
|
||||||
|
/// between [ListTile]s, and dividers between rows in [DataTable]s.
|
||||||
|
final DividerThemeData data;
|
||||||
|
|
||||||
|
/// The closest instance of this class's [data] value that encloses the given
|
||||||
|
/// context.
|
||||||
|
///
|
||||||
|
/// If there is no ancestor, it returns [ThemeData.dividerTheme]. Applications
|
||||||
|
/// can assume that the returned value will not be null.
|
||||||
|
///
|
||||||
|
/// Typical usage is as follows:
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// DividerThemeData theme = DividerTheme.of(context);
|
||||||
|
/// ```
|
||||||
|
static DividerThemeData of(BuildContext context) {
|
||||||
|
final DividerTheme dividerTheme = context.inheritFromWidgetOfExactType(DividerTheme);
|
||||||
|
return dividerTheme?.data ?? Theme.of(context).dividerTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool updateShouldNotify(DividerTheme oldWidget) => data != oldWidget.data;
|
||||||
|
}
|
@ -19,6 +19,7 @@ import 'chip_theme.dart';
|
|||||||
import 'color_scheme.dart';
|
import 'color_scheme.dart';
|
||||||
import 'colors.dart';
|
import 'colors.dart';
|
||||||
import 'dialog_theme.dart';
|
import 'dialog_theme.dart';
|
||||||
|
import 'divider_theme.dart';
|
||||||
import 'floating_action_button_theme.dart';
|
import 'floating_action_button_theme.dart';
|
||||||
import 'ink_splash.dart';
|
import 'ink_splash.dart';
|
||||||
import 'ink_well.dart' show InteractiveInkFeatureFactory;
|
import 'ink_well.dart' show InteractiveInkFeatureFactory;
|
||||||
@ -178,6 +179,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
BottomSheetThemeData bottomSheetTheme,
|
BottomSheetThemeData bottomSheetTheme,
|
||||||
PopupMenuThemeData popupMenuTheme,
|
PopupMenuThemeData popupMenuTheme,
|
||||||
MaterialBannerThemeData bannerTheme,
|
MaterialBannerThemeData bannerTheme,
|
||||||
|
DividerThemeData dividerTheme,
|
||||||
}) {
|
}) {
|
||||||
brightness ??= Brightness.light;
|
brightness ??= Brightness.light;
|
||||||
final bool isDark = brightness == Brightness.dark;
|
final bool isDark = brightness == Brightness.dark;
|
||||||
@ -282,6 +284,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
bottomSheetTheme ??= const BottomSheetThemeData();
|
bottomSheetTheme ??= const BottomSheetThemeData();
|
||||||
popupMenuTheme ??= const PopupMenuThemeData();
|
popupMenuTheme ??= const PopupMenuThemeData();
|
||||||
bannerTheme ??= const MaterialBannerThemeData();
|
bannerTheme ??= const MaterialBannerThemeData();
|
||||||
|
dividerTheme ??= const DividerThemeData();
|
||||||
|
|
||||||
return ThemeData.raw(
|
return ThemeData.raw(
|
||||||
brightness: brightness,
|
brightness: brightness,
|
||||||
@ -344,6 +347,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
bottomSheetTheme: bottomSheetTheme,
|
bottomSheetTheme: bottomSheetTheme,
|
||||||
popupMenuTheme: popupMenuTheme,
|
popupMenuTheme: popupMenuTheme,
|
||||||
bannerTheme: bannerTheme,
|
bannerTheme: bannerTheme,
|
||||||
|
dividerTheme: dividerTheme,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,6 +422,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
@required this.bottomSheetTheme,
|
@required this.bottomSheetTheme,
|
||||||
@required this.popupMenuTheme,
|
@required this.popupMenuTheme,
|
||||||
@required this.bannerTheme,
|
@required this.bannerTheme,
|
||||||
|
@required this.dividerTheme,
|
||||||
}) : assert(brightness != null),
|
}) : assert(brightness != null),
|
||||||
assert(primaryColor != null),
|
assert(primaryColor != null),
|
||||||
assert(primaryColorBrightness != null),
|
assert(primaryColorBrightness != null),
|
||||||
@ -474,7 +479,8 @@ class ThemeData extends Diagnosticable {
|
|||||||
assert(snackBarTheme != null),
|
assert(snackBarTheme != null),
|
||||||
assert(bottomSheetTheme != null),
|
assert(bottomSheetTheme != null),
|
||||||
assert(popupMenuTheme != null),
|
assert(popupMenuTheme != null),
|
||||||
assert(bannerTheme != null);
|
assert(bannerTheme != null),
|
||||||
|
assert(dividerTheme != null);
|
||||||
|
|
||||||
/// Create a [ThemeData] based on the colors in the given [colorScheme] and
|
/// Create a [ThemeData] based on the colors in the given [colorScheme] and
|
||||||
/// text styles of the optional [textTheme].
|
/// text styles of the optional [textTheme].
|
||||||
@ -867,6 +873,10 @@ class ThemeData extends Diagnosticable {
|
|||||||
/// A theme for customizing the color and text style of a [MaterialBanner].
|
/// A theme for customizing the color and text style of a [MaterialBanner].
|
||||||
final MaterialBannerThemeData bannerTheme;
|
final MaterialBannerThemeData bannerTheme;
|
||||||
|
|
||||||
|
/// A theme for customizing the color, thickness, and indents of [Divider]s,
|
||||||
|
/// [VerticalDivider]s, etc.
|
||||||
|
final DividerThemeData dividerTheme;
|
||||||
|
|
||||||
/// 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,
|
||||||
@ -929,6 +939,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
BottomSheetThemeData bottomSheetTheme,
|
BottomSheetThemeData bottomSheetTheme,
|
||||||
PopupMenuThemeData popupMenuTheme,
|
PopupMenuThemeData popupMenuTheme,
|
||||||
MaterialBannerThemeData bannerTheme,
|
MaterialBannerThemeData bannerTheme,
|
||||||
|
DividerThemeData dividerTheme,
|
||||||
}) {
|
}) {
|
||||||
cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault();
|
cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault();
|
||||||
return ThemeData.raw(
|
return ThemeData.raw(
|
||||||
@ -992,6 +1003,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
bottomSheetTheme: bottomSheetTheme ?? this.bottomSheetTheme,
|
bottomSheetTheme: bottomSheetTheme ?? this.bottomSheetTheme,
|
||||||
popupMenuTheme: popupMenuTheme ?? this.popupMenuTheme,
|
popupMenuTheme: popupMenuTheme ?? this.popupMenuTheme,
|
||||||
bannerTheme: bannerTheme ?? this.bannerTheme,
|
bannerTheme: bannerTheme ?? this.bannerTheme,
|
||||||
|
dividerTheme: dividerTheme ?? this.dividerTheme,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1133,6 +1145,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
bottomSheetTheme: BottomSheetThemeData.lerp(a.bottomSheetTheme, b.bottomSheetTheme, t),
|
bottomSheetTheme: BottomSheetThemeData.lerp(a.bottomSheetTheme, b.bottomSheetTheme, t),
|
||||||
popupMenuTheme: PopupMenuThemeData.lerp(a.popupMenuTheme, b.popupMenuTheme, t),
|
popupMenuTheme: PopupMenuThemeData.lerp(a.popupMenuTheme, b.popupMenuTheme, t),
|
||||||
bannerTheme: MaterialBannerThemeData.lerp(a.bannerTheme, b.bannerTheme, t),
|
bannerTheme: MaterialBannerThemeData.lerp(a.bannerTheme, b.bannerTheme, t),
|
||||||
|
dividerTheme: DividerThemeData.lerp(a.dividerTheme, b.dividerTheme, t),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1201,7 +1214,8 @@ class ThemeData extends Diagnosticable {
|
|||||||
(otherData.snackBarTheme == snackBarTheme) &&
|
(otherData.snackBarTheme == snackBarTheme) &&
|
||||||
(otherData.bottomSheetTheme == bottomSheetTheme) &&
|
(otherData.bottomSheetTheme == bottomSheetTheme) &&
|
||||||
(otherData.popupMenuTheme == popupMenuTheme) &&
|
(otherData.popupMenuTheme == popupMenuTheme) &&
|
||||||
(otherData.bannerTheme == bannerTheme);
|
(otherData.bannerTheme == bannerTheme) &&
|
||||||
|
(otherData.dividerTheme == dividerTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -1270,6 +1284,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
bottomSheetTheme,
|
bottomSheetTheme,
|
||||||
popupMenuTheme,
|
popupMenuTheme,
|
||||||
bannerTheme,
|
bannerTheme,
|
||||||
|
dividerTheme,
|
||||||
];
|
];
|
||||||
return hashList(values);
|
return hashList(values);
|
||||||
}
|
}
|
||||||
@ -1335,6 +1350,7 @@ class ThemeData extends Diagnosticable {
|
|||||||
properties.add(DiagnosticsProperty<BottomSheetThemeData>('bottomSheetTheme', bottomSheetTheme, defaultValue: defaultData.bottomSheetTheme));
|
properties.add(DiagnosticsProperty<BottomSheetThemeData>('bottomSheetTheme', bottomSheetTheme, defaultValue: defaultData.bottomSheetTheme));
|
||||||
properties.add(DiagnosticsProperty<PopupMenuThemeData>('popupMenuTheme', popupMenuTheme, defaultValue: defaultData.popupMenuTheme));
|
properties.add(DiagnosticsProperty<PopupMenuThemeData>('popupMenuTheme', popupMenuTheme, defaultValue: defaultData.popupMenuTheme));
|
||||||
properties.add(DiagnosticsProperty<MaterialBannerThemeData>('bannerTheme', bannerTheme, defaultValue: defaultData.bannerTheme));
|
properties.add(DiagnosticsProperty<MaterialBannerThemeData>('bannerTheme', bannerTheme, defaultValue: defaultData.bannerTheme));
|
||||||
|
properties.add(DiagnosticsProperty<DividerThemeData>('dividerTheme', dividerTheme, defaultValue: defaultData.dividerTheme));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,25 @@ void main() {
|
|||||||
);
|
);
|
||||||
final RenderBox box = tester.firstRenderObject(find.byType(Divider));
|
final RenderBox box = tester.firstRenderObject(find.byType(Divider));
|
||||||
expect(box.size.height, 16.0);
|
expect(box.size.height, 16.0);
|
||||||
expect(find.byType(Divider), paints..path(strokeWidth: 0.0));
|
final Container container = tester.widget(find.byType(Container));
|
||||||
|
final BoxDecoration decoration = container.decoration;
|
||||||
|
expect(decoration.border.bottom.width, 0.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Divider custom thickness', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
const Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: Center(
|
||||||
|
child: Divider(
|
||||||
|
thickness: 5.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final Container container = tester.widget(find.byType(Container));
|
||||||
|
final BoxDecoration decoration = container.decoration;
|
||||||
|
expect(decoration.border.bottom.width, 5.0);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Horizontal divider custom indentation', (WidgetTester tester) async {
|
testWidgets('Horizontal divider custom indentation', (WidgetTester tester) async {
|
||||||
@ -86,7 +104,27 @@ void main() {
|
|||||||
);
|
);
|
||||||
final RenderBox box = tester.firstRenderObject(find.byType(VerticalDivider));
|
final RenderBox box = tester.firstRenderObject(find.byType(VerticalDivider));
|
||||||
expect(box.size.width, 16.0);
|
expect(box.size.width, 16.0);
|
||||||
expect(find.byType(VerticalDivider), paints..path(strokeWidth: 0.0));
|
final Container container = tester.widget(find.byType(Container));
|
||||||
|
final BoxDecoration decoration = container.decoration;
|
||||||
|
final Border border = decoration.border;
|
||||||
|
expect(border.left.width, 0.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Divider custom thickness', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
const Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: Center(
|
||||||
|
child: VerticalDivider(
|
||||||
|
thickness: 5.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final Container container = tester.widget(find.byType(Container));
|
||||||
|
final BoxDecoration decoration = container.decoration;
|
||||||
|
final Border border = decoration.border;
|
||||||
|
expect(border.left.width, 5.0);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Vertical Divider Test 2', (WidgetTester tester) async {
|
testWidgets('Vertical Divider Test 2', (WidgetTester tester) async {
|
||||||
|
238
packages/flutter/test/material/divider_theme_test.dart
Normal file
238
packages/flutter/test/material/divider_theme_test.dart
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
// Copyright 2019 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/rendering.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('DividerThemeData copyWith, ==, hashCode basics', () {
|
||||||
|
expect(const DividerThemeData(), const DividerThemeData().copyWith());
|
||||||
|
expect(const DividerThemeData().hashCode, const DividerThemeData().copyWith().hashCode);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('DividerThemeData null fields by default', () {
|
||||||
|
const DividerThemeData dividerTheme = DividerThemeData();
|
||||||
|
expect(dividerTheme.color, null);
|
||||||
|
expect(dividerTheme.space, null);
|
||||||
|
expect(dividerTheme.thickness, null);
|
||||||
|
expect(dividerTheme.indent, null);
|
||||||
|
expect(dividerTheme.endIndent, null);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Default DividerThemeData debugFillProperties', (WidgetTester tester) async {
|
||||||
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||||
|
const DividerThemeData().debugFillProperties(builder);
|
||||||
|
|
||||||
|
final List<String> description = builder.properties
|
||||||
|
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
||||||
|
.map((DiagnosticsNode node) => node.toString())
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
expect(description, <String>[]);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('DividerThemeData implements debugFillProperties', (WidgetTester tester) async {
|
||||||
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||||
|
const DividerThemeData(
|
||||||
|
color: Color(0xFFFFFFFF),
|
||||||
|
space: 5.0,
|
||||||
|
thickness: 4.0,
|
||||||
|
indent: 3.0,
|
||||||
|
endIndent: 2.0,
|
||||||
|
).debugFillProperties(builder);
|
||||||
|
|
||||||
|
final List<String> description = builder.properties
|
||||||
|
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
||||||
|
.map((DiagnosticsNode node) => node.toString())
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
expect(description, <String>[
|
||||||
|
'color: Color(0xffffffff)',
|
||||||
|
'space: 5.0',
|
||||||
|
'thickness: 4.0',
|
||||||
|
'indent: 3.0',
|
||||||
|
'endIndent: 2.0',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('Horizontal Divider', () {
|
||||||
|
testWidgets('Passing no DividerThemeData returns defaults', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(const MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: Divider(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
final RenderBox box = tester.firstRenderObject(find.byType(Divider));
|
||||||
|
expect(box.size.height, 16.0);
|
||||||
|
|
||||||
|
final Container container = tester.widget(find.byType(Container));
|
||||||
|
final BoxDecoration decoration = container.decoration;
|
||||||
|
expect(decoration.border.bottom.width, 0.0);
|
||||||
|
|
||||||
|
final ThemeData theme = ThemeData();
|
||||||
|
expect(decoration.border.bottom.color, theme.dividerColor);
|
||||||
|
|
||||||
|
final Rect dividerRect = tester.getRect(find.byType(Divider));
|
||||||
|
final Rect lineRect = tester.getRect(find.byType(DecoratedBox));
|
||||||
|
expect(lineRect.left, dividerRect.left);
|
||||||
|
expect(lineRect.right, dividerRect.right);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Uses values from DividerThemeData', (WidgetTester tester) async {
|
||||||
|
final DividerThemeData dividerTheme = _dividerTheme();
|
||||||
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
theme: ThemeData(dividerTheme: dividerTheme),
|
||||||
|
home: const Scaffold(
|
||||||
|
body: Divider(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
final RenderBox box = tester.firstRenderObject(find.byType(Divider));
|
||||||
|
expect(box.size.height, dividerTheme.space);
|
||||||
|
|
||||||
|
final Container container = tester.widget(find.byType(Container));
|
||||||
|
final BoxDecoration decoration = container.decoration;
|
||||||
|
expect(decoration.border.bottom.width, dividerTheme.thickness);
|
||||||
|
expect(decoration.border.bottom.color, dividerTheme.color);
|
||||||
|
|
||||||
|
final Rect dividerRect = tester.getRect(find.byType(Divider));
|
||||||
|
final Rect lineRect = tester.getRect(find.byType(DecoratedBox));
|
||||||
|
expect(lineRect.left, dividerRect.left + dividerTheme.indent);
|
||||||
|
expect(lineRect.right, dividerRect.right - dividerTheme.endIndent);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Widget properties take priority over theme', (WidgetTester tester) async {
|
||||||
|
const Color color = Colors.purple;
|
||||||
|
const double height = 10.0;
|
||||||
|
const double thickness = 5.0;
|
||||||
|
const double indent = 8.0;
|
||||||
|
const double endIndent = 9.0;
|
||||||
|
|
||||||
|
final DividerThemeData dividerTheme = _dividerTheme();
|
||||||
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
theme: ThemeData(dividerTheme: dividerTheme),
|
||||||
|
home: const Scaffold(
|
||||||
|
body: Divider(
|
||||||
|
color: color,
|
||||||
|
height: height,
|
||||||
|
thickness: thickness,
|
||||||
|
indent: indent,
|
||||||
|
endIndent: endIndent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
final RenderBox box = tester.firstRenderObject(find.byType(Divider));
|
||||||
|
expect(box.size.height, height);
|
||||||
|
|
||||||
|
final Container container = tester.widget(find.byType(Container));
|
||||||
|
final BoxDecoration decoration = container.decoration;
|
||||||
|
expect(decoration.border.bottom.width, thickness);
|
||||||
|
expect(decoration.border.bottom.color, color);
|
||||||
|
|
||||||
|
final Rect dividerRect = tester.getRect(find.byType(Divider));
|
||||||
|
final Rect lineRect = tester.getRect(find.byType(DecoratedBox));
|
||||||
|
expect(lineRect.left, dividerRect.left + indent);
|
||||||
|
expect(lineRect.right, dividerRect.right - endIndent);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('Vertical Divider', () {
|
||||||
|
testWidgets('Passing no DividerThemeData returns defaults', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(const MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: VerticalDivider(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
final RenderBox box = tester.firstRenderObject(find.byType(VerticalDivider));
|
||||||
|
expect(box.size.width, 16.0);
|
||||||
|
|
||||||
|
final Container container = tester.widget(find.byType(Container));
|
||||||
|
final BoxDecoration decoration = container.decoration;
|
||||||
|
final Border border = decoration.border;
|
||||||
|
expect(border.left.width, 0.0);
|
||||||
|
|
||||||
|
final ThemeData theme = ThemeData();
|
||||||
|
expect(border.left.color, theme.dividerColor);
|
||||||
|
|
||||||
|
final Rect dividerRect = tester.getRect(find.byType(VerticalDivider));
|
||||||
|
final Rect lineRect = tester.getRect(find.byType(DecoratedBox));
|
||||||
|
expect(lineRect.top, dividerRect.top);
|
||||||
|
expect(lineRect.bottom, dividerRect.bottom);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Uses values from DividerThemeData', (WidgetTester tester) async {
|
||||||
|
final DividerThemeData dividerTheme = _dividerTheme();
|
||||||
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
theme: ThemeData(dividerTheme: dividerTheme),
|
||||||
|
home: const Scaffold(
|
||||||
|
body: VerticalDivider(),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
final RenderBox box = tester.firstRenderObject(find.byType(VerticalDivider));
|
||||||
|
expect(box.size.width, dividerTheme.space);
|
||||||
|
|
||||||
|
final Container container = tester.widget(find.byType(Container));
|
||||||
|
final BoxDecoration decoration = container.decoration;
|
||||||
|
final Border border = decoration.border;
|
||||||
|
expect(border.left.width, dividerTheme.thickness);
|
||||||
|
expect(border.left.color, dividerTheme.color);
|
||||||
|
|
||||||
|
final Rect dividerRect = tester.getRect(find.byType(VerticalDivider));
|
||||||
|
final Rect lineRect = tester.getRect(find.byType(DecoratedBox));
|
||||||
|
expect(lineRect.top, dividerRect.top + dividerTheme.indent);
|
||||||
|
expect(lineRect.bottom, dividerRect.bottom - dividerTheme.endIndent);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Widget properties take priority over theme', (WidgetTester tester) async {
|
||||||
|
const Color color = Colors.purple;
|
||||||
|
const double width = 10.0;
|
||||||
|
const double thickness = 5.0;
|
||||||
|
const double indent = 8.0;
|
||||||
|
const double endIndent = 9.0;
|
||||||
|
|
||||||
|
final DividerThemeData dividerTheme = _dividerTheme();
|
||||||
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
theme: ThemeData(dividerTheme: dividerTheme),
|
||||||
|
home: const Scaffold(
|
||||||
|
body: VerticalDivider(
|
||||||
|
color: color,
|
||||||
|
width: width,
|
||||||
|
thickness: thickness,
|
||||||
|
indent: indent,
|
||||||
|
endIndent: endIndent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
final RenderBox box = tester.firstRenderObject(find.byType(VerticalDivider));
|
||||||
|
expect(box.size.width, width);
|
||||||
|
|
||||||
|
final Container container = tester.widget(find.byType(Container));
|
||||||
|
final BoxDecoration decoration = container.decoration;
|
||||||
|
final Border border = decoration.border;
|
||||||
|
expect(border.left.width, thickness);
|
||||||
|
expect(border.left.color, color);
|
||||||
|
|
||||||
|
final Rect dividerRect = tester.getRect(find.byType(VerticalDivider));
|
||||||
|
final Rect lineRect = tester.getRect(find.byType(DecoratedBox));
|
||||||
|
expect(lineRect.top, dividerRect.top + indent);
|
||||||
|
expect(lineRect.bottom, dividerRect.bottom - endIndent);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
DividerThemeData _dividerTheme() {
|
||||||
|
return const DividerThemeData(
|
||||||
|
color: Colors.orange,
|
||||||
|
space: 12.0,
|
||||||
|
thickness: 2.0,
|
||||||
|
indent: 7.0,
|
||||||
|
endIndent: 5.0,
|
||||||
|
);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user