divider.dart 9.92 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
Hans Muller's avatar
Hans Muller committed
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
// @dart = 2.8

Hans Muller's avatar
Hans Muller committed
7
import 'package:flutter/widgets.dart';
jslavitz's avatar
jslavitz committed
8
import 'package:flutter/painting.dart';
Hans Muller's avatar
Hans Muller committed
9

10
import 'divider_theme.dart';
Hans Muller's avatar
Hans Muller committed
11 12
import 'theme.dart';

13 14 15
// Examples can assume:
// BuildContext context;

16
/// A thin horizontal line, with padding on either side.
17
///
18 19
/// In the material design language, this represents a divider. Dividers can be
/// used in lists, [Drawer]s, and elsewhere to separate content.
20
///
21
/// To create a divider between [ListTile] items, consider using
jslavitz's avatar
jslavitz committed
22
/// [ListTile.divideTiles], which is optimized for this case.
23
///
24 25
/// {@youtube 560 315 https://www.youtube.com/watch?v=_liUC641Nmk}
///
jslavitz's avatar
jslavitz committed
26
/// The box's total height is controlled by [height]. The appropriate
27
/// padding is automatically computed from the height.
28
///
29
/// {@tool dartpad --template=stateless_widget_scaffold}
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
///
/// 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}
72
/// See also:
73
///
74
///  * [PopupMenuDivider], which is the equivalent but for popup menus.
75
///  * [ListTile.divideTiles], another approach to dividing widgets in a list.
76
///  * <https://material.io/design/components/dividers.html>
77
class Divider extends StatelessWidget {
78 79
  /// Creates a material design divider.
  ///
80 81
  /// The [height], [thickness], [indent], and [endIndent] must be null or
  /// non-negative.
82
  const Divider({
83
    Key key,
84 85 86 87
    this.height,
    this.thickness,
    this.indent,
    this.endIndent,
88
    this.color,
89 90 91 92
  }) : assert(height == null || height >= 0.0),
       assert(thickness == null || thickness >= 0.0),
       assert(indent == null || indent >= 0.0),
       assert(endIndent == null || endIndent >= 0.0),
93
       super(key: key);
Hans Muller's avatar
Hans Muller committed
94

jslavitz's avatar
jslavitz committed
95 96

  /// The divider's height extent.
97
  ///
98 99
  /// The divider itself is always drawn as a horizontal line that is centered
  /// within the height specified by this value.
100
  ///
101 102
  /// If this is null, then the [DividerThemeData.space] is used. If that is
  /// also null, then this defaults to 16.0.
Hans Muller's avatar
Hans Muller committed
103
  final double height;
104

105 106 107 108 109
  /// 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.
  ///
110
  /// If this is null, then the [DividerThemeData.thickness] is used. If
111 112 113 114 115 116 117
  /// 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.
Hans Muller's avatar
Hans Muller committed
118
  final double indent;
119

120 121 122 123
  /// 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.
124 125
  final double endIndent;

126 127
  /// The color to use when painting the line.
  ///
128 129
  /// If this is null, then the [DividerThemeData.color] is used. If that is
  /// also null, then [ThemeData.dividerColor] is used.
130
  ///
131
  /// {@tool snippet}
132
  ///
133
  /// ```dart
134
  /// Divider(
135 136
  ///   color: Colors.deepOrange,
  /// )
137
  /// ```
138
  /// {@end-tool}
Hans Muller's avatar
Hans Muller committed
139 140
  final Color color;

141
  /// Computes the [BorderSide] that represents a divider.
142
  ///
143 144 145 146 147 148 149 150
  /// 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.
151
  ///
152
  /// {@tool snippet}
153 154 155 156 157 158
  ///
  /// 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
159 160 161
  /// DecoratedBox(
  ///   decoration: BoxDecoration(
  ///     border: Border(
162 163 164 165 166 167 168
  ///       top: Divider.createBorderSide(context),
  ///       bottom: Divider.createBorderSide(context),
  ///     ),
  ///   ),
  ///   // child: ...
  /// )
  /// ```
169
  /// {@end-tool}
170
  static BorderSide createBorderSide(BuildContext context, { Color color, double width }) {
171 172 173 174 175 176 177 178 179 180 181 182 183
    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,
      );
    }
