container.dart 7.82 KB
Newer Older
1 2 3 4
// Copyright 2016 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.

5
import 'package:flutter/foundation.dart';
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 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 72
import 'package:flutter/painting.dart';
import 'package:flutter/rendering.dart';

import 'basic.dart';
import 'framework.dart';
import 'image.dart';

/// A widget that paints a [Decoration] either before or after its child paints.
///
/// [Container] insets its child by the widths of the borders; this widget does
/// not.
///
/// Commonly used with [BoxDecoration].
class DecoratedBox extends SingleChildRenderObjectWidget {
  /// Creates a widget that paints a [Decoration].
  ///
  /// The [decoration] and [position] arguments must not be null. By default the
  /// decoration paints behind the child.
  DecoratedBox({
    Key key,
    @required this.decoration,
    this.position: DecorationPosition.background,
    Widget child
  }) : super(key: key, child: child) {
    assert(decoration != null);
    assert(position != null);
  }

  /// What decoration to paint.
  ///
  /// Commonly a [BoxDecoration].
  final Decoration decoration;

  /// Whether to paint the box decoration behind or in front of the child.
  final DecorationPosition position;

  @override
  RenderDecoratedBox createRenderObject(BuildContext context) {
    return new RenderDecoratedBox(
      decoration: decoration,
      position: position,
      configuration: createLocalImageConfiguration(context)
    );
  }

  @override
  void updateRenderObject(BuildContext context, RenderDecoratedBox renderObject) {
    renderObject
      ..decoration = decoration
      ..configuration = createLocalImageConfiguration(context)
      ..position = position;
  }
}

/// A convenience widget that combines common painting, positioning, and sizing
/// widgets.
///
/// A container first surrounds the child with [padding] (inflated by any
/// borders present in the [decoration]) and then applies additional
/// [constraints] to the padded extent (incorporating the [width] and [height]
/// as constraints, if either is non-null). The container is then surrounded by
/// additional empty space described from the [margin].
///
/// During painting, the container first applies the given [transform], then
/// paints the [decoration] to fill the padded extent, then it paints the child,
/// and finally paints the [foregroundDecoration], also filling the padded
/// extent.
73 74 75 76 77 78
///
/// Containers with no children try to be as big as possible unless the incoming
/// constraints are unbounded, in which case they try to be as small as
/// possible. Containers with children size themselves to their children. The
/// `width`, `height`, and [constraints] arguments to the constructor override
/// this.
79 80
class Container extends StatelessWidget {
  /// Creates a widget that combines common painting, positioning, and sizing widgets.
81 82
  ///
  /// The `height` and `width` values include the padding.
83 84 85 86 87 88
  ///
  /// The `color` argument is a shorthand for
  /// `decoration: new BoxDecoration(backgroundColor: color)`, which means you
  /// cannot supply both a `color` and a `decoration` argument. If you want to
  /// have both a `color` and a `decoration`, you can pass the color as the
  /// `backgroundColor` argument to the `BoxDecoration`.
89 90
  Container({
    Key key,
91
    this.alignment,
92
    this.padding,
93 94
    Color color,
    Decoration decoration,
95 96 97 98 99 100
    this.foregroundDecoration,
    double width,
    double height,
    BoxConstraints constraints,
    this.margin,
    this.transform,
101
    this.child,
102 103
  }) : decoration = decoration ?? (color != null ? new BoxDecoration(backgroundColor: color) : null),
       constraints =
104 105 106 107 108 109 110
        (width != null || height != null)
          ? constraints?.tighten(width: width, height: height)
            ?? new BoxConstraints.tightFor(width: width, height: height)
          : constraints,
       super(key: key) {
    assert(margin == null || margin.isNonNegative);
    assert(padding == null || padding.isNonNegative);
111 112
    assert(decoration == null || decoration.debugAssertIsValid());
    assert(constraints == null || constraints.debugAssertIsValid());
113 114 115 116
    assert(color == null || decoration == null,
      'Cannot provide both a color and a decoration\n'
      'The color argument is just a shorthand for "decoration: new BoxDecoration(backgroundColor: color)".'
    );
117 118
  }

119
  /// The [child] contained by the container.
120
  ///
121 122 123 124
  /// If null, and if the [constraints] are unbounded or also null, the
  /// container will expand to fill all available space in its parent, unless
  /// the parent provides unbounded constraints, in which case the container
  /// will attempt to be as small as possible.
125 126
  final Widget child;

127
  /// Align the [child] within the container.
128 129
  ///
  /// If non-null, the container will expand to fill its parent and position its
130 131 132 133
  /// child within itself according to the given value. If the incoming
  /// constraints are unbounded, then the child will be shrink-wrapped instead.
  ///
  /// Ignored if [child] is null.
134
  final FractionalOffset alignment;
135

136 137
  /// Empty space to inscribe inside the [decoration]. The [child], if any, is
  /// placed inside this padding.
138 139
  final EdgeInsets padding;

140
  /// The decoration to paint behind the [child].
141 142
  final Decoration decoration;

143
  /// The decoration to paint in front of the [child].
144 145 146
  final Decoration foregroundDecoration;

  /// Additional constraints to apply to the child.
147
  ///
148 149 150
  /// The constructor `width` and `height` arguments are combined with the
  /// `constraints` argument to set this property.
  ///
151
  /// The [padding] goes inside the constraints.
152 153
  final BoxConstraints constraints;

154
  /// Empty space to surround the [decoration] and [child].
155 156 157 158 159 160 161 162
  final EdgeInsets margin;

  /// The transformation matrix to apply before painting the container.
  final Matrix4 transform;

  EdgeInsets get _paddingIncludingDecoration {
    if (decoration == null || decoration.padding == null)
      return padding;
163
    final EdgeInsets decorationPadding = decoration.padding;
164 165 166 167 168 169 170 171 172
    if (padding == null)
      return decorationPadding;
    return padding + decorationPadding;
  }

  @override
  Widget build(BuildContext context) {
    Widget current = child;

173 174 175 176 177 178 179
    if (child == null && (constraints == null || !constraints.isTight)) {
      current = new LimitedBox(
        maxWidth: 0.0,
        maxHeight: 0.0,
        child: new ConstrainedBox(constraints: const BoxConstraints.expand())
      );
    }
180

181 182 183
    if (alignment != null)
      current = new Align(alignment: alignment, child: current);

184
    final EdgeInsets effectivePadding = _paddingIncludingDecoration;
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
    if (effectivePadding != null)
      current = new Padding(padding: effectivePadding, child: current);

    if (decoration != null)
      current = new DecoratedBox(decoration: decoration, child: current);

    if (foregroundDecoration != null) {
      current = new DecoratedBox(
        decoration: foregroundDecoration,
        position: DecorationPosition.foreground,
        child: current
      );
    }

    if (constraints != null)
      current = new ConstrainedBox(constraints: constraints, child: current);

    if (margin != null)
      current = new Padding(padding: margin, child: current);

    if (transform != null)
      current = new Transform(transform: transform, child: current);

    return current;
  }

  @override
  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
    if (constraints != null)
      description.add('$constraints');
216 217
    if (alignment != null)
      description.add('$alignment');
218 219 220 221 222 223 224 225 226 227 228 229
    if (decoration != null)
      description.add('bg: $decoration');
    if (foregroundDecoration != null)
      description.add('fg: $foregroundDecoration');
    if (margin != null)
      description.add('margin: $margin');
    if (padding != null)
      description.add('padding: $padding');
    if (transform != null)
      description.add('has transform');
  }
}