blob: edfb5665ff4f0b58e618435db80f22e42ba96c0b [file] [log] [blame]
// Copyright 2014 The Flutter 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/widgets.dart';
import 'package:flutter/painting.dart';
import 'divider_theme.dart';
import 'theme.dart';
// Examples can assume:
// BuildContext context;
/// A thin horizontal line, with padding on either side.
///
/// In the material design language, this represents a divider. Dividers can be
/// used in lists, [Drawer]s, and elsewhere to separate content.
///
/// To create a divider between [ListTile] items, consider using
/// [ListTile.divideTiles], which is optimized for this case.
///
/// The box's total height is controlled by [height]. The appropriate
/// padding is automatically computed from the height.
///
/// {@tool dartpad --template=stateless_widget_scaffold}
///
/// This sample shows how to display a Divider between an orange and blue box
/// inside a column. The Divider is 20 logical pixels in height and contains a
/// vertically centered black line that is 5 logical pixels thick. The black
/// line is indented by 20 logical pixels.
///
/// ![](https://flutter.github.io/assets-for-api-docs/assets/material/divider.png)
///
/// ```dart
/// Widget build(BuildContext context) {
/// return Center(
/// child: Column(
/// children: <Widget>[
/// Expanded(
/// child: Container(
/// color: Colors.amber,
/// child: const Center(
/// child: Text('Above'),
/// ),
/// ),
/// ),
/// const Divider(
/// color: Colors.black,
/// height: 20,
/// thickness: 5,
/// indent: 20,
/// endIndent: 0,
/// ),
/// Expanded(
/// child: Container(
/// color: Colors.blue,
/// child: const Center(
/// child: Text('Below'),
/// ),
/// ),
/// ),
/// ],
/// ),
/// );
/// }
/// ```
/// {@end-tool}
/// See also:
///
/// * [PopupMenuDivider], which is the equivalent but for popup menus.
/// * [ListTile.divideTiles], another approach to dividing widgets in a list.
/// * <https://material.io/design/components/dividers.html>
class Divider extends StatelessWidget {
/// Creates a material design divider.
///
/// The [height], [thickness], [indent], and [endIndent] must be null or
/// non-negative.
const Divider({
Key key,
this.height,
this.thickness,
this.indent,
this.endIndent,
this.color,
}) : 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);
/// The divider's height extent.
///
/// The divider itself is always drawn as a horizontal line that is centered
/// within the height specified by this value.
///
/// If this is null, then the [DividerThemeData.space] is used. If that is
/// also null, then this defaults to 16.0.
final double height;
/// 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;
/// 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;
/// The color to use when painting the line.
///
/// If this is null, then the [DividerThemeData.color] is used. If that is
/// also null, then [ThemeData.dividerColor] is used.
///
/// {@tool snippet}
///
/// ```dart
/// Divider(
/// color: Colors.deepOrange,
/// )
/// ```
/// {@end-tool}
final Color color;
/// Computes the [BorderSide] that represents a divider..
///
/// If [color] is null, then [DividerThemeData.color] is used. If that is also
/// null, then [ThemeData.dividerColor] is used.
///
/// If [width] is null, then [DividerThemeData.thickness] is used. If that is
/// also null, then this defaults to 0.0 (a hairline border).
///
/// If [context] is null, the default color of [BorderSide] is used and the
/// default width of 0.0 is used.
///
/// {@tool snippet}
///
/// This example uses this method to create a box that has a divider above and
/// below it. This is sometimes useful with lists, for instance, to separate a
/// scrollable section from the rest of the interface.
///
/// ```dart
/// DecoratedBox(
/// decoration: BoxDecoration(
/// border: Border(
/// top: Divider.createBorderSide(context),
/// bottom: Divider.createBorderSide(context),
/// ),
/// ),
/// // child: ...
/// )
/// ```
/// {@end-tool}
static BorderSide createBorderSide(BuildContext context, { Color color, double width }) {
final Color effectiveColor = color
?? (context != null ? (DividerTheme.of(context).color ?? Theme.of(context).dividerColor) : null);
final double effectiveWidth = width
?? (context != null ? DividerTheme.of(context).thickness : null)
?? 0.0;
// Prevent assertion since it is possible that context is null and no color
// is specified.
if (effectiveColor == null) {
return BorderSide(
width: effectiveWidth,
);
}
return BorderSide(
color: effectiveColor,
width: effectiveWidth,
);
}
@override
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(
height: height,
child: Center(
child: Container(
height: thickness,
margin: EdgeInsetsDirectional.only(start: indent, end: endIndent),
decoration: BoxDecoration(
border: Border(
bottom: createBorderSide(context, color: color, width: thickness),
),
),
),
),
);
}
}
/// A thin vertical line, with padding on either side.
///
/// In the material design language, this represents a divider. Vertical
/// dividers can be used in horizontally scrolling lists, such as a
/// [ListView] with [ListView.scrollDirection] set to [Axis.horizontal].
///
/// The box's total width is controlled by [width]. The appropriate
/// padding is automatically computed from the width.
///
/// See also:
///
/// * [ListView.separated], which can be used to generate vertical dividers.
/// * <https://material.io/design/components/dividers.html>
class VerticalDivider extends StatelessWidget {
/// Creates a material design vertical divider.
///
/// The [width], [thickness], [indent], and [endIndent] must be null or
/// non-negative.
const VerticalDivider({
Key key,
this.width,
this.thickness,
this.indent,
this.endIndent,
this.color,
}) : 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);
/// The divider's width.
///
/// The divider itself is always drawn as a vertical line that is centered
/// within the width specified by this value.
///
/// If this is null, then the [DividerThemeData.space] is used. If that is
/// also null, then this defaults to 16.0.
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.
///
/// If this is null, then the [DividerThemeData.indent] is used. If that is
/// also null, then this defaults to 0.0.
final double indent;
/// 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;
/// The color to use when painting the line.
///
/// If this is null, then the [DividerThemeData.color] is used. If that is
/// also null, then [ThemeData.dividerColor] is used.
///
/// {@tool snippet}
///
/// ```dart
/// Divider(
/// color: Colors.deepOrange,
/// )
/// ```
/// {@end-tool}
final Color color;
@override
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(
width: width,
child: Center(
child: Container(
width: thickness,
margin: EdgeInsetsDirectional.only(top: indent, bottom: endIndent),
decoration: BoxDecoration(
border: Border(
left: Divider.createBorderSide(context, color: color, width: thickness),
),
),
),
),
);
}
}