184
    return BorderSide(
185 186
      color: effectiveColor,
      width: effectiveWidth,
187 188 189
    );
  }

190
  @override
Hans Muller's avatar
Hans Muller committed
191
  Widget build(BuildContext context) {
192 193 194 195 196 197
    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;

198
    return SizedBox(
199
      height: height,
200 201
      child: Center(
        child: Container(
202
          height: thickness,
203
          margin: EdgeInsetsDirectional.only(start: indent, end: endIndent),
204 205
          decoration: BoxDecoration(
            border: Border(
206
              bottom: createBorderSide(context, color: color, width: thickness),
207 208 209
            ),
          ),
        ),
Hans Muller's avatar
Hans Muller committed
210 211 212 213
      ),
    );
  }
}
jslavitz's avatar
jslavitz committed
214

215
/// A thin vertical line, with padding on either side.
jslavitz's avatar
jslavitz committed
216
///
217 218 219
/// 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].
jslavitz's avatar
jslavitz committed
220 221 222 223 224 225
///
/// The box's total width is controlled by [width]. The appropriate
/// padding is automatically computed from the width.
///
/// See also:
///
226
///  * [ListView.separated], which can be used to generate vertical dividers.
227
///  * <https://material.io/design/components/dividers.html>
jslavitz's avatar
jslavitz committed
228
class VerticalDivider extends StatelessWidget {
229
  /// Creates a material design vertical divider.
jslavitz's avatar
jslavitz committed
230
  ///
231 232
  /// The [width], [thickness], [indent], and [endIndent] must be null or
  /// non-negative.
jslavitz's avatar
jslavitz committed
233 234
  const VerticalDivider({
    Key key,
235 236 237 238
    this.width,
    this.thickness,
    this.indent,
    this.endIndent,
239
    this.color,
240 241 242 243
  }) : assert(width == null || width >= 0.0),
       assert(thickness == null || thickness >= 0.0),
       assert(indent == null || indent >= 0.0),
       assert(endIndent == null || endIndent >= 0.0),
jslavitz's avatar
jslavitz committed
244 245 246 247
       super(key: key);

  /// The divider's width.
  ///
248 249
  /// The divider itself is always drawn as a vertical line that is centered
  /// within the width specified by this value.
jslavitz's avatar
jslavitz committed
250
  ///
251 252
  /// If this is null, then the [DividerThemeData.space] is used. If that is
  /// also null, then this defaults to 16.0.
jslavitz's avatar
jslavitz committed
253 254
  final double width;

255 256 257 258 259 260 261 262 263
  /// 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;

264
  /// The amount of empty space on top of the divider.
265 266 267
  ///
  /// If this is null, then the [DividerThemeData.indent] is used. If that is
  /// also null, then this defaults to 0.0.
jslavitz's avatar
jslavitz committed
268 269
  final double indent;

270
  /// The amount of empty space under the divider.
271 272 273
  ///
  /// If this is null, then the [DividerThemeData.endIndent] is used. If that is
  /// also null, then this defaults to 0.0.
274 275
  final double endIndent;

jslavitz's avatar
jslavitz committed
276 277
  /// The color to use when painting the line.
  ///
278 279
  /// If this is null, then the [DividerThemeData.color] is used. If that is
  /// also null, then [ThemeData.dividerColor] is used.
jslavitz's avatar
jslavitz committed
280
  ///
281
  /// {@tool snippet}
jslavitz's avatar
jslavitz committed
282 283 284 285 286 287
  ///
  /// ```dart
  /// Divider(
  ///   color: Colors.deepOrange,
  /// )
  /// ```
288
  /// {@end-tool}
jslavitz's avatar
jslavitz committed
289 290 291 292
  final Color color;

  @override
  Widget build(BuildContext context) {
293 294 295 296 297 298
    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;

jslavitz's avatar
jslavitz committed
299 300 301 302
    return SizedBox(
      width: width,
      child: Center(
        child: Container(
303
          width: thickness,
304
          margin: EdgeInsetsDirectional.only(top: indent, bottom: endIndent),
jslavitz's avatar
jslavitz committed
305 306
          decoration: BoxDecoration(
            border: Border(
307
              left: Divider.createBorderSide(context, color: color, width: thickness),
jslavitz's avatar
jslavitz committed
308 309 310 311 312 313 314
            ),
          ),
        ),
      ),
    );
  }
}