
I added some tests for the bug that I fixed. I added docs for IconButton and AppBar. I added some new constructors for FractionalOffset.
3393 lines
122 KiB
Dart
3393 lines
122 KiB
Dart
// Copyright 2015 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' as ui show Image, ImageFilter;
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter/services.dart';
|
|
|
|
import 'debug.dart';
|
|
import 'framework.dart';
|
|
|
|
export 'package:flutter/animation.dart';
|
|
export 'package:flutter/foundation.dart' show TargetPlatform;
|
|
export 'package:flutter/painting.dart';
|
|
export 'package:flutter/rendering.dart' show
|
|
Axis,
|
|
BoxConstraints,
|
|
CrossAxisAlignment,
|
|
CustomClipper,
|
|
CustomPainter,
|
|
DecorationPosition,
|
|
FlexFit,
|
|
FlowDelegate,
|
|
FlowPaintingContext,
|
|
FractionalOffsetTween,
|
|
HitTestBehavior,
|
|
MainAxisAlignment,
|
|
MainAxisSize,
|
|
MultiChildLayoutDelegate,
|
|
Overflow,
|
|
PaintingContext,
|
|
PointerCancelEvent,
|
|
PointerCancelEventListener,
|
|
PointerDownEvent,
|
|
PointerDownEventListener,
|
|
PointerEvent,
|
|
PointerMoveEvent,
|
|
PointerMoveEventListener,
|
|
PointerUpEvent,
|
|
PointerUpEventListener,
|
|
RelativeRect,
|
|
ShaderCallback,
|
|
SingleChildLayoutDelegate,
|
|
StackFit,
|
|
TextOverflow,
|
|
ValueChanged,
|
|
ValueGetter,
|
|
WrapAlignment,
|
|
WrapCrossAlignment;
|
|
|
|
// PAINTING NODES
|
|
|
|
/// A widget that makes its child partially transparent.
|
|
///
|
|
/// This class paints its child into an intermediate buffer and then blends the
|
|
/// child back into the scene partially transparent.
|
|
///
|
|
/// For values of opacity other than 0.0 and 1.0, this class is relatively
|
|
/// expensive because it requires painting the child into an intermediate
|
|
/// buffer. For the value 0.0, the child is simply not painted at all. For the
|
|
/// value 1.0, the child is painted immediately without an intermediate buffer.
|
|
class Opacity extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that makes its child partially transparent.
|
|
///
|
|
/// The [opacity] argument must not be null and must be between 0.0 and 1.0
|
|
/// (inclusive).
|
|
const Opacity({
|
|
Key key,
|
|
@required this.opacity,
|
|
Widget child
|
|
}) : assert(opacity != null && opacity >= 0.0 && opacity <= 1.0),
|
|
super(key: key, child: child);
|
|
|
|
/// The fraction to scale the child's alpha value.
|
|
///
|
|
/// An opacity of 1.0 is fully opaque. An opacity of 0.0 is fully transparent
|
|
/// (i.e., invisible).
|
|
///
|
|
/// The opacity must not be null.
|
|
///
|
|
/// Values 1.0 and 0.0 are painted with a fast path. Other values
|
|
/// require painting the child into an intermediate buffer, which is
|
|
/// expensive.
|
|
final double opacity;
|
|
|
|
@override
|
|
RenderOpacity createRenderObject(BuildContext context) => new RenderOpacity(opacity: opacity);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderOpacity renderObject) {
|
|
renderObject.opacity = opacity;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('opacity: $opacity');
|
|
}
|
|
}
|
|
|
|
/// A widget that applies a mask generated by a [Shader] to its child.
|
|
///
|
|
/// For example, [ShaderMask] can be used to gradually fade out the edge
|
|
/// of a child by using a [new ui.Gradient.linear] mask.
|
|
class ShaderMask extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that applies a mask generated by a [Shader] to its child.
|
|
///
|
|
/// The [shaderCallback] and [blendMode] arguments must not be null.
|
|
const ShaderMask({
|
|
Key key,
|
|
@required this.shaderCallback,
|
|
this.blendMode: BlendMode.modulate,
|
|
Widget child
|
|
}) : assert(shaderCallback != null),
|
|
assert(blendMode != null),
|
|
super(key: key, child: child);
|
|
|
|
/// Called to creates the [Shader] that generates the mask.
|
|
///
|
|
/// The shader callback is called with the current size of the child so that
|
|
/// it can customize the shader to the size and location of the child.
|
|
final ShaderCallback shaderCallback;
|
|
|
|
/// The [BlendMode] to use when applying the shader to the child.
|
|
///
|
|
/// The default, [BlendMode.modulate], is useful for applying an alpha blend
|
|
/// to the child. Other blend modes can be used to create other effects.
|
|
final BlendMode blendMode;
|
|
|
|
@override
|
|
RenderShaderMask createRenderObject(BuildContext context) {
|
|
return new RenderShaderMask(
|
|
shaderCallback: shaderCallback,
|
|
blendMode: blendMode
|
|
);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderShaderMask renderObject) {
|
|
renderObject
|
|
..shaderCallback = shaderCallback
|
|
..blendMode = blendMode;
|
|
}
|
|
}
|
|
|
|
/// A widget that applies a filter to the existing painted content and then paints [child].
|
|
///
|
|
/// This effect is relatively expensive, especially if the filter is non-local,
|
|
/// such as a blur.
|
|
class BackdropFilter extends SingleChildRenderObjectWidget {
|
|
/// Creates a backdrop filter.
|
|
///
|
|
/// The [filter] argument must not be null.
|
|
const BackdropFilter({
|
|
Key key,
|
|
@required this.filter,
|
|
Widget child
|
|
}) : assert(filter != null),
|
|
super(key: key, child: child);
|
|
|
|
/// The image filter to apply to the existing painted content before painting the child.
|
|
///
|
|
/// For example, consider using [ImageFilter.blur] to create a backdrop
|
|
/// blur effect
|
|
final ui.ImageFilter filter;
|
|
|
|
@override
|
|
RenderBackdropFilter createRenderObject(BuildContext context) {
|
|
return new RenderBackdropFilter(filter: filter);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderBackdropFilter renderObject) {
|
|
renderObject.filter = filter;
|
|
}
|
|
}
|
|
|
|
/// A widget that provides a canvas on which to draw during the paint phase.
|
|
///
|
|
/// When asked to paint, [CustomPaint] first asks its [painter] to paint on the
|
|
/// current canvas, then it paints its child, and then, after painting its
|
|
/// child, it asks its [foregroundPainter] to paint. The coodinate system of the
|
|
/// canvas matches the coordinate system of the [CustomPaint] object. The
|
|
/// painters are expected to paint within a rectangle starting at the origin and
|
|
/// encompassing a region of the given size. (If the painters paint outside
|
|
/// those bounds, there might be insufficient memory allocated to rasterize the
|
|
/// painting commands and the resulting behavior is undefined.)
|
|
///
|
|
/// Painters are implemented by subclassing [CustomPainter].
|
|
///
|
|
/// Because custom paint calls its painters during paint, you cannot call
|
|
/// `setState` or `markNeedsLayout` during the callback (the layout for this
|
|
/// frame has already happened).
|
|
///
|
|
/// Custom painters normally size themselves to their child. If they do not have
|
|
/// 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], 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 })
|
|
: assert(size != null),
|
|
super(key: key, child: child);
|
|
|
|
/// The painter that paints before the children.
|
|
final CustomPainter painter;
|
|
|
|
/// The painter that paints after the children.
|
|
final CustomPainter foregroundPainter;
|
|
|
|
/// The size that this [CustomPaint] should aim for, given the layout
|
|
/// constraints, if there is no child.
|
|
///
|
|
/// Defaults to [Size.zero].
|
|
///
|
|
/// If there's a child, this is ignored, and the size of the child is used
|
|
/// instead.
|
|
final Size size;
|
|
|
|
@override
|
|
RenderCustomPaint createRenderObject(BuildContext context) => new RenderCustomPaint(
|
|
painter: painter,
|
|
foregroundPainter: foregroundPainter,
|
|
preferredSize: size,
|
|
);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderCustomPaint renderObject) {
|
|
renderObject
|
|
..painter = painter
|
|
..foregroundPainter = foregroundPainter
|
|
..preferredSize = size;
|
|
}
|
|
|
|
@override
|
|
void didUnmountRenderObject(RenderCustomPaint renderObject) {
|
|
renderObject
|
|
..painter = null
|
|
..foregroundPainter = null;
|
|
}
|
|
}
|
|
|
|
/// A widget that clips its child using a rectangle.
|
|
///
|
|
/// By default, [ClipRect] prevents its child from painting outside its
|
|
/// bounds, but the size and location of the clip rect can be customized using a
|
|
/// custom [clipper].
|
|
///
|
|
/// [ClipRect] is commonly used with these widgets, which commonly paint outside
|
|
/// their bounds.
|
|
///
|
|
/// * [CustomPaint]
|
|
/// * [CustomSingleChildLayout]
|
|
/// * [CustomMultiChildLayout]
|
|
/// * [Align] and [Center] (e.g., if [Align.widthFactor] or
|
|
/// [Align.heightFactor] is less than 1.0).
|
|
/// * [OverflowBox]
|
|
/// * [SizedOverflowBox]
|
|
///
|
|
/// ## Example
|
|
///
|
|
/// For example, use a clip to show the top half of an [Image], you can use a
|
|
/// [ClipRect] combined with an [Align]:
|
|
///
|
|
/// ```dart
|
|
/// new ClipRect(
|
|
/// child: new Align(
|
|
/// alignment: FractionalOffset.topCenter,
|
|
/// heightFactor: 0.5,
|
|
/// child: new Image(...),
|
|
/// ),
|
|
/// ),
|
|
/// ```
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [CustomClipper], for information about creating custom clips.
|
|
/// * [ClipRRect], for a clip with rounded corners.
|
|
/// * [ClipOval], for an elliptical clip.
|
|
/// * [ClipPath], for an arbitrarily shaped clip.
|
|
class ClipRect extends SingleChildRenderObjectWidget {
|
|
/// Creates a rectangular clip.
|
|
///
|
|
/// If [clipper] is null, the clip will match the layout size and position of
|
|
/// the child.
|
|
const ClipRect({ Key key, this.clipper, Widget child }) : super(key: key, child: child);
|
|
|
|
/// If non-null, determines which clip to use.
|
|
final CustomClipper<Rect> clipper;
|
|
|
|
@override
|
|
RenderClipRect createRenderObject(BuildContext context) => new RenderClipRect(clipper: clipper);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderClipRect renderObject) {
|
|
renderObject.clipper = clipper;
|
|
}
|
|
|
|
@override
|
|
void didUnmountRenderObject(RenderClipRect renderObject) {
|
|
renderObject.clipper = null;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
if (clipper != null)
|
|
description.add('clipper: $clipper');
|
|
}
|
|
}
|
|
|
|
/// A widget that clips its child using a rounded rectangle.
|
|
///
|
|
/// By default, [ClipRRect] uses its own bounds as the base rectangle for the
|
|
/// clip, but the size and location of the clip can be customized using a custom
|
|
/// [clipper].
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [CustomClipper], for information about creating custom clips.
|
|
/// * [ClipRect], for more efficient clips without rounded corners.
|
|
/// * [ClipOval], for an elliptical clip.
|
|
/// * [ClipPath], for an arbitrarily shaped clip.
|
|
class ClipRRect extends SingleChildRenderObjectWidget {
|
|
/// Creates a rounded-rectangular clip.
|
|
///
|
|
/// The [borderRadius] defaults to [BorderRadius.zero], i.e. a rectangle with
|
|
/// right-angled corners.
|
|
///
|
|
/// If [clipper] is non-null, then [borderRadius] is ignored.
|
|
const ClipRRect({
|
|
Key key,
|
|
this.borderRadius,
|
|
this.clipper,
|
|
Widget child,
|
|
}) : assert(borderRadius != null || clipper != null),
|
|
super(key: key, child: child);
|
|
|
|
/// The border radius of the rounded corners.
|
|
///
|
|
/// Values are clamped so that horizontal and vertical radii sums do not
|
|
/// exceed width/height.
|
|
///
|
|
/// This value is ignored if [clipper] is non-null.
|
|
final BorderRadius borderRadius;
|
|
|
|
/// If non-null, determines which clip to use.
|
|
final CustomClipper<RRect> clipper;
|
|
|
|
@override
|
|
RenderClipRRect createRenderObject(BuildContext context) => new RenderClipRRect(borderRadius: borderRadius, clipper: clipper);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderClipRRect renderObject) {
|
|
renderObject
|
|
..borderRadius = borderRadius
|
|
..clipper = clipper;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
if (borderRadius != null)
|
|
description.add('$borderRadius');
|
|
if (clipper != null)
|
|
description.add('clipper: $clipper');
|
|
}
|
|
}
|
|
|
|
/// A widget that clips its child using an oval.
|
|
///
|
|
/// By default, inscribes an axis-aligned oval into its layout dimensions and
|
|
/// prevents its child from painting outside that oval, but the size and
|
|
/// location of the clip oval can be customized using a custom [clipper].
|
|
/// See also:
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [CustomClipper], for information about creating custom clips.
|
|
/// * [ClipRect], for more efficient clips without rounded corners.
|
|
/// * [ClipRRect], for a clip with rounded corners.
|
|
/// * [ClipPath], for an arbitrarily shaped clip.
|
|
class ClipOval extends SingleChildRenderObjectWidget {
|
|
/// Creates an oval-shaped clip.
|
|
///
|
|
/// If [clipper] is null, the oval will be inscribed into the layout size and
|
|
/// position of the child.
|
|
const ClipOval({ Key key, this.clipper, Widget child }) : super(key: key, child: child);
|
|
|
|
/// If non-null, determines which clip to use.
|
|
///
|
|
/// The delegate returns a rectangle that describes the axis-aligned
|
|
/// bounding box of the oval. The oval's axes will themselves also
|
|
/// be axis-aligned.
|
|
///
|
|
/// If the [clipper] delegate is null, then the oval uses the
|
|
/// widget's bounding box (the layout dimensions of the render
|
|
/// object) instead.
|
|
final CustomClipper<Rect> clipper;
|
|
|
|
@override
|
|
RenderClipOval createRenderObject(BuildContext context) => new RenderClipOval(clipper: clipper);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderClipOval renderObject) {
|
|
renderObject.clipper = clipper;
|
|
}
|
|
|
|
@override
|
|
void didUnmountRenderObject(RenderClipOval renderObject) {
|
|
renderObject.clipper = null;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
if (clipper != null)
|
|
description.add('clipper: $clipper');
|
|
}
|
|
}
|
|
|
|
/// A widget that clips its child using a path.
|
|
///
|
|
/// Calls a callback on a delegate whenever the widget is to be
|
|
/// painted. The callback returns a path and the widget prevents the
|
|
/// child from painting outside the path.
|
|
///
|
|
/// Clipping to a path is expensive. Certain shapes have more
|
|
/// optimized widgets:
|
|
///
|
|
/// * To clip to a rectangle, consider [ClipRect].
|
|
/// * To clip to an oval or circle, consider [ClipOval].
|
|
/// * To clip to a rounded rectangle, consider [ClipRRect].
|
|
class ClipPath extends SingleChildRenderObjectWidget {
|
|
/// Creates a path clip.
|
|
///
|
|
/// If [clipper] is null, the clip will be a rectangle that matches the layout
|
|
/// size and location of the child. However, rather than use this default,
|
|
/// consider using a [ClipRect], which can achieve the same effect more
|
|
/// efficiently.
|
|
const ClipPath({ Key key, this.clipper, Widget child }) : super(key: key, child: child);
|
|
|
|
/// If non-null, determines which clip to use.
|
|
///
|
|
/// The default clip, which is used if this property is null, is the
|
|
/// bounding box rectangle of the widget. [ClipRect] is a more
|
|
/// efficient way of obtaining that effect.
|
|
final CustomClipper<Path> clipper;
|
|
|
|
@override
|
|
RenderClipPath createRenderObject(BuildContext context) => new RenderClipPath(clipper: clipper);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderClipPath renderObject) {
|
|
renderObject.clipper = clipper;
|
|
}
|
|
|
|
@override
|
|
void didUnmountRenderObject(RenderClipPath renderObject) {
|
|
renderObject.clipper = null;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
if (clipper != null)
|
|
description.add('clipper: $clipper');
|
|
}
|
|
}
|
|
|
|
/// A widget representing a physical layer that clips its children to a shape.
|
|
///
|
|
/// Physical layers cast shadows based on an [elevation] which is nominally in
|
|
/// logical pixels, coming vertically out of the rendering surface.
|
|
class PhysicalModel extends SingleChildRenderObjectWidget {
|
|
/// Creates a physical model with a rounded-rectangular clip.
|
|
///
|
|
/// The [color] is required; physical things have a color.
|
|
///
|
|
/// The [shape], [elevation], and [color] must not be null.
|
|
const PhysicalModel({
|
|
Key key,
|
|
this.shape: BoxShape.rectangle,
|
|
this.borderRadius,
|
|
this.elevation: 0.0,
|
|
@required this.color,
|
|
Widget child,
|
|
}) : assert(shape != null),
|
|
assert(elevation != null),
|
|
assert(color != null),
|
|
super(key: key, child: child);
|
|
|
|
/// The type of shape.
|
|
final BoxShape shape;
|
|
|
|
/// The border radius of the rounded corners.
|
|
///
|
|
/// Values are clamped so that horizontal and vertical radii sums do not
|
|
/// exceed width/height.
|
|
///
|
|
/// This is ignored if the [shape] is not [BoxShape.rectangle].
|
|
final BorderRadius borderRadius;
|
|
|
|
/// The z-coordinate at which to place this physical object.
|
|
final double elevation;
|
|
|
|
/// The background color.
|
|
final Color color;
|
|
|
|
@override
|
|
RenderPhysicalModel createRenderObject(BuildContext context) => new RenderPhysicalModel(shape: shape, borderRadius: borderRadius, elevation: elevation, color: color);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderPhysicalModel renderObject) {
|
|
renderObject
|
|
..shape = shape
|
|
..borderRadius = borderRadius
|
|
..elevation = elevation
|
|
..color = color;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('shape: $shape');
|
|
description.add('borderRadius: $borderRadius');
|
|
description.add('elevation: ${elevation.toStringAsFixed(1)}');
|
|
description.add('color: $color');
|
|
}
|
|
}
|
|
|
|
// POSITIONING AND SIZING NODES
|
|
|
|
/// A widget that applies a transformation before painting its child.
|
|
class Transform extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that transforms its child.
|
|
///
|
|
/// The [transform] argument must not be null.
|
|
const Transform({
|
|
Key key,
|
|
@required this.transform,
|
|
this.origin,
|
|
this.alignment,
|
|
this.transformHitTests: true,
|
|
Widget child
|
|
}) : assert(transform != null),
|
|
super(key: key, child: child);
|
|
|
|
/// The matrix to transform the child by during painting.
|
|
final Matrix4 transform;
|
|
|
|
/// The origin of the coordinate system (relative to the upper left corder of
|
|
/// this render object) in which to apply the matrix.
|
|
///
|
|
/// Setting an origin is equivalent to conjugating the transform matrix by a
|
|
/// translation. This property is provided just for convenience.
|
|
final Offset origin;
|
|
|
|
/// The alignment of the origin, relative to the size of the box.
|
|
///
|
|
/// This is equivalent to setting an origin based on the size of the box.
|
|
/// If it is specified at the same time as an offset, both are applied.
|
|
final FractionalOffset alignment;
|
|
|
|
/// Whether to apply the translation when performing hit tests.
|
|
final bool transformHitTests;
|
|
|
|
@override
|
|
RenderTransform createRenderObject(BuildContext context) => new RenderTransform(
|
|
transform: transform,
|
|
origin: origin,
|
|
alignment: alignment,
|
|
transformHitTests: transformHitTests
|
|
);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderTransform renderObject) {
|
|
renderObject
|
|
..transform = transform
|
|
..origin = origin
|
|
..alignment = alignment
|
|
..transformHitTests = transformHitTests;
|
|
}
|
|
}
|
|
|
|
/// Scales and positions its child within itself according to [fit].
|
|
class FittedBox extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that scales and positions its child within itself according to [fit].
|
|
///
|
|
/// The [fit] and [alignment] arguments must not be null.
|
|
const FittedBox({
|
|
Key key,
|
|
this.fit: BoxFit.contain,
|
|
this.alignment: FractionalOffset.center,
|
|
Widget child
|
|
}) : assert(fit != null),
|
|
assert(alignment != null),
|
|
super(key: key, child: child);
|
|
|
|
/// How to inscribe the child into the space allocated during layout.
|
|
final BoxFit fit;
|
|
|
|
/// How to align the child within its parent's bounds.
|
|
///
|
|
/// An alignment of (0.0, 0.0) aligns the child to the top-left corner of its
|
|
/// parent's bounds. An alignment of (1.0, 0.5) aligns the child to the middle
|
|
/// of the right edge of its parent's bounds.
|
|
final FractionalOffset alignment;
|
|
|
|
@override
|
|
RenderFittedBox createRenderObject(BuildContext context) => new RenderFittedBox(fit: fit, alignment: alignment);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderFittedBox renderObject) {
|
|
renderObject
|
|
..fit = fit
|
|
..alignment = alignment;
|
|
}
|
|
}
|
|
|
|
/// A widget that applies a translation expressed as a fraction of the box's
|
|
/// size before painting its child.
|
|
class FractionalTranslation extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that translates its child's painting.
|
|
///
|
|
/// The [translation] argument must not be null.
|
|
const FractionalTranslation({
|
|
Key key,
|
|
@required this.translation,
|
|
this.transformHitTests: true,
|
|
Widget child
|
|
}) : assert(translation != null),
|
|
super(key: key, child: child);
|
|
|
|
/// The offset by which to translate the child, as a multiple of its size.
|
|
final FractionalOffset translation;
|
|
|
|
/// Whether to apply the translation when performing hit tests.
|
|
final bool transformHitTests;
|
|
|
|
@override
|
|
RenderFractionalTranslation createRenderObject(BuildContext context) => new RenderFractionalTranslation(translation: translation, transformHitTests: transformHitTests);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderFractionalTranslation renderObject) {
|
|
renderObject
|
|
..translation = translation
|
|
..transformHitTests = transformHitTests;
|
|
}
|
|
}
|
|
|
|
/// A widget that rotates its child by a integral number of quarter turns.
|
|
///
|
|
/// Unlike [Transform], which applies a transform just prior to painting,
|
|
/// this object applies its rotation prior to layout, which means the entire
|
|
/// rotated box consumes only as much space as required by the rotated child.
|
|
class RotatedBox extends SingleChildRenderObjectWidget {
|
|
/// A widget that rotates its child.
|
|
///
|
|
/// The [quarterTurns] argument must not be null.
|
|
const RotatedBox({
|
|
Key key,
|
|
@required this.quarterTurns,
|
|
Widget child
|
|
}) : assert(quarterTurns != null),
|
|
super(key: key, child: child);
|
|
|
|
/// The number of clockwise quarter turns the child should be rotated.
|
|
final int quarterTurns;
|
|
|
|
@override
|
|
RenderRotatedBox createRenderObject(BuildContext context) => new RenderRotatedBox(quarterTurns: quarterTurns);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderRotatedBox renderObject) {
|
|
renderObject.quarterTurns = quarterTurns;
|
|
}
|
|
}
|
|
|
|
/// A widget that insets its child by the given padding.
|
|
///
|
|
/// When passing layout constraints to its child, padding shrinks the
|
|
/// constraints by the given padding, causing the child to layout at a smaller
|
|
/// size. Padding then sizes itself to its child's size, inflated by the
|
|
/// padding, effectively creating empty space around the child.
|
|
///
|
|
/// ## Design discussion
|
|
///
|
|
/// ### Why use a [Padding] widget rather than a [Container] with a [Container.padding] property?
|
|
///
|
|
/// There isn't really any difference between the two. If you supply a
|
|
/// [Container.padding] argument, [Container] simply builds a [Padding] widget
|
|
/// for you.
|
|
///
|
|
/// [Container] doesn't implement its properties directly. Instead, [Container]
|
|
/// combines a number of simpler widgets together into a convenient package. For
|
|
/// example, the [Container.padding] property causes the container to build a
|
|
/// [Padding] widget and the [Container.decoration] property causes the
|
|
/// container to build a [DecoratedBox] widget. If you find [Container]
|
|
/// convenient, feel free to use it. If not, feel free to build these simpler
|
|
/// widgets in whatever combination meets your needs.
|
|
///
|
|
/// In fact, the majority of widgets in Flutter are simply combinations of other
|
|
/// simpler widgets. Composition, rather than inheritance, is the primary
|
|
/// mechansim for building up widgets.
|
|
class Padding extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that insets its child.
|
|
///
|
|
/// The [padding] argument must not be null.
|
|
const Padding({
|
|
Key key,
|
|
@required this.padding,
|
|
Widget child
|
|
}) : assert(padding != null),
|
|
super(key: key, child: child);
|
|
|
|
/// The amount of space by which to inset the child.
|
|
final EdgeInsets padding;
|
|
|
|
@override
|
|
RenderPadding createRenderObject(BuildContext context) => new RenderPadding(padding: padding);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderPadding renderObject) {
|
|
renderObject.padding = padding;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('padding: $padding');
|
|
}
|
|
}
|
|
|
|
/// A widget that aligns its child within itself and optionally sizes itself
|
|
/// based on the child's size.
|
|
///
|
|
/// For example, to align a box at the bottom right, you would pass this box a
|
|
/// tight constraint that is bigger than the child's natural size,
|
|
/// with an alignment of [FractionalOffset.bottomRight].
|
|
///
|
|
/// This widget will be as big as possible if its dimensions are constrained and
|
|
/// [widthFactor] and [heightFactor] are null. If a dimension is unconstrained
|
|
/// and the corresponding size factor is null then the widget will match its
|
|
/// child's size in that dimension. If a size factor is non-null then the
|
|
/// corresponding dimension of this widget will be the product of the child's
|
|
/// dimension and the size factor. For example if widthFactor is 2.0 then
|
|
/// the width of this widget will always be twice its child's width.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [CustomSingleChildLayout], which uses a delegate to control the layout of
|
|
/// a single child.
|
|
/// * [Center], which is the same as [Align] but with the [alignment] always
|
|
/// set to [FractionalOffset.center].
|
|
/// * [FractionallySizedBox], which sizes its child based on a fraction of its own
|
|
/// size and positions the child according to a [FractionalOffset] value.
|
|
class Align extends SingleChildRenderObjectWidget {
|
|
/// Creates an alignment widget.
|
|
///
|
|
/// The alignment defaults to [FractionalOffset.center].
|
|
const Align({
|
|
Key key,
|
|
this.alignment: FractionalOffset.center,
|
|
this.widthFactor,
|
|
this.heightFactor,
|
|
Widget child
|
|
}) : assert(alignment != null),
|
|
assert(widthFactor == null || widthFactor >= 0.0),
|
|
assert(heightFactor == null || heightFactor >= 0.0),
|
|
super(key: key, child: child);
|
|
|
|
/// How to align the child.
|
|
///
|
|
/// The x and y values of the alignment control the horizontal and vertical
|
|
/// alignment, respectively. An x value of 0.0 means that the left edge of
|
|
/// the child is aligned with the left edge of the parent whereas an x value
|
|
/// of 1.0 means that the right edge of the child is aligned with the right
|
|
/// edge of the parent. Other values interpolate (and extrapolate) linearly.
|
|
/// For example, a value of 0.5 means that the center of the child is aligned
|
|
/// with the center of the parent.
|
|
final FractionalOffset alignment;
|
|
|
|
/// If non-null, sets its width to the child's width multipled by this factor.
|
|
///
|
|
/// Can be both greater and less than 1.0 but must be positive.
|
|
final double widthFactor;
|
|
|
|
/// If non-null, sets its height to the child's height multipled by this factor.
|
|
///
|
|
/// Can be both greater and less than 1.0 but must be positive.
|
|
final double heightFactor;
|
|
|
|
@override
|
|
RenderPositionedBox createRenderObject(BuildContext context) => new RenderPositionedBox(alignment: alignment, widthFactor: widthFactor, heightFactor: heightFactor);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderPositionedBox renderObject) {
|
|
renderObject
|
|
..alignment = alignment
|
|
..widthFactor = widthFactor
|
|
..heightFactor = heightFactor;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('alignment: $alignment');
|
|
if (widthFactor != null)
|
|
description.add('widthFactor: $widthFactor');
|
|
if (heightFactor != null)
|
|
description.add('heightFactor: $heightFactor');
|
|
}
|
|
}
|
|
|
|
/// A widget that centers its child within itself.
|
|
///
|
|
/// This widget will be as big as possible if its dimensions are constrained and
|
|
/// [widthFactor] and [heightFactor] are null. If a dimension is unconstrained
|
|
/// and the corresponding size factor is null then the widget will match its
|
|
/// child's size in that dimension. If a size factor is non-null then the
|
|
/// corresponding dimension of this widget will be the product of the child's
|
|
/// dimension and the size factor. For example if widthFactor is 2.0 then
|
|
/// the width of this widget will always be twice its child's width.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [Align], which lets you arbitrarily position a child within itself,
|
|
/// rather than just centering it.
|
|
class Center extends Align {
|
|
/// Creates a widget that centers its child.
|
|
const Center({ Key key, double widthFactor, double heightFactor, Widget child })
|
|
: super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child);
|
|
}
|
|
|
|
/// A widget that defers the layout of its single child to a delegate.
|
|
///
|
|
/// The delegate can determine the layout constraints for the child and can
|
|
/// decide where to position the child. The delegate can also determine the size
|
|
/// of the parent, but the size of the parent cannot depend on the size of the
|
|
/// child.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [SingleChildLayoutDelegate], which controls the layout of the child.
|
|
/// * [Align], which sizes itself based on its child's size and positions
|
|
/// the child according to a [FractionalOffset] value.
|
|
/// * [FractionallySizedBox], which sizes its child based on a fraction of its own
|
|
/// size and positions the child according to a [FractionalOffset] value.
|
|
/// * [CustomMultiChildLayout], which uses a delegate to position multiple
|
|
/// children.
|
|
class CustomSingleChildLayout extends SingleChildRenderObjectWidget {
|
|
/// Creates a custom single child layout.
|
|
///
|
|
/// The [delegate] argument must not be null.
|
|
const CustomSingleChildLayout({
|
|
Key key,
|
|
@required this.delegate,
|
|
Widget child
|
|
}) : assert(delegate != null),
|
|
super(key: key, child: child);
|
|
|
|
/// The delegate that controls the layout of the child.
|
|
final SingleChildLayoutDelegate delegate;
|
|
|
|
@override
|
|
RenderCustomSingleChildLayoutBox createRenderObject(BuildContext context) => new RenderCustomSingleChildLayoutBox(delegate: delegate);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderCustomSingleChildLayoutBox renderObject) {
|
|
renderObject.delegate = delegate;
|
|
}
|
|
}
|
|
|
|
/// Meta data for identifying children in a [CustomMultiChildLayout].
|
|
///
|
|
/// The [MultiChildLayoutDelegate] hasChild, layoutChild, and positionChild
|
|
/// methods use these identifiers.
|
|
class LayoutId extends ParentDataWidget<CustomMultiChildLayout> {
|
|
/// Marks a child with a layout identifier.
|
|
///
|
|
/// Both the child and the id arguments must not be null.
|
|
LayoutId({
|
|
Key key,
|
|
@required this.id,
|
|
@required Widget child
|
|
}) : assert(child != null),
|
|
assert(id != null),
|
|
super(key: key ?? new ValueKey<Object>(id), child: child);
|
|
|
|
/// An object representing the identity of this child.
|
|
final Object id;
|
|
|
|
@override
|
|
void applyParentData(RenderObject renderObject) {
|
|
assert(renderObject.parentData is MultiChildLayoutParentData);
|
|
final MultiChildLayoutParentData parentData = renderObject.parentData;
|
|
if (parentData.id != id) {
|
|
parentData.id = id;
|
|
final AbstractNode targetParent = renderObject.parent;
|
|
if (targetParent is RenderObject)
|
|
targetParent.markNeedsLayout();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('id: $id');
|
|
}
|
|
}
|
|
|
|
/// A widget that uses a delegate to size and position multiple children.
|
|
///
|
|
/// The delegate can determine the layout constraints for each child and can
|
|
/// decide where to position each child. The delegate can also determine the
|
|
/// size of the parent, but the size of the parent cannot depend on the sizes of
|
|
/// the children.
|
|
///
|
|
/// [CustomMultiChildLayout] is appropriate when there are complex relationships
|
|
/// between the size and positioning of a multiple widgets. To control the
|
|
/// layout of a single child, [CustomSingleChildLayout] is more appropriate. For
|
|
/// simple cases, such as aligning a widget to one or another edge, the [Stack]
|
|
/// widget is more appropriate.
|
|
///
|
|
/// Each child must be wrapped in a [LayoutId] widget to identify the widget for
|
|
/// the delegate.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [MultiChildLayoutDelegate], for details about how to control the layout of
|
|
/// the children.
|
|
/// * [CustomSingleChildLayout], which uses a delegate to control the layout of
|
|
/// a single child.
|
|
/// * [Stack], which arranges children relative to the edges of the container.
|
|
/// * [Flow], which provides paint-time control of its children using transform
|
|
/// matrices.
|
|
class CustomMultiChildLayout extends MultiChildRenderObjectWidget {
|
|
/// Creates a custom multi-child layout.
|
|
///
|
|
/// The [delegate] argument must not be null.
|
|
CustomMultiChildLayout({
|
|
Key key,
|
|
@required this.delegate,
|
|
List<Widget> children: const <Widget>[],
|
|
}) : assert(delegate != null),
|
|
super(key: key, children: children);
|
|
|
|
/// The delegate that controls the layout of the children.
|
|
final MultiChildLayoutDelegate delegate;
|
|
|
|
@override
|
|
RenderCustomMultiChildLayoutBox createRenderObject(BuildContext context) {
|
|
return new RenderCustomMultiChildLayoutBox(delegate: delegate);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderCustomMultiChildLayoutBox renderObject) {
|
|
renderObject.delegate = delegate;
|
|
}
|
|
}
|
|
|
|
/// A box with a specified size.
|
|
///
|
|
/// If given a child, this widget forces its child to have a specific width
|
|
/// and/or height (assuming values are permitted by this widget's parent). If
|
|
/// either the width or height is null, this widget will size itself to match
|
|
/// the child's size in that dimension.
|
|
///
|
|
/// If not given a child, this widget will size itself to the given width and
|
|
/// height, treating nulls as zero.
|
|
///
|
|
/// The [new SizedBox.expand] constructor can be used to make a [SizedBox] that
|
|
/// sizes itself to fit the parent. It is equivalent to setting [width] and
|
|
/// [height] to [double.INFINITY].
|
|
class SizedBox extends SingleChildRenderObjectWidget {
|
|
/// Creates a fixed size box. The [width] and [height] parameters can be null
|
|
/// to indicate that the size of the box should not be constrained in
|
|
/// the corresponding dimension.
|
|
const SizedBox({ Key key, this.width, this.height, Widget child })
|
|
: super(key: key, child: child);
|
|
|
|
/// Creates a box that will become as large as its parent allows.
|
|
const SizedBox.expand({ Key key, Widget child })
|
|
: width = double.INFINITY,
|
|
height = double.INFINITY,
|
|
super(key: key, child: child);
|
|
|
|
/// Creates a box with the specified size.
|
|
SizedBox.fromSize({ Key key, Widget child, Size size })
|
|
: width = size?.width,
|
|
height = size?.height,
|
|
super(key: key, child: child);
|
|
|
|
/// If non-null, requires the child to have exactly this width.
|
|
final double width;
|
|
|
|
/// If non-null, requires the child to have exactly this height.
|
|
final double height;
|
|
|
|
@override
|
|
RenderConstrainedBox createRenderObject(BuildContext context) => new RenderConstrainedBox(
|
|
additionalConstraints: _additionalConstraints
|
|
);
|
|
|
|
BoxConstraints get _additionalConstraints {
|
|
return new BoxConstraints.tightFor(width: width, height: height);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderConstrainedBox renderObject) {
|
|
renderObject.additionalConstraints = _additionalConstraints;
|
|
}
|
|
|
|
@override
|
|
String toStringShort() {
|
|
final String type = (width == double.INFINITY && height == double.INFINITY) ?
|
|
'$runtimeType.expand' : '$runtimeType';
|
|
return key == null ? '$type' : '$type-$key';
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
if (width != double.INFINITY || height != double.INFINITY) {
|
|
if (width != null)
|
|
description.add('width: $width');
|
|
if (height != null)
|
|
description.add('height: $height');
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A widget that imposes additional constraints on its child.
|
|
///
|
|
/// For example, if you wanted [child] to have a minimum height of 50.0 logical
|
|
/// pixels, you could use `const BoxConstraints(minHeight: 50.0)`` as the
|
|
/// [constraints].
|
|
class ConstrainedBox extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that imposes additional constraints on its child.
|
|
///
|
|
/// The [constraints] argument must not be null.
|
|
ConstrainedBox({
|
|
Key key,
|
|
@required this.constraints,
|
|
Widget child
|
|
}) : assert(constraints != null),
|
|
super(key: key, child: child) {
|
|
assert(constraints.debugAssertIsValid());
|
|
}
|
|
|
|
/// The additional constraints to impose on the child.
|
|
final BoxConstraints constraints;
|
|
|
|
@override
|
|
RenderConstrainedBox createRenderObject(BuildContext context) => new RenderConstrainedBox(additionalConstraints: constraints);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderConstrainedBox renderObject) {
|
|
renderObject.additionalConstraints = constraints;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('$constraints');
|
|
}
|
|
}
|
|
|
|
/// A widget that sizes its child to a fraction of the total available space.
|
|
/// For more details about the layout algorithm, see [RenderFractionallySizedOverflowBox].
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [Align] (which sizes itself based on its child's size and positions
|
|
/// the child according to a [FractionalOffset] value)
|
|
/// * [OverflowBox]
|
|
class FractionallySizedBox extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that sizes its child to a fraction of the total available space.
|
|
///
|
|
/// If non-null, the [widthFactor] and [heightFactor] arguments must be
|
|
/// non-negative.
|
|
const FractionallySizedBox({
|
|
Key key,
|
|
this.alignment: FractionalOffset.center,
|
|
this.widthFactor,
|
|
this.heightFactor,
|
|
Widget child
|
|
}) : assert(alignment != null),
|
|
assert(widthFactor == null || widthFactor >= 0.0),
|
|
assert(heightFactor == null || heightFactor >= 0.0),
|
|
super(key: key, child: child);
|
|
|
|
/// If non-null, the fraction of the incoming width given to the child.
|
|
///
|
|
/// If non-null, the child is given a tight width constraint that is the max
|
|
/// incoming width constraint multipled by this factor.
|
|
///
|
|
/// If null, the incoming width constraints are passed to the child
|
|
/// unmodified.
|
|
final double widthFactor;
|
|
|
|
/// If non-null, the fraction of the incoming height given to the child.
|
|
///
|
|
/// If non-null, the child is given a tight height constraint that is the max
|
|
/// incoming height constraint multipled by this factor.
|
|
///
|
|
/// If null, the incoming height constraints are passed to the child
|
|
/// unmodified.
|
|
final double heightFactor;
|
|
|
|
/// How to align the child.
|
|
///
|
|
/// The x and y values of the alignment control the horizontal and vertical
|
|
/// alignment, respectively. An x value of 0.0 means that the left edge of
|
|
/// the child is aligned with the left edge of the parent whereas an x value
|
|
/// of 1.0 means that the right edge of the child is aligned with the right
|
|
/// edge of the parent. Other values interpolate (and extrapolate) linearly.
|
|
/// For example, a value of 0.5 means that the center of the child is aligned
|
|
/// with the center of the parent.
|
|
final FractionalOffset alignment;
|
|
|
|
@override
|
|
RenderFractionallySizedOverflowBox createRenderObject(BuildContext context) => new RenderFractionallySizedOverflowBox(
|
|
alignment: alignment,
|
|
widthFactor: widthFactor,
|
|
heightFactor: heightFactor
|
|
);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderFractionallySizedOverflowBox renderObject) {
|
|
renderObject
|
|
..alignment = alignment
|
|
..widthFactor = widthFactor
|
|
..heightFactor = heightFactor;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('alignment: $alignment');
|
|
if (widthFactor != null)
|
|
description.add('widthFactor: $widthFactor');
|
|
if (heightFactor != null)
|
|
description.add('heightFactor: $heightFactor');
|
|
}
|
|
}
|
|
|
|
/// A box that limits its size only when it's unconstrained.
|
|
///
|
|
/// If this widget's maximum width is unconstrained then its child's width is
|
|
/// limited to maxWidth. Similarly, if this widget's maximum height is unconstrained
|
|
/// then its child's height is limited to to maxHeight.
|
|
class LimitedBox extends SingleChildRenderObjectWidget {
|
|
/// Creates a box that limits its size only when it's unconstrained.
|
|
///
|
|
/// The [maxWidth] and [maxHeight] arguments must not be null and must not be
|
|
/// negative.
|
|
const LimitedBox({
|
|
Key key,
|
|
this.maxWidth: double.INFINITY,
|
|
this.maxHeight: double.INFINITY,
|
|
Widget child
|
|
}) : assert(maxWidth != null && maxWidth >= 0.0),
|
|
assert(maxHeight != null && maxHeight >= 0.0),
|
|
super(key: key, child: child);
|
|
|
|
/// The maximum width limit to apply in the absence of a maxWidth constraint.
|
|
final double maxWidth;
|
|
|
|
/// The maximum height limit to apply in the absence of a maxHeight constraint.
|
|
final double maxHeight;
|
|
|
|
@override
|
|
RenderLimitedBox createRenderObject(BuildContext context) => new RenderLimitedBox(
|
|
maxWidth: maxWidth,
|
|
maxHeight: maxHeight
|
|
);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderLimitedBox renderObject) {
|
|
renderObject
|
|
..maxWidth = maxWidth
|
|
..maxHeight = maxHeight;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
if (maxWidth != double.INFINITY)
|
|
description.add('maxWidth: $maxWidth');
|
|
if (maxHeight != double.INFINITY)
|
|
description.add('maxHeight: $maxHeight');
|
|
}
|
|
}
|
|
|
|
/// A widget that imposes different constraints on its child than it gets
|
|
/// from its parent, possibly allowing the child to overflow the parent.
|
|
///
|
|
/// See [RenderConstrainedOverflowBox] for details.
|
|
class OverflowBox extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that lets its child overflow itself.
|
|
const OverflowBox({
|
|
Key key,
|
|
this.alignment: FractionalOffset.center,
|
|
this.minWidth,
|
|
this.maxWidth,
|
|
this.minHeight,
|
|
this.maxHeight,
|
|
Widget child
|
|
}) : super(key: key, child: child);
|
|
|
|
/// How to align the child.
|
|
///
|
|
/// The x and y values of the alignment control the horizontal and vertical
|
|
/// alignment, respectively. An x value of 0.0 means that the left edge of
|
|
/// the child is aligned with the left edge of the parent whereas an x value
|
|
/// of 1.0 means that the right edge of the child is aligned with the right
|
|
/// edge of the parent. Other values interpolate (and extrapolate) linearly.
|
|
/// For example, a value of 0.5 means that the center of the child is aligned
|
|
/// with the center of the parent.
|
|
final FractionalOffset alignment;
|
|
|
|
/// The minimum width constraint to give the child. Set this to null (the
|
|
/// default) to use the constraint from the parent instead.
|
|
final double minWidth;
|
|
|
|
/// The maximum width constraint to give the child. Set this to null (the
|
|
/// default) to use the constraint from the parent instead.
|
|
final double maxWidth;
|
|
|
|
/// The minimum height constraint to give the child. Set this to null (the
|
|
/// default) to use the constraint from the parent instead.
|
|
final double minHeight;
|
|
|
|
/// The maximum height constraint to give the child. Set this to null (the
|
|
/// default) to use the constraint from the parent instead.
|
|
final double maxHeight;
|
|
|
|
@override
|
|
RenderConstrainedOverflowBox createRenderObject(BuildContext context) => new RenderConstrainedOverflowBox(
|
|
alignment: alignment,
|
|
minWidth: minWidth,
|
|
maxWidth: maxWidth,
|
|
minHeight: minHeight,
|
|
maxHeight: maxHeight
|
|
);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderConstrainedOverflowBox renderObject) {
|
|
renderObject
|
|
..alignment = alignment
|
|
..minWidth = minWidth
|
|
..maxWidth = maxWidth
|
|
..minHeight = minHeight
|
|
..maxHeight = maxHeight;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('alignment: $alignment');
|
|
if (minWidth != null)
|
|
description.add('minWidth: $minWidth');
|
|
if (maxWidth != null)
|
|
description.add('maxWidth: $maxWidth');
|
|
if (minHeight != null)
|
|
description.add('minHeight: $minHeight');
|
|
if (maxHeight != null)
|
|
description.add('maxHeight: $maxHeight');
|
|
}
|
|
}
|
|
|
|
/// A widget that is a specific size but passes its original constraints
|
|
/// through to its child, which will probably overflow.
|
|
class SizedOverflowBox extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget of a given size that lets its child overflow.
|
|
///
|
|
/// The [size] argument must not be null.
|
|
const SizedOverflowBox({
|
|
Key key,
|
|
@required this.size,
|
|
this.alignment: FractionalOffset.center,
|
|
Widget child
|
|
}) : assert(size != null),
|
|
assert(alignment != null),
|
|
super(key: key, child: child);
|
|
|
|
/// How to align the child.
|
|
///
|
|
/// The x and y values of the alignment control the horizontal and vertical
|
|
/// alignment, respectively. An x value of 0.0 means that the left edge of
|
|
/// the child is aligned with the left edge of the parent whereas an x value
|
|
/// of 1.0 means that the right edge of the child is aligned with the right
|
|
/// edge of the parent. Other values interpolate (and extrapolate) linearly.
|
|
/// For example, a value of 0.5 means that the center of the child is aligned
|
|
/// with the center of the parent.
|
|
final FractionalOffset alignment;
|
|
|
|
/// The size this widget should attempt to be.
|
|
final Size size;
|
|
|
|
@override
|
|
RenderSizedOverflowBox createRenderObject(BuildContext context) {
|
|
return new RenderSizedOverflowBox(
|
|
alignment: alignment,
|
|
requestedSize: size
|
|
);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderSizedOverflowBox renderObject) {
|
|
renderObject
|
|
..alignment = alignment
|
|
..requestedSize = size;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('alignment: $alignment');
|
|
if (size != null)
|
|
description.add('size: $size');
|
|
}
|
|
}
|
|
|
|
/// A widget that lays the child out as if it was in the tree, but without painting anything,
|
|
/// without making the child available for hit testing, and without taking any
|
|
/// room in the parent.
|
|
class Offstage extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that visually hides its child.
|
|
const Offstage({ Key key, this.offstage: true, Widget child })
|
|
: assert(offstage != null),
|
|
super(key: key, child: child);
|
|
|
|
/// Whether the child is hidden from the rest of the tree.
|
|
///
|
|
/// If true, the child is laid out as if it was in the tree, but without
|
|
/// painting anything, without making the child available for hit testing, and
|
|
/// without taking any room in the parent.
|
|
///
|
|
/// If false, the child is included in the tree as normal.
|
|
final bool offstage;
|
|
|
|
@override
|
|
RenderOffstage createRenderObject(BuildContext context) => new RenderOffstage(offstage: offstage);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderOffstage renderObject) {
|
|
renderObject.offstage = offstage;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('offstage: $offstage');
|
|
}
|
|
|
|
@override
|
|
_OffstageElement createElement() => new _OffstageElement(this);
|
|
}
|
|
|
|
class _OffstageElement extends SingleChildRenderObjectElement {
|
|
_OffstageElement(Offstage widget) : super(widget);
|
|
|
|
@override
|
|
Offstage get widget => super.widget;
|
|
|
|
@override
|
|
void visitChildrenForSemantics(ElementVisitor visitor) {
|
|
if (!widget.offstage)
|
|
super.visitChildrenForSemantics(visitor);
|
|
}
|
|
}
|
|
|
|
/// A widget that attempts to size the child to a specific aspect ratio.
|
|
///
|
|
/// The widget first tries the largest width permited by the layout
|
|
/// constraints. The height of the widget is determined by applying the
|
|
/// given aspect ratio to the width, expressed as a ratio of width to height.
|
|
///
|
|
/// For example, a 16:9 width:height aspect ratio would have a value of
|
|
/// 16.0/9.0. If the maximum width is infinite, the initial width is determined
|
|
/// by applying the aspect ratio to the maximum height.
|
|
///
|
|
/// Now consider a second example, this time with an aspect ratio of 2.0 and
|
|
/// layout constraints that require the width to be between 0.0 and 100.0 and
|
|
/// the height to be between 0.0 and 100.0. We'll select a width of 100.0 (the
|
|
/// biggest allowed) and a height of 50.0 (to match the aspect ratio).
|
|
///
|
|
/// In that same situation, if the aspect ratio is 0.5, we'll also select a
|
|
/// width of 100.0 (still the biggest allowed) and we'll attempt to use a height
|
|
/// of 200.0. Unfortunately, that violates the constraints because the child can
|
|
/// be at most 100.0 pixels tall. The widget will then take that value
|
|
/// and apply the aspect ratio again to obtain a width of 50.0. That width is
|
|
/// permitted by the constraints and the child receives a width of 50.0 and a
|
|
/// height of 100.0. If the width were not permitted, the widget would
|
|
/// continue iterating through the constraints. If the widget does not
|
|
/// find a feasible size after consulting each constraint, the widget
|
|
/// will eventually select a size for the child that meets the layout
|
|
/// constraints but fails to meet the aspect ratio constraints.
|
|
class AspectRatio extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget with a specific aspect ratio.
|
|
///
|
|
/// The [aspectRatio] argument must not be null.
|
|
const AspectRatio({
|
|
Key key,
|
|
@required this.aspectRatio,
|
|
Widget child
|
|
}) : assert(aspectRatio != null),
|
|
super(key: key, child: child);
|
|
|
|
/// The aspect ratio to attempt to use.
|
|
///
|
|
/// The aspect ratio is expressed as a ratio of width to height. For example,
|
|
/// a 16:9 width:height aspect ratio would have a value of 16.0/9.0.
|
|
final double aspectRatio;
|
|
|
|
@override
|
|
RenderAspectRatio createRenderObject(BuildContext context) => new RenderAspectRatio(aspectRatio: aspectRatio);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderAspectRatio renderObject) {
|
|
renderObject.aspectRatio = aspectRatio;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('aspectRatio: $aspectRatio');
|
|
}
|
|
}
|
|
|
|
/// A widget that sizes its child to the child's intrinsic width.
|
|
///
|
|
/// Sizes its child's width to the child's maximum intrinsic width. If
|
|
/// [stepWidth] is non-null, the child's width will be snapped to a multiple of
|
|
/// the [stepWidth]. Similarly, if [stepHeight] is non-null, the child's height
|
|
/// will be snapped to a multiple of the [stepHeight].
|
|
///
|
|
/// This class is useful, for example, when unlimited width is available and
|
|
/// you would like a child that would otherwise attempt to expand infinitely to
|
|
/// instead size itself to a more reasonable width.
|
|
///
|
|
/// This class is relatively expensive. Avoid using it where possible.
|
|
class IntrinsicWidth extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that sizes its child to the child's intrinsic width.
|
|
///
|
|
/// This class is relatively expensive. Avoid using it where possible.
|
|
const IntrinsicWidth({ Key key, this.stepWidth, this.stepHeight, Widget child })
|
|
: super(key: key, child: child);
|
|
|
|
/// If non-null, force the child's width to be a multiple of this value.
|
|
final double stepWidth;
|
|
|
|
/// If non-null, force the child's height to be a multiple of this value.
|
|
final double stepHeight;
|
|
|
|
@override
|
|
RenderIntrinsicWidth createRenderObject(BuildContext context) => new RenderIntrinsicWidth(stepWidth: stepWidth, stepHeight: stepHeight);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderIntrinsicWidth renderObject) {
|
|
renderObject
|
|
..stepWidth = stepWidth
|
|
..stepHeight = stepHeight;
|
|
}
|
|
}
|
|
|
|
/// A widget that sizes its child to the child's intrinsic height.
|
|
///
|
|
/// This class is useful, for example, when unlimited height is available and
|
|
/// you would like a child that would otherwise attempt to expand infinitely to
|
|
/// instead size itself to a more reasonable height.
|
|
///
|
|
/// This class is relatively expensive. Avoid using it where possible.
|
|
class IntrinsicHeight extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that sizes its child to the child's intrinsic height.
|
|
///
|
|
/// This class is relatively expensive. Avoid using it where possible.
|
|
const IntrinsicHeight({ Key key, Widget child }) : super(key: key, child: child);
|
|
|
|
@override
|
|
RenderIntrinsicHeight createRenderObject(BuildContext context) => new RenderIntrinsicHeight();
|
|
}
|
|
|
|
/// A widget that positions its child according to the child's baseline.
|
|
///
|
|
/// This widget shifts the child down such that the child's baseline (or the
|
|
/// bottom of the child, if the child has no baseline) is [baseline]
|
|
/// logical pixels below the top of this box, then sizes this box to
|
|
/// contain the child. If [baseline] is less than the distance from
|
|
/// the top of the child to the baseline of the child, then the child
|
|
/// is top-aligned instead.
|
|
class Baseline extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that positions its child according to the child's baseline.
|
|
///
|
|
/// The [baseline] and [baselineType] arguments must not be null.
|
|
const Baseline({
|
|
Key key,
|
|
@required this.baseline,
|
|
@required this.baselineType,
|
|
Widget child
|
|
}) : assert(baseline != null),
|
|
assert(baselineType != null),
|
|
super(key: key, child: child);
|
|
|
|
/// The number of logical pixels from the top of this box at which to position
|
|
/// the child's baseline.
|
|
final double baseline;
|
|
|
|
/// The type of baseline to use for positioning the child.
|
|
final TextBaseline baselineType;
|
|
|
|
@override
|
|
RenderBaseline createRenderObject(BuildContext context) => new RenderBaseline(baseline: baseline, baselineType: baselineType);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderBaseline renderObject) {
|
|
renderObject
|
|
..baseline = baseline
|
|
..baselineType = baselineType;
|
|
}
|
|
}
|
|
|
|
|
|
// SLIVERS
|
|
|
|
/// A sliver that contains a single box widget.
|
|
///
|
|
/// Slivers are special-purpose widgets that can be combined using a
|
|
/// [CustomScrollView] to create custom scroll effects. A [SliverToBoxAdapter]
|
|
/// is a basic sliver that creates a bridge back to one of the usual box-based
|
|
/// widgets.
|
|
///
|
|
/// Rather than using multiple [SliverToBoxAdapter] widgets to display multiple
|
|
/// box widgets in a [CustomScrollView], consider using [SliverList],
|
|
/// [SliverFixedExtentList], [SliverPrototypeExtentList], or [SliverGrid],
|
|
/// which are more efficient because they instantiate only those children that
|
|
/// are actually visible through the scroll view's viewport.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [CustomScrollView], which displays a scrollable list of slivers.
|
|
/// * [SliverList], which displays multiple box widgets in a linear array.
|
|
/// * [SliverFixedExtentList], which displays multiple box widgets with the
|
|
/// same main-axis extent in a linear array.
|
|
/// * [SliverPrototypeExtentList], which displays multiple box widgets with the
|
|
/// same main-axis extent as a prototype item, in a linear array.
|
|
/// * [SliverGrid], which displays multiple box widgets in arbitrary positions.
|
|
class SliverToBoxAdapter extends SingleChildRenderObjectWidget {
|
|
/// Creates a sliver that contains a single box widget.
|
|
const SliverToBoxAdapter({
|
|
Key key,
|
|
Widget child,
|
|
}) : super(key: key, child: child);
|
|
|
|
@override
|
|
RenderSliverToBoxAdapter createRenderObject(BuildContext context) => new RenderSliverToBoxAdapter();
|
|
}
|
|
|
|
/// A sliver that applies padding on each side of another sliver.
|
|
///
|
|
/// Slivers are special-purpose widgets that can be combined using a
|
|
/// [CustomScrollView] to create custom scroll effects. A [SliverPadding]
|
|
/// is a basic sliver that insets another sliver by applying padding on each
|
|
/// side.
|
|
///
|
|
/// Applying padding to anything but the most mundane sliver is likely to have
|
|
/// undesired effects. For example, wrapping a [SliverPersistentHeader] with
|
|
/// `pinned:true` will cause the app bar to overlap earlier slivers (contrary to
|
|
/// the normal behavior of pinned app bars), and while the app bar is pinned,
|
|
/// the padding will scroll away.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [CustomScrollView], which displays a scrollable list of slivers.
|
|
class SliverPadding extends SingleChildRenderObjectWidget {
|
|
/// Creates a sliver that applies padding on each side of another sliver.
|
|
///
|
|
/// The [padding] argument must not be null.
|
|
const SliverPadding({
|
|
Key key,
|
|
@required this.padding,
|
|
Widget sliver,
|
|
}) : assert(padding != null),
|
|
super(key: key, child: sliver);
|
|
|
|
/// The amount of space by which to inset the child sliver.
|
|
final EdgeInsets padding;
|
|
|
|
@override
|
|
RenderSliverPadding createRenderObject(BuildContext context) => new RenderSliverPadding(padding: padding);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderSliverPadding renderObject) {
|
|
renderObject.padding = padding;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('padding: $padding');
|
|
}
|
|
}
|
|
|
|
|
|
// LAYOUT NODES
|
|
|
|
/// A widget that arranges its children sequentially along a given axis, forcing
|
|
/// them to the dimension of the parent in the other axis.
|
|
///
|
|
/// This widget is rarely used directly. Instead, consider using [ListView],
|
|
/// which combines a similar layout algorithm with scrolling behavior, or
|
|
/// [Column], which gives you more flexible control over the layout of a
|
|
/// vertical set of boxes.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [RenderListBody], which implements this layout algorithm and the
|
|
/// documentation for which describes some of its subtleties.
|
|
/// * [SingleChildScrollView], which is sometimes used with [ListBody] to
|
|
/// make the contents scrollable.
|
|
/// * [Column] and [Row], which implement a more elaborate version of
|
|
/// this layout algorithm (at the cost of being slightly less efficient).
|
|
/// * [ListView], which implements an efficient scrolling version of this
|
|
/// layout algorithm.
|
|
class ListBody extends MultiChildRenderObjectWidget {
|
|
/// Creates a layout widget that arranges its children sequentially along a
|
|
/// given axis.
|
|
///
|
|
/// By default, the [mainAxis] is [Axis.vertical].
|
|
ListBody({
|
|
Key key,
|
|
this.mainAxis: Axis.vertical,
|
|
List<Widget> children: const <Widget>[],
|
|
}) : assert(mainAxis != null),
|
|
super(key: key, children: children);
|
|
|
|
/// The direction to use as the main axis.
|
|
final Axis mainAxis;
|
|
|
|
@override
|
|
RenderListBody createRenderObject(BuildContext context) => new RenderListBody(mainAxis: mainAxis);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderListBody renderObject) {
|
|
renderObject.mainAxis = mainAxis;
|
|
}
|
|
}
|
|
|
|
/// A widget that uses the stack layout algorithm for its children.
|
|
///
|
|
/// This class is useful if you want to overlap several children in a simple
|
|
/// way, for example having some text and an image, overlaid with a gradient and
|
|
/// a button attached to the bottom.
|
|
///
|
|
/// Each child of a [Stack] widget is either _positioned_ or _non-positioned_.
|
|
/// Positioned children are those wrapped in a [Positioned] widget that has at
|
|
/// least one non-null property. The stack sizes itself to contain all the
|
|
/// non-positioned children, which are positioned according to [alignment]
|
|
/// (which defaults to the top-left corner). The positioned children are then
|
|
/// placed relative to the stack according to their top, right, bottom, and left
|
|
/// properties.
|
|
///
|
|
/// The stack paints its children in order. If you want to change the order in
|
|
/// which the children paint, you can rebuild the stack with the children in
|
|
/// the new order. If you reorder the children in this way, consider giving the
|
|
/// children non-null keys. These keys will cause the framework to move the
|
|
/// underlying objects for the children to their new locations rather than
|
|
/// recreate them at their new location.
|
|
///
|
|
/// For more details about the stack layout algorithm, see [RenderStack].
|
|
///
|
|
/// If you want to lay a number of children out in a particular pattern, or if
|
|
/// you want to make a custom layout manager, you probably want to use
|
|
/// [CustomMultiChildLayout] instead. In particular, when using a [Stack] you
|
|
/// can't position children relative to their size or the stack's own size.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [Align], which sizes itself based on its child's size and positions
|
|
/// the child according to a [FractionalOffset] value.
|
|
/// * [CustomSingleChildLayout], which uses a delegate to control the layout of
|
|
/// a single child.
|
|
/// * [CustomMultiChildLayout], which uses a delegate to position multiple
|
|
/// children.
|
|
/// * [Flow], which provides paint-time control of its children using transform
|
|
/// matrices.
|
|
class Stack extends MultiChildRenderObjectWidget {
|
|
/// Creates a stack layout widget.
|
|
///
|
|
/// By default, the non-positioned children of the stack are aligned by their
|
|
/// top left corners.
|
|
Stack({
|
|
Key key,
|
|
this.alignment: FractionalOffset.topLeft,
|
|
this.fit: StackFit.loose,
|
|
this.overflow: Overflow.clip,
|
|
List<Widget> children: const <Widget>[],
|
|
}) : super(key: key, children: children);
|
|
|
|
/// How to align the non-positioned children in the stack.
|
|
///
|
|
/// The non-positioned children are placed relative to each other such that
|
|
/// the points determined by [alignment] are co-located. For example, if the
|
|
/// [alignment] is [FractionalOffset.topLeft], then the top left corner of
|
|
/// each non-positioned child will be located at the same global coordinate.
|
|
final FractionalOffset alignment;
|
|
|
|
/// How to size the non-positioned children in the stack.
|
|
///
|
|
/// The constraints passed into the [Stack] from its parent are either
|
|
/// loosened ([StackFit.loose]) or tightened to their biggest size
|
|
/// ([StackFit.expand]).
|
|
final StackFit fit;
|
|
|
|
/// Whether overflowing children should be clipped. See [Overflow].
|
|
///
|
|
/// Some children in a stack might overflow its box. When this flag is set to
|
|
/// [Overflow.clip], children cannot paint outside of the stack's box.
|
|
final Overflow overflow;
|
|
|
|
@override
|
|
RenderStack createRenderObject(BuildContext context) {
|
|
return new RenderStack(
|
|
alignment: alignment,
|
|
fit: fit,
|
|
overflow: overflow,
|
|
);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderStack renderObject) {
|
|
renderObject
|
|
..alignment = alignment
|
|
..fit = fit
|
|
..overflow = overflow;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('alignment: $alignment');
|
|
description.add('fit: $fit');
|
|
description.add('overflow: $overflow');
|
|
}
|
|
}
|
|
|
|
/// A [Stack] that shows a single child from a list of children.
|
|
///
|
|
/// The displayed child is the one with the given [index]. The stack is
|
|
/// always as big as the largest child.
|
|
///
|
|
/// If value is null, then nothing is displayed.
|
|
///
|
|
/// For more details, see [Stack].
|
|
class IndexedStack extends Stack {
|
|
/// Creates a [Stack] widget that paints a single child.
|
|
///
|
|
/// The [index] argument must not be null.
|
|
IndexedStack({
|
|
Key key,
|
|
FractionalOffset alignment: FractionalOffset.topLeft,
|
|
StackFit sizing: StackFit.loose,
|
|
this.index: 0,
|
|
List<Widget> children: const <Widget>[],
|
|
}) : super(key: key, alignment: alignment, fit: sizing, children: children);
|
|
|
|
/// The index of the child to show.
|
|
final int index;
|
|
|
|
@override
|
|
RenderIndexedStack createRenderObject(BuildContext context) => new RenderIndexedStack(index: index, alignment: alignment);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderIndexedStack renderObject) {
|
|
renderObject
|
|
..index = index
|
|
..alignment = alignment;
|
|
}
|
|
}
|
|
|
|
/// A widget that controls where a child of a [Stack] is positioned.
|
|
///
|
|
/// A [Positioned] widget must be a descendant of a [Stack], and the path from
|
|
/// the [Positioned] widget to its enclosing [Stack] must contain only
|
|
/// [StatelessWidget]s or [StatefulWidget]s (not other kinds of widgets, like
|
|
/// [RenderObjectWidget]s).
|
|
///
|
|
/// If a widget is wrapped in a [Positioned], then it is a _positioned_ widget
|
|
/// in its [Stack]. If the [top] property is non-null, the top edge of this child
|
|
/// will be positioned [top] layout units from the top of the stack widget. The
|
|
/// [right], [bottom], and [left] properties work analogously.
|
|
///
|
|
/// If both the [top] and [bottom] properties are non-null, then the child will
|
|
/// be forced to have exactly the height required to satisfy both constraints.
|
|
/// Similarly, setting the [right] and [left] properties to non-null values will
|
|
/// force the child to have a particular width. Alternatively the [width] and
|
|
/// [height] properties can be used to give the dimensions, with one
|
|
/// corresponding position property (e.g. [top] and [height]).
|
|
class Positioned extends ParentDataWidget<Stack> {
|
|
/// Creates a widget that controls where a child of a [Stack] is positioned.
|
|
///
|
|
/// Only two out of the three horizontal values ([left], [right],
|
|
/// [width]), and only two out of the three vertical values ([top],
|
|
/// [bottom], [height]), can be set. In each case, at least one of
|
|
/// the three must be null.
|
|
const Positioned({
|
|
Key key,
|
|
this.left,
|
|
this.top,
|
|
this.right,
|
|
this.bottom,
|
|
this.width,
|
|
this.height,
|
|
@required Widget child
|
|
}) : assert(left == null || right == null || width == null),
|
|
assert(top == null || bottom == null || height == null),
|
|
super(key: key, child: child);
|
|
|
|
/// Creates a Positioned object with the values from the given [Rect].
|
|
///
|
|
/// This sets the [left], [top], [width], and [height] properties
|
|
/// from the given [Rect]. The [right] and [bottom] properties are
|
|
/// set to null.
|
|
Positioned.fromRect({
|
|
Key key,
|
|
Rect rect,
|
|
@required Widget child,
|
|
}) : left = rect.left,
|
|
top = rect.top,
|
|
width = rect.width,
|
|
height = rect.height,
|
|
right = null,
|
|
bottom = null,
|
|
super(key: key, child: child);
|
|
|
|
/// Creates a Positioned object with the values from the given [RelativeRect].
|
|
///
|
|
/// This sets the [left], [top], [right], and [bottom] properties from the
|
|
/// given [RelativeRect]. The [height] and [width] properties are set to null.
|
|
Positioned.fromRelativeRect({
|
|
Key key,
|
|
RelativeRect rect,
|
|
@required Widget child,
|
|
}) : left = rect.left,
|
|
top = rect.top,
|
|
right = rect.right,
|
|
bottom = rect.bottom,
|
|
width = null,
|
|
height = null,
|
|
super(key: key, child: child);
|
|
|
|
/// Creates a Positioned object with [left], [top], [right], and [bottom] set
|
|
/// to 0.0 unless a value for them is passed.
|
|
const Positioned.fill({
|
|
Key key,
|
|
this.left: 0.0,
|
|
this.top: 0.0,
|
|
this.right: 0.0,
|
|
this.bottom: 0.0,
|
|
@required Widget child,
|
|
}) : width = null,
|
|
height = null,
|
|
super(key: key, child: child);
|
|
|
|
/// The distance that the child's left edge is inset from the left of the stack.
|
|
///
|
|
/// Only two out of the three horizontal values ([left], [right], [width]) can be
|
|
/// set. The third must be null.
|
|
final double left;
|
|
|
|
/// The distance that the child's top edge is inset from the top of the stack.
|
|
///
|
|
/// Only two out of the three vertical values ([top], [bottom], [height]) can be
|
|
/// set. The third must be null.
|
|
final double top;
|
|
|
|
/// The distance that the child's right edge is inset from the right of the stack.
|
|
///
|
|
/// Only two out of the three horizontal values ([left], [right], [width]) can be
|
|
/// set. The third must be null.
|
|
final double right;
|
|
|
|
/// The distance that the child's bottom edge is inset from the bottom of the stack.
|
|
///
|
|
/// Only two out of the three vertical values ([top], [bottom], [height]) can be
|
|
/// set. The third must be null.
|
|
final double bottom;
|
|
|
|
/// The child's width.
|
|
///
|
|
/// Only two out of the three horizontal values ([left], [right], [width]) can be
|
|
/// set. The third must be null.
|
|
final double width;
|
|
|
|
/// The child's height.
|
|
///
|
|
/// Only two out of the three vertical values ([top], [bottom], [height]) can be
|
|
/// set. The third must be null.
|
|
final double height;
|
|
|
|
@override
|
|
void applyParentData(RenderObject renderObject) {
|
|
assert(renderObject.parentData is StackParentData);
|
|
final StackParentData parentData = renderObject.parentData;
|
|
bool needsLayout = false;
|
|
|
|
if (parentData.left != left) {
|
|
parentData.left = left;
|
|
needsLayout = true;
|
|
}
|
|
|
|
if (parentData.top != top) {
|
|
parentData.top = top;
|
|
needsLayout = true;
|
|
}
|
|
|
|
if (parentData.right != right) {
|
|
parentData.right = right;
|
|
needsLayout = true;
|
|
}
|
|
|
|
if (parentData.bottom != bottom) {
|
|
parentData.bottom = bottom;
|
|
needsLayout = true;
|
|
}
|
|
|
|
if (parentData.width != width) {
|
|
parentData.width = width;
|
|
needsLayout = true;
|
|
}
|
|
|
|
if (parentData.height != height) {
|
|
parentData.height = height;
|
|
needsLayout = true;
|
|
}
|
|
|
|
if (needsLayout) {
|
|
final AbstractNode targetParent = renderObject.parent;
|
|
if (targetParent is RenderObject)
|
|
targetParent.markNeedsLayout();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
if (left != null)
|
|
description.add('left: $left');
|
|
if (top != null)
|
|
description.add('top: $top');
|
|
if (right != null)
|
|
description.add('right: $right');
|
|
if (bottom != null)
|
|
description.add('bottom: $bottom');
|
|
if (width != null)
|
|
description.add('width: $width');
|
|
if (height != null)
|
|
description.add('height: $height');
|
|
}
|
|
}
|
|
|
|
/// A widget that displays its children in a one-dimensional array.
|
|
///
|
|
/// The [Flex] widget allows you to control the axis along which the children are
|
|
/// placed (horizontal or vertical). This is referred to as the _main axis_. If
|
|
/// you know the main axis in advance, then consider using a [Row] (if it's
|
|
/// horizontal) or [Column] (if it's vertical) instead, because that will be less
|
|
/// verbose.
|
|
///
|
|
/// To cause a child to expand to fill the available vertical space, wrap the
|
|
/// child in an [Expanded] widget.
|
|
///
|
|
/// The [Flex] widget does not scroll (and in general it is considered an error
|
|
/// to have more children in a [Flex] than will fit in the available room). If
|
|
/// you have some widgets and want them to be able to scroll if there is
|
|
/// insufficient room, consider using a [ListView].
|
|
///
|
|
/// If you only have one child, then rather than using [Flex], [Row], or
|
|
/// [Column], consider using [Align] or [Center] to position the child.
|
|
///
|
|
/// ## Layout algorithm
|
|
///
|
|
/// Layout for a [Flex] proceeds in six steps:
|
|
///
|
|
/// 1. Layout each child a null or zero flex factor (e.g., those that are not
|
|
/// [Expanded]) with unbounded main axis constraints and the incoming
|
|
/// cross axis constraints. If the [crossAxisAlignment] is
|
|
/// [CrossAxisAlignment.stretch], instead use tight cross axis constraints
|
|
/// that match the incoming max extent in the cross axis.
|
|
/// 2. Divide the remaining main axis space among the children with non-zero
|
|
/// flex factors (e.g., those that are [Expanded]) according to their flex
|
|
/// factor. For example, a child with a flex factor of 2.0 will receive twice
|
|
/// the amount of main axis space as a child with a flex factor of 1.0.
|
|
/// 3. Layout each of the remaining children with the same cross axis
|
|
/// constraints as in step 1, but instead of using unbounded main axis
|
|
/// constraints, use max axis constraints based on the amount of space
|
|
/// allocated in step 2. Children with [Flexible.fit] properties that are
|
|
/// [FlexFit.tight] are given tight constraints (i.e., forced to fill the
|
|
/// allocated space), and children with [Flexible.fit] properties that are
|
|
/// [FlexFit.loose] are given loose constraints (i.e., not forced to fill the
|
|
/// allocated space).
|
|
/// 4. The cross axis extent of the [Flex] is the maximum cross axis extent of
|
|
/// the children (which will always satisfy the incoming constraints).
|
|
/// 5. The main axis extent of the [Flex] is determined by the [mainAxisSize]
|
|
/// property. If the [mainAxisSize] property is [MainAxisSize.max], then the
|
|
/// main axis extent of the [Flex] is the max extent of the incoming main
|
|
/// axis constraints. If the [mainAxisSize] property is [MainAxisSize.min],
|
|
/// then the main axis extent of the [Flex] is the sum of the main axis
|
|
/// extents of the children (subject to the incoming constraints).
|
|
/// 6. Determine the position for each child according to the
|
|
/// [mainAxisAlignment] and the [crossAxisAlignment]. For example, if the
|
|
/// [mainAxisAlignment] is [MainAxisAlignment.spaceBetween], any main axis
|
|
/// space that has not been allocated to children is divided evenly and
|
|
/// placed between the children.
|
|
class Flex extends MultiChildRenderObjectWidget {
|
|
/// Creates a flex layout.
|
|
///
|
|
/// The [direction] is required.
|
|
///
|
|
/// The [direction], [mainAxisAlignment], and [crossAxisAlignment] arguments
|
|
/// must not be null. If [crossAxisAlignment] is
|
|
/// [CrossAxisAlignment.baseline], then [textBaseline] must not be null.
|
|
Flex({
|
|
Key key,
|
|
@required this.direction,
|
|
this.mainAxisAlignment: MainAxisAlignment.start,
|
|
this.mainAxisSize: MainAxisSize.max,
|
|
this.crossAxisAlignment: CrossAxisAlignment.center,
|
|
this.textBaseline,
|
|
List<Widget> children: const <Widget>[],
|
|
}) : assert(direction != null),
|
|
assert(mainAxisAlignment != null),
|
|
assert(mainAxisSize != null),
|
|
assert(crossAxisAlignment != null),
|
|
super(key: key, children: children) {
|
|
assert(crossAxisAlignment != CrossAxisAlignment.baseline || textBaseline != null); // https://github.com/dart-lang/sdk/issues/29278
|
|
}
|
|
|
|
/// The direction to use as the main axis.
|
|
///
|
|
/// If you know the axis in advance, then consider using a [Row] (if it's
|
|
/// horizontal) or [Column] (if it's vertical) instead of a [Flex], since that
|
|
/// will be less verbose. (For [Row] and [Column] this property is fixed to
|
|
/// the appropriate axis.)
|
|
final Axis direction;
|
|
|
|
/// How the children should be placed along the main axis.
|
|
///
|
|
/// For example, [MainAxisAlignment.start], the default, places the children
|
|
/// at the start (i.e., the left for a [Row] or the top for a [Column]) of the
|
|
/// main axis.
|
|
final MainAxisAlignment mainAxisAlignment;
|
|
|
|
/// How much space space should be occupied in the main axis.
|
|
///
|
|
/// After allocating space to children, there might be some remaining free
|
|
/// space. This value controls whether to maximize or minimize the amount of
|
|
/// free space, subject to the incoming layout constraints.
|
|
///
|
|
/// If some children have a non-zero flex factors (and none have a fit of
|
|
/// [FlexFit.loose]), they will expand to consume all the available space and
|
|
/// there will be no remaining free space to maximize or minimize, making this
|
|
/// value irrelevant to the final layout.
|
|
final MainAxisSize mainAxisSize;
|
|
|
|
/// How the children should be placed along the cross axis.
|
|
///
|
|
/// For example, [CrossAxisAlignment.center], the default, centers the
|
|
/// children in the cross axis (e.g., horizontally for a [Column]).
|
|
final CrossAxisAlignment crossAxisAlignment;
|
|
|
|
/// If aligning items according to their baseline, which baseline to use.
|
|
final TextBaseline textBaseline;
|
|
|
|
@override
|
|
RenderFlex createRenderObject(BuildContext context) {
|
|
return new RenderFlex(
|
|
direction: direction,
|
|
mainAxisAlignment: mainAxisAlignment,
|
|
mainAxisSize: mainAxisSize,
|
|
crossAxisAlignment: crossAxisAlignment,
|
|
textBaseline: textBaseline
|
|
);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, covariant RenderFlex renderObject) {
|
|
renderObject
|
|
..direction = direction
|
|
..mainAxisAlignment = mainAxisAlignment
|
|
..mainAxisSize = mainAxisSize
|
|
..crossAxisAlignment = crossAxisAlignment
|
|
..textBaseline = textBaseline;
|
|
}
|
|
}
|
|
|
|
/// A widget that displays its children in a horizontal array.
|
|
///
|
|
/// To cause a child to expand to fill the available horizontal space, wrap the
|
|
/// child in an [Expanded] widget.
|
|
///
|
|
/// The [Row] widget does not scroll (and in general it is considered an error
|
|
/// to have more children in a [Row] than will fit in the available room). If
|
|
/// you have a line of widgets and want them to be able to scroll if there is
|
|
/// insufficient room, consider using a [ListView].
|
|
///
|
|
/// For a vertical variant, see [Column].
|
|
///
|
|
/// If you only have one child, then consider using [Align] or [Center] to
|
|
/// position the child.
|
|
///
|
|
/// ## Layout algorithm
|
|
///
|
|
/// Layout for a [Row] proceeds in six steps:
|
|
///
|
|
/// 1. Layout each child a null or zero flex factor (e.g., those that are not
|
|
/// [Expanded]) with unbounded horizontal constraints and the incoming
|
|
/// vertical constraints. If the [crossAxisAlignment] is
|
|
/// [CrossAxisAlignment.stretch], instead use tight vertical constraints that
|
|
/// match the incoming max height.
|
|
/// 2. Divide the remaining horizontal space among the children with non-zero
|
|
/// flex factors (e.g., those that are [Expanded]) according to their flex
|
|
/// factor. For example, a child with a flex factor of 2.0 will receive twice
|
|
/// the amount of horizontal space as a child with a flex factor of 1.0.
|
|
/// 3. Layout each of the remaining children with the same vertical constraints
|
|
/// as in step 1, but instead of using unbounded horizontal constraints, use
|
|
/// horizontal constraints based on the amount of space allocated in step 2.
|
|
/// Children with [Flexible.fit] properties that are [FlexFit.tight] are
|
|
/// given tight constraints (i.e., forced to fill the allocated space), and
|
|
/// children with [Flexible.fit] properties that are [FlexFit.loose] are
|
|
/// given loose constraints (i.e., not forced to fill the allocated space).
|
|
/// 4. The height of the [Row] is the maximum height of the children (which will
|
|
/// always satisfy the incoming vertical constraints).
|
|
/// 5. The width of the [Row] is determined by the [mainAxisSize] property. If
|
|
/// the [mainAxisSize] property is [MainAxisSize.max], then the width of the
|
|
/// [Row] is the max width of the incoming constraints. If the [mainAxisSize]
|
|
/// property is [MainAxisSize.min], then the width of the [Row] is the sum
|
|
/// of widths of the children (subject to the incoming constraints).
|
|
/// 6. Determine the position for each child according to the
|
|
/// [mainAxisAlignment] and the [crossAxisAlignment]. For example, if the
|
|
/// [mainAxisAlignment] is [MainAxisAlignment.spaceBetween], any horizontal
|
|
/// space that has not been allocated to children is divided evenly and
|
|
/// placed between the children.
|
|
class Row extends Flex {
|
|
/// Creates a horizontal array of children.
|
|
///
|
|
/// The [direction], [mainAxisAlignment], [mainAxisSize], and
|
|
/// [crossAxisAlignment] arguments must not be null. If [crossAxisAlignment]
|
|
/// is [CrossAxisAlignment.baseline], then [textBaseline] must not be null.
|
|
Row({
|
|
Key key,
|
|
MainAxisAlignment mainAxisAlignment: MainAxisAlignment.start,
|
|
MainAxisSize mainAxisSize: MainAxisSize.max,
|
|
CrossAxisAlignment crossAxisAlignment: CrossAxisAlignment.center,
|
|
TextBaseline textBaseline,
|
|
List<Widget> children: const <Widget>[],
|
|
}) : super(
|
|
children: children,
|
|
key: key,
|
|
direction: Axis.horizontal,
|
|
mainAxisAlignment: mainAxisAlignment,
|
|
mainAxisSize: mainAxisSize,
|
|
crossAxisAlignment: crossAxisAlignment,
|
|
textBaseline: textBaseline
|
|
);
|
|
}
|
|
|
|
/// A widget that displays its children in a vertical array.
|
|
///
|
|
/// To cause a child to expand to fill the available vertical space, wrap the
|
|
/// child in an [Expanded] widget.
|
|
///
|
|
/// The [Column] widget does not scroll (and in general it is considered an error
|
|
/// to have more children in a [Column] than will fit in the available room). If
|
|
/// you have a line of widgets and want them to be able to scroll if there is
|
|
/// insufficient room, consider using a [ListView].
|
|
///
|
|
/// For a horizontal variant, see [Row].
|
|
///
|
|
/// If you only have one child, then consider using [Align] or [Center] to
|
|
/// position the child.
|
|
///
|
|
/// ## Layout algorithm
|
|
///
|
|
/// Layout for a [Column] proceeds in six steps:
|
|
///
|
|
/// 1. Layout each child a null or zero flex factor (e.g., those that are not
|
|
/// [Expanded]) with unbounded vertical constraints and the incoming
|
|
/// horizontal constraints. If the [crossAxisAlignment] is
|
|
/// [CrossAxisAlignment.stretch], instead use tight horizontal constraints
|
|
/// that match the incoming max width.
|
|
/// 2. Divide the remaining vertical space among the children with non-zero
|
|
/// flex factors (e.g., those that are [Expanded]) according to their flex
|
|
/// factor. For example, a child with a flex factor of 2.0 will receive twice
|
|
/// the amount of vertical space as a child with a flex factor of 1.0.
|
|
/// 3. Layout each of the remaining children with the same horizontal
|
|
/// constraints as in step 1, but instead of using unbounded vertical
|
|
/// constraints, use vertical constraints based on the amount of space
|
|
/// allocated in step 2. Children with [Flexible.fit] properties that are
|
|
/// [FlexFit.tight] are given tight constraints (i.e., forced to fill the
|
|
/// allocated space), and children with [Flexible.fit] properties that are
|
|
/// [FlexFit.loose] are given loose constraints (i.e., not forced to fill the
|
|
/// allocated space).
|
|
/// 4. The width of the [Column] is the maximum width of the children (which
|
|
/// will always satisfy the incoming horizontal constraints).
|
|
/// 5. The height of the [Column] is determined by the [mainAxisSize] property.
|
|
/// If the [mainAxisSize] property is [MainAxisSize.max], then the height of
|
|
/// the [Column] is the max height of the incoming constraints. If the
|
|
/// [mainAxisSize] property is [MainAxisSize.min], then the height of the
|
|
/// [Column] is the sum of heights of the children (subject to the incoming
|
|
/// constraints).
|
|
/// 6. Determine the position for each child according to the
|
|
/// [mainAxisAlignment] and the [crossAxisAlignment]. For example, if the
|
|
/// [mainAxisAlignment] is [MainAxisAlignment.spaceBetween], any vertical
|
|
/// space that has not been allocated to children is divided evenly and
|
|
/// placed between the children.
|
|
class Column extends Flex {
|
|
/// Creates a vertical array of children.
|
|
///
|
|
/// The [direction], [mainAxisAlignment], [mainAxisSize], and
|
|
/// [crossAxisAlignment] arguments must not be null. If [crossAxisAlignment]
|
|
/// is [CrossAxisAlignment.baseline], then [textBaseline] must not be null.
|
|
Column({
|
|
Key key,
|
|
MainAxisAlignment mainAxisAlignment: MainAxisAlignment.start,
|
|
MainAxisSize mainAxisSize: MainAxisSize.max,
|
|
CrossAxisAlignment crossAxisAlignment: CrossAxisAlignment.center,
|
|
TextBaseline textBaseline,
|
|
List<Widget> children: const <Widget>[],
|
|
}) : super(
|
|
children: children,
|
|
key: key,
|
|
direction: Axis.vertical,
|
|
mainAxisAlignment: mainAxisAlignment,
|
|
mainAxisSize: mainAxisSize,
|
|
crossAxisAlignment: crossAxisAlignment,
|
|
textBaseline: textBaseline
|
|
);
|
|
}
|
|
|
|
/// A widget that controls how a child of a [Row], [Column], or [Flex] flexes.
|
|
///
|
|
/// Using a [Flexible] widget gives a child of a [Row], [Column], or [Flex]
|
|
/// the flexibility to expand to fill the available space in the main axis
|
|
/// (e.g., horizontally for a [Row] or vertically for a [Column]), but, unlike
|
|
/// [Expanded], [Flexible] does not require the child to fill the available
|
|
/// space.
|
|
///
|
|
/// A [Flexible] widget must be a descendant of a [Row], [Column], or [Flex],
|
|
/// and the path from the [Flexible] widget to its enclosing [Row], [Column], or
|
|
/// [Flex] must contain only [StatelessWidget]s or [StatefulWidget]s (not other
|
|
/// kinds of widgets, like [RenderObjectWidget]s).
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [Expanded], which forces the child to expand to fill the available space.
|
|
class Flexible extends ParentDataWidget<Flex> {
|
|
/// Creates a widget that controls how a child of a [Row], [Column], or [Flex]
|
|
/// flexes.
|
|
const Flexible({
|
|
Key key,
|
|
this.flex: 1,
|
|
this.fit: FlexFit.loose,
|
|
@required Widget child,
|
|
}) : super(key: key, child: child);
|
|
|
|
/// The flex factor to use for this child
|
|
///
|
|
/// If null or zero, the child is inflexible and determines its own size. If
|
|
/// non-zero, the amount of space the child's can occupy in the main axis is
|
|
/// determined by dividing the free space (after placing the inflexible
|
|
/// children) according to the flex factors of the flexible children.
|
|
final int flex;
|
|
|
|
/// How a flexible child is inscribed into the available space.
|
|
///
|
|
/// If [flex] is non-zero, the [fit] determines whether the child fills the
|
|
/// space the parent makes available during layout. If the fit is
|
|
/// [FlexFit.tight], the child is required to fill the available space. If the
|
|
/// fit is [FlexFit.loose], the child can be at most as large as the available
|
|
/// space (but is allowed to be smaller).
|
|
final FlexFit fit;
|
|
|
|
@override
|
|
void applyParentData(RenderObject renderObject) {
|
|
assert(renderObject.parentData is FlexParentData);
|
|
final FlexParentData parentData = renderObject.parentData;
|
|
bool needsLayout = false;
|
|
|
|
if (parentData.flex != flex) {
|
|
parentData.flex = flex;
|
|
needsLayout = true;
|
|
}
|
|
|
|
if (parentData.fit != fit) {
|
|
parentData.fit = fit;
|
|
needsLayout = true;
|
|
}
|
|
|
|
if (needsLayout) {
|
|
final AbstractNode targetParent = renderObject.parent;
|
|
if (targetParent is RenderObject)
|
|
targetParent.markNeedsLayout();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('flex: $flex');
|
|
}
|
|
}
|
|
|
|
/// A widget that expands a child of a [Row], [Column], or [Flex].
|
|
///
|
|
/// Using an [Expanded] widget makes a child of a [Row], [Column], or [Flex]
|
|
/// expand to fill the available space in the main axis (e.g., horizontally for
|
|
/// a [Row] or vertically for a [Column]). If multiple children are expanded,
|
|
/// the available space is divided amoung them according to the [flex] factor.
|
|
///
|
|
/// An [Expanded] widget must be a descendant of a [Row], [Column], or [Flex],
|
|
/// and the path from the [Expanded] widget to its enclosing [Row], [Column], or
|
|
/// [Flex] must contain only [StatelessWidget]s or [StatefulWidget]s (not other
|
|
/// kinds of widgets, like [RenderObjectWidget]s).
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [Flexible], which does not force the child to fill the available space.
|
|
class Expanded extends Flexible {
|
|
/// Creates a widget that expands a child of a [Row], [Column], or [Flex]
|
|
/// expand to fill the available space in the main axis.
|
|
const Expanded({
|
|
Key key,
|
|
int flex: 1,
|
|
@required Widget child,
|
|
}) : super(key: key, flex: flex, fit: FlexFit.tight, child: child);
|
|
}
|
|
|
|
/// A widget that displays its children in multiple horizontal or vertical runs.
|
|
///
|
|
/// A [Wrap] lays out each child and attempts to place the child adjacent to the
|
|
/// previous child in the main axis, given by [direction], leaving [spacing]
|
|
/// space in between. If there is not enough space to fit the child, [Wrap]
|
|
/// creates a new _run_ adjacent to the existing children in the cross axis.
|
|
///
|
|
/// After all the children have been allocated to runs, the children within the
|
|
/// runs are positioned according to the [alignment] in the main axis and
|
|
/// according to the [crossAxisAlignment] in the cross axis.
|
|
///
|
|
/// The runs themselves are then positioned in the cross axis according to the
|
|
/// [runSpacing] and [runAlignment].
|
|
class Wrap extends MultiChildRenderObjectWidget {
|
|
/// Creates a wrap layout.
|
|
///
|
|
/// By default, the wrap layout is horizontal and both the children and the
|
|
/// runs are aligned to the start.
|
|
Wrap({
|
|
Key key,
|
|
this.direction: Axis.horizontal,
|
|
this.alignment: WrapAlignment.start,
|
|
this.spacing: 0.0,
|
|
this.runAlignment: WrapAlignment.start,
|
|
this.runSpacing: 0.0,
|
|
this.crossAxisAlignment: WrapCrossAlignment.start,
|
|
List<Widget> children: const <Widget>[],
|
|
}) : super(key: key, children: children);
|
|
|
|
/// The direction to use as the main axis.
|
|
///
|
|
/// For example, if [direction] is [Axis.horizontal], the default, the
|
|
/// children are placed adjacent to one another in a horizontal run until the
|
|
/// available horizontal space is consumed, at which point a subsequent
|
|
/// children are placed in a new run vertically adjacent to the previous run.
|
|
final Axis direction;
|
|
|
|
/// How the children within a run should be places in the main axis.
|
|
///
|
|
/// For example, if [alignment] is [WrapAlignment.center], the children in
|
|
/// each run are grouped togeter in the center of their run in the main axis.
|
|
///
|
|
/// Defaults to [WrapAlignment.start].
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [runAlignment], which controls how the runs are placed relative to each
|
|
/// other in the cross axis.
|
|
/// * [crossAxisAlignment], which controls how the children within each run
|
|
/// are placed relative to each other in the cross axis.
|
|
final WrapAlignment alignment;
|
|
|
|
/// How much space to place between children in a run in the main axis.
|
|
///
|
|
/// For example, if [spacing] is 10.0, the children will be spaced at least
|
|
/// 10.0 logical pixels apart in the main axis.
|
|
///
|
|
/// If there is additional free space in a run (e.g., because the wrap has a
|
|
/// minimum size that is not filled or because some runs are longer than
|
|
/// others), the additional free space will be allocated according to the
|
|
/// [alignment].
|
|
///
|
|
/// Defaults to 0.0.
|
|
final double spacing;
|
|
|
|
/// How the runs themselves should be placed in the cross axis.
|
|
///
|
|
/// For example, if [runAlignment] is [WrapAlignment.center], the runs are
|
|
/// grouped togeter in the center of the overall [Wrap] in the cross axis.
|
|
///
|
|
/// Defaults to [WrapAlignment.start].
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [alignment], which controls how the children within each run are placed
|
|
/// relative to each other in the main axis.
|
|
/// * [crossAxisAlignment], which controls how the children within each run
|
|
/// are placed relative to each other in the cross axis.
|
|
final WrapAlignment runAlignment;
|
|
|
|
/// How much space to place between the runs themselves in the cross axis.
|
|
///
|
|
/// For example, if [runSpacing] is 10.0, the runs will be spaced at least
|
|
/// 10.0 logical pixels apart in the cross axis.
|
|
///
|
|
/// If there is additional free space in the overall [Wrap] (e.g., because
|
|
/// the wrap has a minimum size that is not filled), the additional free space
|
|
/// will be allocated according to the [runAlignment].
|
|
///
|
|
/// Defaults to 0.0.
|
|
final double runSpacing;
|
|
|
|
/// How the children within a run should be aligned relative to each other in
|
|
/// the cross axis.
|
|
///
|
|
/// For example, if this is set to [WrapCrossAlignment.end], and the
|
|
/// [direction] is [Axis.horizontal], then the children within each
|
|
/// run will have their bottom edges aligned to the bottom edge of the run.
|
|
///
|
|
/// Defaults to [WrapCrossAlignment.start].
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [alignment], which controls how the children within each run are placed
|
|
/// relative to each other in the main axis.
|
|
/// * [runAlignment], which controls how the runs are placed relative to each
|
|
/// other in the cross axis.
|
|
final WrapCrossAlignment crossAxisAlignment;
|
|
|
|
@override
|
|
RenderWrap createRenderObject(BuildContext context) {
|
|
return new RenderWrap(
|
|
direction: direction,
|
|
alignment: alignment,
|
|
spacing: spacing,
|
|
runAlignment: runAlignment,
|
|
runSpacing: runSpacing,
|
|
crossAxisAlignment: crossAxisAlignment,
|
|
);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderWrap renderObject) {
|
|
renderObject
|
|
..direction = direction
|
|
..alignment = alignment
|
|
..spacing = spacing
|
|
..runAlignment = runAlignment
|
|
..runSpacing = runSpacing
|
|
..crossAxisAlignment = crossAxisAlignment;
|
|
}
|
|
}
|
|
|
|
/// A widget that implements the flow layout algorithm.
|
|
///
|
|
/// Flow layouts are optimized for repositioning children using transformation
|
|
/// matrices.
|
|
///
|
|
/// The flow container is sized independently from the children by the
|
|
/// [FlowDelegate.getSize] function of the delegate. The children are then sized
|
|
/// independently given the constraints from the
|
|
/// [FlowDelegate.getConstraintsForChild] function.
|
|
///
|
|
/// Rather than positioning the children during layout, the children are
|
|
/// positioned using transformation matrices during the paint phase using the
|
|
/// matrices from the [FlowDelegate.paintChildren] function. The children can be
|
|
/// repositioned efficiently by simply repainting the flow.
|
|
///
|
|
/// The most efficient way to trigger a repaint of the flow is to supply a
|
|
/// repaint argument to the constructor of the [FlowDelegate]. The flow will
|
|
/// listen to this animation and repaint whenever the animation ticks, avoiding
|
|
/// both the build and layout phases of the pipeline.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [FlowDelegate], which controls the visual presentation of the children.
|
|
/// * [Stack], which arranges children relative to the edges of the container.
|
|
/// * [CustomSingleChildLayout], which uses a delegate to control the layout of
|
|
/// a single child.
|
|
/// * [CustomMultiChildLayout], which uses a delegate to position multiple
|
|
/// children.
|
|
class Flow extends MultiChildRenderObjectWidget {
|
|
/// Creates a flow layout.
|
|
///
|
|
/// Wraps each of the given children in a [RepaintBoundary] to avoid
|
|
/// repainting the children when the flow repaints.
|
|
///
|
|
/// The [delegate] argument must not be null.
|
|
Flow({
|
|
Key key,
|
|
@required this.delegate,
|
|
List<Widget> children: const <Widget>[],
|
|
}) : assert(delegate != null),
|
|
super(key: key, children: RepaintBoundary.wrapAll(children));
|
|
// https://github.com/dart-lang/sdk/issues/29277
|
|
|
|
/// Creates a flow layout.
|
|
///
|
|
/// Does not wrap the given children in repaint boundaries, unlike the default
|
|
/// constructor. Useful when the child is trivial to paint or already contains
|
|
/// a repaint boundary.
|
|
///
|
|
/// The [delegate] argument must not be null.
|
|
Flow.unwrapped({
|
|
Key key,
|
|
@required this.delegate,
|
|
List<Widget> children: const <Widget>[],
|
|
}) : super(key: key, children: children) {
|
|
assert(delegate != null);
|
|
}
|
|
|
|
/// The delegate that controls the transformation matrices of the children.
|
|
final FlowDelegate delegate;
|
|
|
|
@override
|
|
RenderFlow createRenderObject(BuildContext context) => new RenderFlow(delegate: delegate);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderFlow renderObject) {
|
|
renderObject
|
|
..delegate = delegate;
|
|
}
|
|
}
|
|
|
|
/// A paragraph of rich text.
|
|
///
|
|
/// The [RichText] widget displays text that uses multiple different styles. The
|
|
/// text to display is described using a tree of [TextSpan] objects, each of
|
|
/// which has an associated style that is used for that subtree. The text might
|
|
/// break across multiple lines or might all be displayed on the same line
|
|
/// depending on the layout constraints.
|
|
///
|
|
/// Text displayed in a [RichText] widget must be explicitly styled. When
|
|
/// picking which style to use, consider using [DefaultTextStyle.of] the current
|
|
/// [BuildContext] to provide defaults.
|
|
///
|
|
/// When all the text uses the same style, consider using the [Text] widget,
|
|
/// which is less verbose and integrates with [DefaultTextStyle] for default
|
|
/// styling.
|
|
///
|
|
/// Example:
|
|
///
|
|
/// ```dart
|
|
/// new RichText(
|
|
/// text: new TextSpan(
|
|
/// text: 'Hello ',
|
|
/// style: DefaultTextStyle.of(context).style,
|
|
/// children: <TextSpan>[
|
|
/// new TextSpan(text: 'bold', style: new TextStyle(fontWeight: FontWeight.bold)),
|
|
/// new TextSpan(text: ' world!'),
|
|
/// ],
|
|
/// ),
|
|
/// ),
|
|
/// ```
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [Text]
|
|
/// * [TextSpan]
|
|
/// * [DefaultTextStyle]
|
|
class RichText extends LeafRenderObjectWidget {
|
|
/// Creates a paragraph of rich text.
|
|
///
|
|
/// The [text], [softWrap], and [overflow] arguments must not be null.
|
|
const RichText({
|
|
Key key,
|
|
@required this.text,
|
|
this.textAlign,
|
|
this.softWrap: true,
|
|
this.overflow: TextOverflow.clip,
|
|
this.textScaleFactor: 1.0,
|
|
this.maxLines,
|
|
}) : assert(text != null),
|
|
assert(softWrap != null),
|
|
assert(overflow != null),
|
|
assert(textScaleFactor != null),
|
|
super(key: key);
|
|
|
|
/// The text to display in this widget.
|
|
final TextSpan text;
|
|
|
|
/// How the text should be aligned horizontally.
|
|
final TextAlign textAlign;
|
|
|
|
/// Whether the text should break at soft line breaks.
|
|
///
|
|
/// If false, the glyphs in the text will be positioned as if there was unlimited horizontal space.
|
|
final bool softWrap;
|
|
|
|
/// How visual overflow should be handled.
|
|
final TextOverflow overflow;
|
|
|
|
/// The number of font pixels for each logical pixel.
|
|
///
|
|
/// For example, if the text scale factor is 1.5, text will be 50% larger than
|
|
/// the specified font size.
|
|
final double textScaleFactor;
|
|
|
|
/// An optional maximum number of lines for the text to span, wrapping if necessary.
|
|
/// If the text exceeds the given number of lines, it will be truncated according
|
|
/// to [overflow].
|
|
final int maxLines;
|
|
|
|
@override
|
|
RenderParagraph createRenderObject(BuildContext context) {
|
|
return new RenderParagraph(text,
|
|
textAlign: textAlign,
|
|
softWrap: softWrap,
|
|
overflow: overflow,
|
|
textScaleFactor: textScaleFactor,
|
|
maxLines: maxLines,
|
|
);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderParagraph renderObject) {
|
|
renderObject
|
|
..text = text
|
|
..textAlign = textAlign
|
|
..softWrap = softWrap
|
|
..overflow = overflow
|
|
..textScaleFactor = textScaleFactor
|
|
..maxLines = maxLines;
|
|
}
|
|
}
|
|
|
|
/// A widget that displays a [dart:ui.Image] directly.
|
|
///
|
|
/// The image is painted using [paintImage], which describes the meanings of the
|
|
/// various fields on this class in more detail.
|
|
///
|
|
/// This widget is rarely used directly. Instead, consider using [Image].
|
|
class RawImage extends LeafRenderObjectWidget {
|
|
/// Creates a widget that displays an image.
|
|
///
|
|
/// The [scale] and [repeat] arguments must not be null.
|
|
const RawImage({
|
|
Key key,
|
|
this.image,
|
|
this.width,
|
|
this.height,
|
|
this.scale: 1.0,
|
|
this.color,
|
|
this.colorBlendMode,
|
|
this.fit,
|
|
this.alignment,
|
|
this.repeat: ImageRepeat.noRepeat,
|
|
this.centerSlice
|
|
}) : assert(scale != null),
|
|
assert(repeat != null),
|
|
super(key: key);
|
|
|
|
/// The image to display.
|
|
final ui.Image image;
|
|
|
|
/// If non-null, require the image to have this width.
|
|
///
|
|
/// If null, the image will pick a size that best preserves its intrinsic
|
|
/// aspect ratio.
|
|
final double width;
|
|
|
|
/// If non-null, require the image to have this height.
|
|
///
|
|
/// If null, the image will pick a size that best preserves its intrinsic
|
|
/// aspect ratio.
|
|
final double height;
|
|
|
|
/// Specifies the image's scale.
|
|
///
|
|
/// Used when determining the best display size for the image.
|
|
final double scale;
|
|
|
|
/// If non-null, this color is blended with each image pixel using [colorBlendMode].
|
|
final Color color;
|
|
|
|
/// Used to combine [color] with this image.
|
|
///
|
|
/// The default is [BlendMode.srcIn]. In terms of the blend mode, [color] is
|
|
/// the source and this image is the destination.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [BlendMode], which includes an illustration of the effect of each blend mode.
|
|
final BlendMode colorBlendMode;
|
|
|
|
/// How to inscribe the image into the space allocated during layout.
|
|
///
|
|
/// The default varies based on the other fields. See the discussion at
|
|
/// [paintImage].
|
|
final BoxFit fit;
|
|
|
|
/// How to align the image within its bounds.
|
|
///
|
|
/// An alignment of (0.0, 0.0) aligns the image to the top-left corner of its
|
|
/// layout bounds. An alignment of (1.0, 0.5) aligns the image to the middle
|
|
/// of the right edge of its layout bounds.
|
|
final FractionalOffset alignment;
|
|
|
|
/// How to paint any portions of the layout bounds not covered by the image.
|
|
final ImageRepeat repeat;
|
|
|
|
/// The center slice for a nine-patch image.
|
|
///
|
|
/// The region of the image inside the center slice will be stretched both
|
|
/// horizontally and vertically to fit the image into its destination. The
|
|
/// region of the image above and below the center slice will be stretched
|
|
/// only horizontally and the region of the image to the left and right of
|
|
/// the center slice will be stretched only vertically.
|
|
final Rect centerSlice;
|
|
|
|
@override
|
|
RenderImage createRenderObject(BuildContext context) => new RenderImage(
|
|
image: image,
|
|
width: width,
|
|
height: height,
|
|
scale: scale,
|
|
color: color,
|
|
colorBlendMode: colorBlendMode,
|
|
fit: fit,
|
|
alignment: alignment,
|
|
repeat: repeat,
|
|
centerSlice: centerSlice
|
|
);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderImage renderObject) {
|
|
renderObject
|
|
..image = image
|
|
..width = width
|
|
..height = height
|
|
..scale = scale
|
|
..color = color
|
|
..colorBlendMode = colorBlendMode
|
|
..alignment = alignment
|
|
..fit = fit
|
|
..repeat = repeat
|
|
..centerSlice = centerSlice;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('image: $image');
|
|
if (width != null)
|
|
description.add('width: $width');
|
|
if (height != null)
|
|
description.add('height: $height');
|
|
if (scale != 1.0)
|
|
description.add('scale: $scale');
|
|
if (color != null)
|
|
description.add('color: $color');
|
|
if (colorBlendMode != null)
|
|
description.add('colorBlendMode: $colorBlendMode');
|
|
if (fit != null)
|
|
description.add('fit: $fit');
|
|
if (alignment != null)
|
|
description.add('alignment: $alignment');
|
|
if (repeat != ImageRepeat.noRepeat)
|
|
description.add('repeat: $repeat');
|
|
if (centerSlice != null)
|
|
description.add('centerSlice: $centerSlice');
|
|
}
|
|
}
|
|
|
|
/// A widget that determines the default asset bundle for its descendants.
|
|
///
|
|
/// For example, used by [Image] to determine which bundle to use for
|
|
/// [AssetImage]s if no bundle is specified explicitly.
|
|
class DefaultAssetBundle extends InheritedWidget {
|
|
/// Creates a widget that determines the default asset bundle for its descendants.
|
|
///
|
|
/// The [bundle] and [child] arguments must not be null.
|
|
const DefaultAssetBundle({
|
|
Key key,
|
|
@required this.bundle,
|
|
@required Widget child
|
|
}) : assert(bundle != null),
|
|
assert(child != null),
|
|
super(key: key, child: child);
|
|
|
|
/// The bundle to use as a default.
|
|
final AssetBundle bundle;
|
|
|
|
/// The bundle from the closest instance of this class that encloses
|
|
/// the given context.
|
|
///
|
|
/// If there is no [DefaultAssetBundle] ancestor widget in the tree
|
|
/// at the given context, then this will return the [rootBundle].
|
|
///
|
|
/// Typical usage is as follows:
|
|
///
|
|
/// ```dart
|
|
/// AssetBundle bundle = DefaultAssetBundle.of(context);
|
|
/// ```
|
|
static AssetBundle of(BuildContext context) {
|
|
final DefaultAssetBundle result = context.inheritFromWidgetOfExactType(DefaultAssetBundle);
|
|
return result?.bundle ?? rootBundle;
|
|
}
|
|
|
|
@override
|
|
bool updateShouldNotify(DefaultAssetBundle old) => bundle != old.bundle;
|
|
}
|
|
|
|
/// An adapter for placing a specific [RenderBox] in the widget tree.
|
|
///
|
|
/// A given render object can be placed at most once in the widget tree. This
|
|
/// widget enforces that restriction by keying itself using a [GlobalObjectKey]
|
|
/// for the given render object.
|
|
class WidgetToRenderBoxAdapter extends LeafRenderObjectWidget {
|
|
/// Creates an adapter for placing a specific [RenderBox] in the widget tree.
|
|
///
|
|
/// The [renderBox] argument must not be null.
|
|
WidgetToRenderBoxAdapter({
|
|
@required this.renderBox,
|
|
this.onBuild,
|
|
}) : assert(renderBox != null),
|
|
// WidgetToRenderBoxAdapter objects are keyed to their render box. This
|
|
// prevents the widget being used in the widget hierarchy in two different
|
|
// places, which would cause the RenderBox to get inserted in multiple
|
|
// places in the RenderObject tree.
|
|
super(key: new GlobalObjectKey(renderBox));
|
|
|
|
/// The render box to place in the widget tree.
|
|
final RenderBox renderBox;
|
|
|
|
/// Called when it is safe to update the render box and its descendants. If
|
|
/// you update the RenderObject subtree under this widget outside of
|
|
/// invocations of this callback, features like hit-testing will fail as the
|
|
/// tree will be dirty.
|
|
final VoidCallback onBuild;
|
|
|
|
@override
|
|
RenderBox createRenderObject(BuildContext context) => renderBox;
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderBox renderObject) {
|
|
if (onBuild != null)
|
|
onBuild();
|
|
}
|
|
}
|
|
|
|
|
|
// EVENT HANDLING
|
|
|
|
/// A widget that calls callbacks in response to pointer events.
|
|
///
|
|
/// Rather than listening for raw pointer events, consider listening for
|
|
/// higher-level gestures using [GestureDetector].
|
|
///
|
|
/// If it has a child, this widget defers to the child for sizing behavior. If
|
|
/// it does not have a child, it grows to fit the parent instead.
|
|
class Listener extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that forwards point events to callbacks.
|
|
///
|
|
/// The [behavior] argument defaults to [HitTestBehavior.deferToChild].
|
|
const Listener({
|
|
Key key,
|
|
this.onPointerDown,
|
|
this.onPointerMove,
|
|
this.onPointerUp,
|
|
this.onPointerCancel,
|
|
this.behavior: HitTestBehavior.deferToChild,
|
|
Widget child
|
|
}) : assert(behavior != null),
|
|
super(key: key, child: child);
|
|
|
|
/// Called when a pointer comes into contact with the screen at this object.
|
|
final PointerDownEventListener onPointerDown;
|
|
|
|
/// Called when a pointer that triggered an [onPointerDown] changes position.
|
|
final PointerMoveEventListener onPointerMove;
|
|
|
|
/// Called when a pointer that triggered an [onPointerDown] is no longer in contact with the screen.
|
|
final PointerUpEventListener onPointerUp;
|
|
|
|
/// Called when the input from a pointer that triggered an [onPointerDown] is no longer directed towards this receiver.
|
|
final PointerCancelEventListener onPointerCancel;
|
|
|
|
/// How to behave during hit testing.
|
|
final HitTestBehavior behavior;
|
|
|
|
@override
|
|
RenderPointerListener createRenderObject(BuildContext context) => new RenderPointerListener(
|
|
onPointerDown: onPointerDown,
|
|
onPointerMove: onPointerMove,
|
|
onPointerUp: onPointerUp,
|
|
onPointerCancel: onPointerCancel,
|
|
behavior: behavior
|
|
);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderPointerListener renderObject) {
|
|
renderObject
|
|
..onPointerDown = onPointerDown
|
|
..onPointerMove = onPointerMove
|
|
..onPointerUp = onPointerUp
|
|
..onPointerCancel = onPointerCancel
|
|
..behavior = behavior;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
final List<String> listeners = <String>[];
|
|
if (onPointerDown != null)
|
|
listeners.add('down');
|
|
if (onPointerMove != null)
|
|
listeners.add('move');
|
|
if (onPointerUp != null)
|
|
listeners.add('up');
|
|
if (onPointerCancel != null)
|
|
listeners.add('cancel');
|
|
if (listeners.isEmpty)
|
|
listeners.add('<none>');
|
|
description.add('listeners: ${listeners.join(", ")}');
|
|
switch (behavior) {
|
|
case HitTestBehavior.translucent:
|
|
description.add('behavior: translucent');
|
|
break;
|
|
case HitTestBehavior.opaque:
|
|
description.add('behavior: opaque');
|
|
break;
|
|
case HitTestBehavior.deferToChild:
|
|
description.add('behavior: defer-to-child');
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A widget that creates a separate display list for its child.
|
|
///
|
|
/// This widget creates a separate display list for its child, which
|
|
/// can improve performance if the subtree repaints at different times than
|
|
/// the surrounding parts of the tree. Specifically, when the child does not
|
|
/// repaint but its parent does, we can re-use the display list we recorded
|
|
/// previously. Similarly, when the child repaints but the surround tree does
|
|
/// not, we can re-record its display list without re-recording the display list
|
|
/// for the surround tree.
|
|
class RepaintBoundary extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that isolates repaints.
|
|
const RepaintBoundary({ Key key, Widget child }) : super(key: key, child: child);
|
|
|
|
/// Wraps the given child in a [RepaintBoundary].
|
|
///
|
|
/// The key for the [RepaintBoundary] is derived either from the child's key
|
|
/// (if the child has a non-null key) or from the given `childIndex`.
|
|
factory RepaintBoundary.wrap(Widget child, int childIndex) {
|
|
assert(child != null);
|
|
final Key key = child.key != null ? new ValueKey<Key>(child.key) : new ValueKey<int>(childIndex);
|
|
return new RepaintBoundary(key: key, child: child);
|
|
}
|
|
|
|
/// Wraps each of the given children in [RepaintBoundary]s.
|
|
///
|
|
/// The key for each [RepaintBoundary] is derived either from the wrapped
|
|
/// child's key (if the wrapped child has a non-null key) or from the wrapped
|
|
/// child's index in the list.
|
|
static List<RepaintBoundary> wrapAll(List<Widget> widgets) {
|
|
final List<RepaintBoundary> result = new List<RepaintBoundary>(widgets.length);
|
|
for (int i = 0; i < result.length; ++i)
|
|
result[i] = new RepaintBoundary.wrap(widgets[i], i);
|
|
return result;
|
|
}
|
|
|
|
@override
|
|
RenderRepaintBoundary createRenderObject(BuildContext context) => new RenderRepaintBoundary();
|
|
}
|
|
|
|
/// A widget that is invisible during hit testing.
|
|
///
|
|
/// When [ignoring] is true, this widget (and its subtree) is invisible
|
|
/// to hit testing. It still consumes space during layout and paints its child
|
|
/// as usual. It just cannot be the target of located events, because it returns
|
|
/// false from [RenderBox.hitTest].
|
|
///
|
|
/// When [ignoringSemantics] is true, the subtree will be invisible to
|
|
/// the semantics layer (and thus e.g. accessibility tools). If
|
|
/// [ignoringSemantics] is null, it uses the value of [ignoring].
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [AbsorbPointer], which also prevents its children from receiving pointer
|
|
/// events but is itself visible to hit testing.
|
|
class IgnorePointer extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that is invisible to hit testing.
|
|
///
|
|
/// The [ignoring] argument must not be null. If [ignoringSemantics], this
|
|
/// render object will be ignored for semantics if [ignoring] is true.
|
|
const IgnorePointer({
|
|
Key key,
|
|
this.ignoring: true,
|
|
this.ignoringSemantics,
|
|
Widget child
|
|
}) : assert(ignoring != null),
|
|
super(key: key, child: child);
|
|
|
|
/// Whether this widget is ignored during hit testing.
|
|
///
|
|
/// Regardless of whether this widget is ignored during hit testing, it will
|
|
/// still consume space during layout and be visible during painting.
|
|
final bool ignoring;
|
|
|
|
/// Whether the semantics of this widget is ignored when compiling the semantics tree.
|
|
///
|
|
/// If null, defaults to value of [ignoring].
|
|
///
|
|
/// See [SemanticsNode] for additional information about the semantics tree.
|
|
final bool ignoringSemantics;
|
|
|
|
@override
|
|
RenderIgnorePointer createRenderObject(BuildContext context) => new RenderIgnorePointer(
|
|
ignoring: ignoring,
|
|
ignoringSemantics: ignoringSemantics
|
|
);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderIgnorePointer renderObject) {
|
|
renderObject
|
|
..ignoring = ignoring
|
|
..ignoringSemantics = ignoringSemantics;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('ignoring: $ignoring');
|
|
if (ignoringSemantics != null)
|
|
description.add('ignoringSemantics: $ignoringSemantics');
|
|
}
|
|
}
|
|
|
|
/// A widget that absorbs pointers during hit testing.
|
|
///
|
|
/// When [absorbing] is true, this widget prevents its subtree from receiving
|
|
/// pointer events by terminating hit testing at itself. It still consumes space
|
|
/// during layout and paints its child as usual. It just prevents its children
|
|
/// from being the target of located events, because it returns true from
|
|
/// [RenderBox.hitTest].
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [IgnorePointer], which also prevents its children from receiving pointer
|
|
/// events but is itself invisible to hit testing.
|
|
class AbsorbPointer extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that absorbs pointers during hit testing.
|
|
///
|
|
/// The [absorbing] argument must not be null
|
|
const AbsorbPointer({
|
|
Key key,
|
|
this.absorbing: true,
|
|
Widget child
|
|
}) : assert(absorbing != null),
|
|
super(key: key, child: child);
|
|
|
|
/// Whether this widget absorbs pointers during hit testing.
|
|
///
|
|
/// Regardless of whether this render object absorbs pointers during hit
|
|
/// testing, it will still consume space during layout and be visible during
|
|
/// painting.
|
|
final bool absorbing;
|
|
|
|
@override
|
|
RenderAbsorbPointer createRenderObject(BuildContext context) => new RenderAbsorbPointer(absorbing: absorbing);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderAbsorbPointer renderObject) {
|
|
renderObject.absorbing = absorbing;
|
|
}
|
|
}
|
|
|
|
/// Holds opaque meta data in the render tree.
|
|
///
|
|
/// Useful for decorating the render tree with information that will be consumed
|
|
/// later. For example, you could store information in the render tree that will
|
|
/// be used when the user interacts with the render tree but has no visual
|
|
/// impact prior to the interaction.
|
|
class MetaData extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that hold opaque meta data.
|
|
///
|
|
/// The [behavior] argument defaults to [HitTestBehavior.deferToChild].
|
|
const MetaData({
|
|
Key key,
|
|
this.metaData,
|
|
this.behavior: HitTestBehavior.deferToChild,
|
|
Widget child
|
|
}) : super(key: key, child: child);
|
|
|
|
/// Opaque meta data ignored by the render tree
|
|
final dynamic metaData;
|
|
|
|
/// How to behave during hit testing.
|
|
final HitTestBehavior behavior;
|
|
|
|
@override
|
|
RenderMetaData createRenderObject(BuildContext context) => new RenderMetaData(
|
|
metaData: metaData,
|
|
behavior: behavior
|
|
);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderMetaData renderObject) {
|
|
renderObject
|
|
..metaData = metaData
|
|
..behavior = behavior;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('behavior: $behavior');
|
|
description.add('metaData: $metaData');
|
|
}
|
|
}
|
|
|
|
|
|
// UTILITY NODES
|
|
|
|
/// A widget that annotates the widget tree with a description of the meaning of
|
|
/// the widgets.
|
|
///
|
|
/// Used by accessibility tools, search engines, and other semantic analysis
|
|
/// software to determine the meaning of the application.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [MergeSemantics], which marks a subtree as being a single node for
|
|
/// accessibility purposes.
|
|
/// * [ExcludeSemantics], which excludes a subtree from the semantics tree
|
|
/// (which might be useful if it is, e.g., totally decorative and not
|
|
/// important to the user).
|
|
/// * [RenderObject.semanticsAnnotator], the rendering library API through which
|
|
/// the [Semantics] widget is actually implemented.
|
|
/// * [SemanticsNode], the object used by the rendering library to represent
|
|
/// semantics in the semantics tree.
|
|
/// * [SemanticsDebugger], an overlay to help visualize the semantics tree. Can
|
|
/// be enabled using [WidgetsApp.showSemanticsDebugger] or
|
|
/// [MaterialApp.showSemanticsDebugger].
|
|
class Semantics extends SingleChildRenderObjectWidget {
|
|
/// Creates a semantic annotation.
|
|
///
|
|
/// The [container] argument must not be null.
|
|
const Semantics({
|
|
Key key,
|
|
Widget child,
|
|
this.container: false,
|
|
this.checked,
|
|
this.label
|
|
}) : assert(container != null),
|
|
super(key: key, child: child);
|
|
|
|
/// If 'container' is true, this Widget will introduce a new node in
|
|
/// the semantics tree. Otherwise, the semantics will be merged with
|
|
/// the semantics of any ancestors.
|
|
///
|
|
/// The 'container' flag is implicitly set to true on the immediate
|
|
/// semantics-providing descendants of a node where multiple
|
|
/// children have semantics or have descendants providing semantics.
|
|
/// In other words, the semantics of siblings are not merged. To
|
|
/// merge the semantics of an entire subtree, including siblings,
|
|
/// you can use a [MergeSemantics] widget.
|
|
final bool container;
|
|
|
|
/// If non-null, indicates that this subtree represents a checkbox
|
|
/// or similar widget with a "checked" state, and what its current
|
|
/// state is.
|
|
final bool checked;
|
|
|
|
/// Provides a textual description of the widget.
|
|
final String label;
|
|
|
|
@override
|
|
RenderSemanticsAnnotations createRenderObject(BuildContext context) => new RenderSemanticsAnnotations(
|
|
container: container,
|
|
checked: checked,
|
|
label: label
|
|
);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderSemanticsAnnotations renderObject) {
|
|
renderObject
|
|
..container = container
|
|
..checked = checked
|
|
..label = label;
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('container: $container');
|
|
if (checked != null)
|
|
description.add('checked: $checked');
|
|
if (label != null)
|
|
description.add('label: "$label"');
|
|
}
|
|
}
|
|
|
|
/// A widget that merges the semantics of its descendants.
|
|
///
|
|
/// Causes all the semantics of the subtree rooted at this node to be
|
|
/// merged into one node in the semantics tree. For example, if you
|
|
/// have a widget with a Text node next to a checkbox widget, this
|
|
/// could be used to merge the label from the Text node with the
|
|
/// "checked" semantic state of the checkbox into a single node that
|
|
/// had both the label and the checked state. Otherwise, the label
|
|
/// would be presented as a separate feature than the checkbox, and
|
|
/// the user would not be able to be sure that they were related.
|
|
///
|
|
/// Be aware that if two nodes in the subtree have conflicting
|
|
/// semantics, the result may be nonsensical. For example, a subtree
|
|
/// with a checked checkbox and an unchecked checkbox will be
|
|
/// presented as checked. All the labels will be merged into a single
|
|
/// string (with newlines separating each label from the other). If
|
|
/// multiple nodes in the merged subtree can handle semantic gestures,
|
|
/// the first one in tree order will be the one to receive the
|
|
/// callbacks.
|
|
class MergeSemantics extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that merges the semantics of its descendants.
|
|
const MergeSemantics({ Key key, Widget child }) : super(key: key, child: child);
|
|
|
|
@override
|
|
RenderMergeSemantics createRenderObject(BuildContext context) => new RenderMergeSemantics();
|
|
}
|
|
|
|
/// A widget that drops all the semantics of its descendants.
|
|
///
|
|
/// This can be used to hide subwidgets that would otherwise be
|
|
/// reported but that would only be confusing. For example, the
|
|
/// material library's [Chip] widget hides the avatar since it is
|
|
/// redundant with the chip label.
|
|
class ExcludeSemantics extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that drops all the semantics of its descendants.
|
|
const ExcludeSemantics({ Key key, Widget child }) : super(key: key, child: child);
|
|
|
|
@override
|
|
RenderExcludeSemantics createRenderObject(BuildContext context) => new RenderExcludeSemantics();
|
|
}
|
|
|
|
/// A widget that builds its child.
|
|
///
|
|
/// Useful for attaching a key to an existing widget.
|
|
class KeyedSubtree extends StatelessWidget {
|
|
/// Creates a widget that builds its child.
|
|
const KeyedSubtree({
|
|
Key key,
|
|
@required this.child
|
|
}) : assert(child != null),
|
|
super(key: key);
|
|
|
|
/// The widget below this widget in the tree.
|
|
final Widget child;
|
|
|
|
/// Creates a KeyedSubtree for child with a key that's based on the child's existing key or childIndex.
|
|
factory KeyedSubtree.wrap(Widget child, int childIndex) {
|
|
final Key key = child.key != null ? new ValueKey<Key>(child.key) : new ValueKey<int>(childIndex);
|
|
return new KeyedSubtree(key: key, child: child);
|
|
}
|
|
|
|
/// Wrap each item in a KeyedSubtree whose key is based on the item's existing key or
|
|
/// the sum of its list index and `baseIndex`.
|
|
static List<Widget> ensureUniqueKeysForList(Iterable<Widget> items, { int baseIndex: 0 }) {
|
|
if (items == null || items.isEmpty)
|
|
return items;
|
|
|
|
final List<Widget> itemsWithUniqueKeys = <Widget>[];
|
|
int itemIndex = baseIndex;
|
|
for (Widget item in items) {
|
|
itemsWithUniqueKeys.add(new KeyedSubtree.wrap(item, itemIndex));
|
|
itemIndex += 1;
|
|
}
|
|
|
|
assert(!debugItemsHaveDuplicateKeys(itemsWithUniqueKeys));
|
|
return itemsWithUniqueKeys;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) => child;
|
|
}
|
|
|
|
/// A platonic widget that calls a closure to obtain its child widget.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [StatefulBuilder], a platonic widget which also has state.
|
|
class Builder extends StatelessWidget {
|
|
/// Creates a widget that delegates its build to a callback.
|
|
///
|
|
/// The [builder] argument must not be null.
|
|
const Builder({
|
|
Key key,
|
|
@required this.builder
|
|
}) : assert(builder != null),
|
|
super(key: key);
|
|
|
|
/// Called to obtain the child widget.
|
|
///
|
|
/// This function is called whenever this widget is included in its parent's
|
|
/// build and the old widget (if any) that it synchronizes with has a distinct
|
|
/// object identity. Typically the parent's build method will construct
|
|
/// a new tree of widgets and so a new Builder child will not be [identical]
|
|
/// to the corresponding old one.
|
|
final WidgetBuilder builder;
|
|
|
|
@override
|
|
Widget build(BuildContext context) => builder(context);
|
|
}
|
|
|
|
/// Signature for the builder callback used by [StatefulBuilder].
|
|
///
|
|
/// Call [setState] to schedule the [StatefulBuilder] to rebuild.
|
|
typedef Widget StatefulWidgetBuilder(BuildContext context, StateSetter setState);
|
|
|
|
/// A platonic widget that both has state and calls a closure to obtain its child widget.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [Builder], the platonic stateless widget.
|
|
class StatefulBuilder extends StatefulWidget {
|
|
/// Creates a widget that both has state and delegates its build to a callback.
|
|
///
|
|
/// The [builder] argument must not be null.
|
|
const StatefulBuilder({
|
|
Key key,
|
|
@required this.builder
|
|
}) : assert(builder != null),
|
|
super(key: key);
|
|
|
|
/// Called to obtain the child widget.
|
|
///
|
|
/// This function is called whenever this widget is included in its parent's
|
|
/// build and the old widget (if any) that it synchronizes with has a distinct
|
|
/// object identity. Typically the parent's build method will construct
|
|
/// a new tree of widgets and so a new Builder child will not be [identical]
|
|
/// to the corresponding old one.
|
|
final StatefulWidgetBuilder builder;
|
|
|
|
@override
|
|
_StatefulBuilderState createState() => new _StatefulBuilderState();
|
|
}
|
|
|
|
class _StatefulBuilderState extends State<StatefulBuilder> {
|
|
@override
|
|
Widget build(BuildContext context) => widget.builder(context, setState);
|
|
}
|