Commit 9cb12086 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Samples for BoxDecoration and some related classes (#9902)

parent 9935b307
......@@ -122,7 +122,7 @@ class BorderRadius {
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
if (other is! BorderRadius)
if (runtimeType != other.runtimeType)
return false;
final BorderRadius typedOther = other;
return topLeft == typedOther.topLeft &&
......@@ -152,6 +152,37 @@ enum BorderStyle {
}
/// A side of a border of a box.
///
/// A [Border] consists of four [BorderSide] objects: [Border.top],
/// [Border.left], [Border.right], and [Border.bottom].
///
/// ## Sample code
///
/// This sample shows how [BorderSide] objects can be used in a [Container], via
/// a [BoxDecoration] and a [Border], to decorate some [Text]. In this example,
/// the text has a thick bar above it that is light blue, and a thick bar below
/// it that is a darker shade of blue.
///
/// ```dart
/// new Container(
/// padding: new EdgeInsets.all(8.0),
/// decoration: new BoxDecoration(
/// border: new Border(
/// top: new BorderSide(width: 16.0, color: Colors.lightBlue.shade50),
/// bottom: new BorderSide(width: 16.0, color: Colors.lightBlue.shade900),
/// ),
/// ),
/// child: new Text('Flutter in the sky', textAlign: TextAlign.center),
/// ),
/// ```
///
/// See also:
///
/// * [Border], which uses [BorderSide] objects to represent its sides.
/// * [BoxDecoration], which optionally takes a [Border] object.
/// * [TableBorder], which extends [Border] to have two more sides
/// ([TableBorder.horizontalInside] and [TableBorder.verticalInside]), both
/// of which are also [BorderSide] objects.
@immutable
class BorderSide {
/// Creates the side of a border.
......@@ -228,7 +259,7 @@ class BorderSide {
return new BorderSide(
color: Color.lerp(colorA, colorB, t),
width: ui.lerpDouble(a.width, b.width, t),
style: BorderStyle.solid
style: BorderStyle.solid,
);
}
......@@ -236,7 +267,7 @@ class BorderSide {
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
if (other is! BorderSide)
if (runtimeType != other.runtimeType)
return false;
final BorderSide typedOther = other;
return color == typedOther.color &&
......@@ -252,6 +283,58 @@ class BorderSide {
}
/// A border of a box, comprised of four sides.
///
/// The sides are represented by [BorderSide] objects.
///
/// ## Sample code
///
/// ```dart
/// // All four borders the same, two-pixel wide solid white:
/// const Border.all(width: 2.0, color: const Color(0xFFFFFFFF))
/// ```
///
/// ```dart
/// // The border for a material design divider:
/// new Border(bottom: new BorderSide(color: Theme.of(context).dividerColor))
/// ```
///
/// ```dart
/// // A 1990s-era "OK" button:
/// new Container(
/// decoration: const BoxDecoration(
/// border: const Border(
/// top: const BorderSide(width: 1.0, color: const Color(0xFFFFFFFFFF)),
/// left: const BorderSide(width: 1.0, color: const Color(0xFFFFFFFFFF)),
/// right: const BorderSide(width: 1.0, color: const Color(0xFFFF000000)),
/// bottom: const BorderSide(width: 1.0, color: const Color(0xFFFF000000)),
/// ),
/// ),
/// child: new Container(
/// padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 2.0),
/// decoration: const BoxDecoration(
/// border: const Border(
/// top: const BorderSide(width: 1.0, color: const Color(0xFFFFDFDFDF)),
/// left: const BorderSide(width: 1.0, color: const Color(0xFFFFDFDFDF)),
/// right: const BorderSide(width: 1.0, color: const Color(0xFFFF7F7F7F)),
/// bottom: const BorderSide(width: 1.0, color: const Color(0xFFFF7F7F7F)),
/// ),
/// color: const Color(0xFFBFBFBF),
/// ),
/// child: const Text(
/// 'OK',
/// textAlign: TextAlign.center,
/// style: const TextStyle(color: const Color(0xFF000000))
/// ),
/// ),
/// ),
/// ```
///
/// See also:
///
/// * [BoxDecoration], which uses this class to describe its edge decoration.
/// * [BorderSide], which is used to describe each side of the box.
/// * [Theme], from the material layer, which can be queried to obtain appropriate colors
/// to use for borders in a material app, as shown in the "divider" sample above.
@immutable
class Border {
/// Creates a border.
......@@ -261,14 +344,16 @@ class Border {
this.top: BorderSide.none,
this.right: BorderSide.none,
this.bottom: BorderSide.none,
this.left: BorderSide.none
this.left: BorderSide.none,
});
/// A uniform border with all sides the same color and width.
///
/// The sides default to black solid borders, one logical pixel wide.
factory Border.all({
Color color: const Color(0xFF000000),
double width: 1.0,
BorderStyle style: BorderStyle.solid
BorderStyle style: BorderStyle.solid,
}) {
final BorderSide side = new BorderSide(color: color, width: width, style: style);
return new Border(top: side, right: side, bottom: side, left: side);
......@@ -286,12 +371,16 @@ class Border {
/// The left side of this border.
final BorderSide left;
/// The widths of the sides of this border represented as an EdgeInsets.
/// The widths of the sides of this border represented as an [EdgeInsets].
///
/// This can be used, for example, with a [Padding] widget to inset a box by
/// the size of these borders.
EdgeInsets get dimensions {
return new EdgeInsets.fromLTRB(left.width, top.width, right.width, bottom.width);
}
/// Whether all four sides of the border are identical.
/// Whether all four sides of the border are identical. Uniform borders are
/// typically more efficient to paint.
bool get isUniform {
assert(top != null);
assert(right != null);
......@@ -319,7 +408,7 @@ class Border {
return true;
}
/// Creates a new border with the widths of this border multiplied by [t].
/// Creates a new border with the widths of this border multiplied by `t`.
Border scale(double t) {
return new Border(
top: top.copyWith(width: t * top.width),
......@@ -330,6 +419,9 @@ class Border {
}
/// Linearly interpolate between two borders.
///
/// If a border is null, it is treated as having four [BorderSide.none]
/// borders.
static Border lerp(Border a, Border b, double t) {
if (a == null && b == null)
return null;
......@@ -345,24 +437,35 @@ class Border {
);
}
/// Paints the border within the given rect on the given canvas.
/// Paints the border within the given [Rect] on the given [Canvas].
///
/// Uniform borders are more efficient to paint than more complex borders.
///
/// You can provide a [BoxShape] to draw the border on. If the shape in
/// [BoxShape.circle], there is the requirement that the border [isUniform].
///
/// If you specify a rectangular box shape (BoxShape.rectangle), then you may
/// specify a [BorderRadius]. If a border radius is specified, there is the
/// requirement that the border [isUniform].
void paint(Canvas canvas, Rect rect, {
BoxShape shape: BoxShape.rectangle,
BorderRadius borderRadius: null
BorderRadius borderRadius: null,
}) {
if (isUniform) {
if (borderRadius != null) {
assert(shape == BoxShape.rectangle, 'A borderRadius can only be given for rectangular boxes.');
_paintBorderWithRadius(canvas, rect, borderRadius);
return;
}
if (shape == BoxShape.circle) {
assert(borderRadius == null);
_paintBorderWithCircle(canvas, rect);
return;
}
}
assert(borderRadius == null); // TODO(abarth): Support non-uniform rounded borders.
assert(shape == BoxShape.rectangle); // TODO(ianh): Support non-uniform borders on circles.
assert(borderRadius == null, 'A borderRadius can only be given for uniform borders.'); // TODO(abarth): Support non-uniform rounded borders.
assert(shape == BoxShape.rectangle, 'A border can only be drawn as a circle if it is uniform.'); // TODO(ianh): Support non-uniform borders on circles.
assert(top != null);
assert(right != null);
......@@ -483,7 +586,7 @@ class Border {
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
if (other.runtimeType != runtimeType)
if (runtimeType != other.runtimeType)
return false;
final Border typedOther = other;
return top == typedOther.top &&
......@@ -598,7 +701,7 @@ class BoxShadow {
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
if (other is! BoxShadow)
if (runtimeType != other.runtimeType)
return false;
final BoxShadow typedOther = other;
return color == typedOther.color &&
......@@ -615,6 +718,9 @@ class BoxShadow {
}
/// A 2D gradient.
///
/// This is an interface that allows [LinearGradient] and [RadialGradient]
/// classes to be used interchangeably in [BoxDecoration]s.
@immutable
abstract class Gradient {
/// Abstract const constructor. This constructor enables subclasses to provide
......@@ -626,6 +732,52 @@ abstract class Gradient {
}
/// A 2D linear gradient.
///
/// This class is used by [BoxDecoration] to represent gradients. This abstracts
/// out the arguments to the [new ui.Gradient.linear] constructor from the
/// `dart:ui` library.
///
/// A gradient has two anchor points, [begin] and [end]. The [begin] point
/// corresponds to 0.0, and the [end] point corresponds to 1.0. These points are
/// expressed in fractions, so that the same gradient can be reused with varying
/// sized boxes without changing the parameters. (This contrasts with [new
/// ui.Gradient.linear], whose arguments are expressed in logical pixels.)
///
/// The [colors] are described by a list of [Color] objects. There must be at
/// least two colors. If there are more than two, a [stops] list must be
/// provided. It must have the same length as [colors], and specifies the
/// position of each color stop between 0.0 and 1.0.
///
/// The region of the canvas before [begin] and after [end] is colored according
/// to [tileMode].
///
/// Typically this class is used with [BoxDecoration], which does the painting.
/// To use a [LinearGradient] to paint on a canvas directly, see [createShader].
///
/// ## Sample code
///
/// This sample draws a picture that looks like vertical window shades by having
/// a [Container] display a [BoxDecoration] with a [LinearGradient].
///
/// ```dart
/// new Container(
/// decoration: new BoxDecoration(
/// gradient: new LinearGradient(
/// begin: FractionalOffset.topLeft,
/// end: new FractionalOffset(0.1, 0.0), // 10% of the width, so there are ten blinds.
/// colors: [const Color(0xFFFFFFEE), const Color(0xFF999999)], // whitish to gray
/// tileMode: TileMode.repeated, // repeats the gradient over the canvas
/// ),
/// ),
/// ),
/// ```
///
/// See also:
///
/// * [RadialGradient], which displays a gradient in concentric circles, and
/// has an example which shows a different way to use [Gradient] objects.
/// * [BoxDecoration], which can take a [LinearGradient] in its
/// [BoxDecoration.gradient] property.
class LinearGradient extends Gradient {
/// Creates a linear graident.
///
......@@ -636,7 +788,7 @@ class LinearGradient extends Gradient {
this.end: FractionalOffset.centerRight,
this.colors,
this.stops,
this.tileMode: TileMode.clamp
this.tileMode: TileMode.clamp,
}) : assert(begin != null),
assert(end != null),
assert(colors != null),
......@@ -660,17 +812,32 @@ class LinearGradient extends Gradient {
/// The colors the gradient should obtain at each of the stops.
///
/// If [stops] is non-null, this list must have the same length as [stops].
/// If [stops] is non-null, this list must have the same length as [stops]. If
/// [colors] has more than two colors, [stops] must be non-null.
///
/// This list must have at least two colors in it (otherwise, it's not a
/// gradient!).
final List<Color> colors;
/// A list of values from 0.0 to 1.0 that denote fractions of the vector from
/// start to end.
///
/// If non-null, this list must have the same length as [colors]. Otherwise
/// the colors are distributed evenly between [begin] and [end].
/// If non-null, this list must have the same length as [colors]. If
/// [colors] has more than two colors, [stops] must be non-null.
///
/// If the first value is not 0.0, then a stop with position 0.0 and a color
/// equal to the first color in [colors] is implied.
///
/// If the last value is not 1.0, then a stop with position 1.0 and a color
/// equal to the last color in [colors] is implied.
///
/// The values in the [stops] list must be in ascending order. If a value in
/// the [stops] list is less than an earlier value in the list, then its value
/// is assumed to equal the previous value.
final List<double> stops;
/// How this gradient should tile the plane.
/// How this gradient should tile the plane beyond in the region before
/// [begin] and after [end].
final TileMode tileMode;
@override
......@@ -682,7 +849,8 @@ class LinearGradient extends Gradient {
);
}
/// Returns a new [LinearGradient] with its properties scaled by the given factor.
/// Returns a new [LinearGradient] with its properties scaled by the given
/// factor.
LinearGradient scale(double factor) {
return new LinearGradient(
begin: begin,
......@@ -696,8 +864,10 @@ class LinearGradient extends Gradient {
/// Linearly interpolate between two [LinearGradient]s.
///
/// If either gradient is null, this function linearly interpolates from a
/// a gradient that matches the other gradient in begin, end, stops and
/// tileMode and with the same colors but transparent.
/// a gradient that matches the other gradient in [begin], [end], [stops] and
/// [tileMode] and with the same [colors] but transparent.
///
/// If neither gradient is null, they must have the same number of [colors].
static LinearGradient lerp(LinearGradient a, LinearGradient b, double t) {
if (a == null && b == null)
return null;
......@@ -707,16 +877,16 @@ class LinearGradient extends Gradient {
return a.scale(1.0 - t);
// Interpolation is only possible when the lengths of colors and stops are
// the same or stops is null for one.
// TODO(xster): lerp unsimilar LinearGradients in the future by scaling
// lists of LinearGradients.
// TODO(xster): lerp unsimilar LinearGradients in the future by scaling
// lists of LinearGradients.
assert(a.colors.length == b.colors.length);
assert(a.stops == null || b.stops == null || a.stops.length == b.stops.length);
final List<Color> interpolatedColors = <Color>[];
for (int i = 0; i < a.colors.length; i++)
for (int i = 0; i < a.colors.length; i += 1)
interpolatedColors.add(Color.lerp(a.colors[i], b.colors[i], t));
List<double> interpolatedStops;
if (a.stops != null && b.stops != null) {
for (int i = 0; i < a.stops.length; i++)
for (int i = 0; i < a.stops.length; i += 1)
interpolatedStops.add(ui.lerpDouble(a.stops[i], b.stops[i], t));
} else {
interpolatedStops = a.stops ?? b.stops;
......@@ -734,7 +904,7 @@ class LinearGradient extends Gradient {
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
if (other is! LinearGradient)
if (runtimeType != other.runtimeType)
return false;
final LinearGradient typedOther = other;
if (begin != typedOther.begin ||
......@@ -772,6 +942,56 @@ class LinearGradient extends Gradient {
}
/// A 2D radial gradient.
///
/// This class is used by [BoxDecoration] to represent gradients. This abstracts
/// out the arguments to the [new ui.Gradient.radial] constructor from the
/// `dart:ui` library.
///
/// A gradient has a [center] and a [radius]. The [center] point corresponds to
/// 0.0, and the ring at [radius] from the center corresponds to 1.0. These
/// lengths are expressed in fractions, so that the same gradient can be reused
/// with varying sized boxes without changing the parameters. (This contrasts
/// with [new ui.Gradient.radial], whose arguments are expressed in logical
/// pixels.)
///
/// The [colors] are described by a list of [Color] objects. There must be at
/// least two colors. If there are more than two, a [stops] list must be
/// provided. It must have the same length as [colors], and specifies the
/// position of each color stop between 0.0 and 1.0.
///
/// The region of the canvas beyond [radius] from the [center] is colored
/// according to [tileMode].
///
/// Typically this class is used with [BoxDecoration], which does the painting.
/// To use a [RadialGradient] to paint on a canvas directly, see [createShader].
///
/// ## Sample code
///
/// ```dart
/// // This gradient looks like a sun in a blue sky.
/// var gradient = new RadialGradient(
/// center: const FractionalOffset(0.7, 0.2), // near the top right
/// radius: 0.2,
/// colors: [
/// const Color(0xFFFFFF00), // yellow sun
/// const Color(0xFF0099FF), // blue sky
/// ],
/// stops: [0.4, 1.0],
/// );
/// // rect is the area we are painting over
/// var paint = new Paint()
/// ..shader = gradient.createShader(rect);
/// canvas.drawRect(rect, paint);
/// ```
///
/// See also:
///
/// * [LinearGradient], which displays a gradient in parallel lines, and has an
/// example which shows a different way to use [Gradient] objects.
/// * [BoxDecoration], which can take a [RadialGradient] in its
/// [BoxDecoration.gradient] property.
/// * [CustomPainter], which shows how to use the above sample code in a custom
/// painter.
class RadialGradient extends Gradient {
/// Creates a radial graident.
///
......@@ -782,7 +1002,7 @@ class RadialGradient extends Gradient {
this.radius: 0.5,
this.colors,
this.stops,
this.tileMode: TileMode.clamp
this.tileMode: TileMode.clamp,
});
/// The center of the gradient, as an offset into the unit square
......@@ -802,7 +1022,11 @@ class RadialGradient extends Gradient {
/// The colors the gradient should obtain at each of the stops.
///
/// If [stops] is non-null, this list must have the same length as [stops].
/// If [stops] is non-null, this list must have the same length as [stops]. If
/// [colors] has more than two colors, [stops] must be non-null.
///
/// This list must have at least two colors in it (otherwise, it's not a
/// gradient!).
final List<Color> colors;
/// A list of values from 0.0 to 1.0 that denote concentric rings.
......@@ -810,12 +1034,22 @@ class RadialGradient extends Gradient {
/// The rings are centered at [center] and have a radius equal to the value of
/// the stop times [radius].
///
/// If non-null, this list must have the same length as [colors]. Otherwise
/// the colors are distributed evenly between the [center] and the ring at
/// [radius].
/// If non-null, this list must have the same length as [colors]. If
/// [colors] has more than two colors, [stops] must be non-null.
///
/// If the first value is not 0.0, then a stop with position 0.0 and a color
/// equal to the first color in [colors] is implied.
///
/// If the last value is not 1.0, then a stop with position 1.0 and a color
/// equal to the last color in [colors] is implied.
///
/// The values in the [stops] list must be in ascending order. If a value in
/// the [stops] list is less than an earlier value in the list, then its value
/// is assumed to equal the previous value.
final List<double> stops;
/// How this gradient should tile the plane.
/// How this gradient should tile the plane beyond the outer ring at [radius]
/// pixels from the [center].
final TileMode tileMode;
@override
......@@ -831,7 +1065,7 @@ class RadialGradient extends Gradient {
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
if (other is! RadialGradient)
if (runtimeType != other.runtimeType)
return false;
final RadialGradient typedOther = other;
if (center != typedOther.center ||
......
......@@ -1811,6 +1811,41 @@ class RenderFractionalTranslation extends RenderProxyBox {
///
/// The [hitTest] method is called when the user interacts with the underlying
/// render object, to determine if the user hit the object or missed it.
///
/// ## Sample code
///
/// This sample extends the same code shown for [RadialGradient] to create a
/// custom painter that paints a sky.
///
/// ```dart
/// class Sky extends CustomPainter {
/// @override
/// void paint(Canvas canvas, Size size) {
/// var rect = Offset.zero & size;
/// var gradient = new RadialGradient(
/// center: const FractionalOffset(0.7, 0.2),
/// radius: 0.2,
/// colors: [const Color(0xFFFFFF00), const Color(0xFF0099FF)],
/// stops: [0.4, 1.0],
/// );
/// canvas.drawRect(
/// rect,
/// new Paint()..shader = gradient.createShader(rect),
/// );
/// }
///
/// @override
/// bool shouldRepaint(CustomPainter oldDelegate) => false;
/// }
/// ```
///
/// See also:
///
/// * [Canvas], the class that a custom painter uses to paint.
/// * [CustomPaint], the widget that uses [CustomPainter], and whose sample
/// code shows how to use the above `Sky` class.
/// * [RadialGradient], whose sample code section shows a different take
/// on the sample code above.
abstract class CustomPainter extends Listenable {
/// Creates a custom painter.
///
......
......@@ -198,10 +198,32 @@ class BackdropFilter extends SingleChildRenderObjectWidget {
/// a child, they attempt to size themselves to the [size], which defaults to
/// [Size.zero].
///
/// ## Sample code
///
/// This example shows how the sample custom painter shown at [CustomPainter]
/// could be used in a [CustomPaint] widget to display a background to some
/// text.
///
/// ```dart
/// new CustomPaint(
/// painter: new Sky(),
/// child: new Center(
/// child: new Text(
/// 'Once upon a time...',
/// style: const TextStyle(
/// fontSize: 40.0,
/// fontWeight: FontWeight.w900,
/// color: const Color(0xFFFFFFFF),
/// ),
/// ),
/// ),
/// ),
/// ```
///
/// See also:
///
/// * [CustomPainter].
/// * [Canvas].
/// * [CustomPainter], the class to extend when creating custom painters.
/// * [Canvas], the class that a custom painter uses to paint.
class CustomPaint extends SingleChildRenderObjectWidget {
/// Creates a widget that delegates its painting.
const CustomPaint({ Key key, this.painter, this.foregroundPainter, this.size: Size.zero, Widget child })
......
......@@ -16,10 +16,34 @@ import 'image.dart';
/// not.
///
/// Commonly used with [BoxDecoration].
///
///
/// ## Sample code
///
/// This sample shows a radial gradient that draws a moon on a night sky:
///
/// ```dart
/// new DecoratedBox(
/// decoration: new BoxDecoration(
/// gradient: new RadialGradient(
/// center: const FractionalOffset(0.25, 0.3),
/// radius: 0.15,
/// colors: <Color>[
/// const Color(0xFFEEEEEE),
/// const Color(0xFF111133),
/// ],
/// stops: <double>[0.9, 1.0],
/// ),
/// ),
/// ),
/// ```
///
/// See also:
///
/// * [DecoratedBoxTransition], the version of this class that animates on the [decoration] property.
/// * [DecoratedBoxTransition], the version of this class that animates on the
/// [decoration] property.
/// * [Decoration], which you can extend to provide other effects with
/// [DecoratedBox].
/// * [CustomPaint], another way to draw custom effects from the widget layer.
class DecoratedBox extends SingleChildRenderObjectWidget {
/// Creates a widget that paints a [Decoration].
///
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment