mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
3194 lines
112 KiB
Dart
3194 lines
112 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/painting.dart';
|
|
export 'package:flutter/rendering.dart' show
|
|
Axis,
|
|
BoxConstraints,
|
|
CrossAxisAlignment,
|
|
CustomClipper,
|
|
CustomPainter,
|
|
FixedColumnCountGridDelegate,
|
|
FlexFit,
|
|
FlowDelegate,
|
|
FlowPaintingContext,
|
|
FractionalOffsetTween,
|
|
GridChildPlacement,
|
|
GridDelegate,
|
|
GridDelegateWithInOrderChildPlacement,
|
|
GridSpecification,
|
|
HitTestBehavior,
|
|
MainAxisAlignment,
|
|
MainAxisSize,
|
|
MaxTileWidthGridDelegate,
|
|
MultiChildLayoutDelegate,
|
|
Overflow,
|
|
PaintingContext,
|
|
PointerCancelEvent,
|
|
PointerCancelEventListener,
|
|
PointerDownEvent,
|
|
PointerDownEventListener,
|
|
PointerEvent,
|
|
PointerMoveEvent,
|
|
PointerMoveEventListener,
|
|
PointerUpEvent,
|
|
PointerUpEventListener,
|
|
RelativeRect,
|
|
ShaderCallback,
|
|
SingleChildLayoutDelegate,
|
|
TextOverflow,
|
|
ValueChanged,
|
|
ValueGetter,
|
|
ViewportAnchor,
|
|
ViewportDimensions,
|
|
ViewportDimensionsChangeCallback;
|
|
|
|
// 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).
|
|
Opacity({
|
|
Key key,
|
|
@required this.opacity,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(opacity != null && opacity >= 0.0 && opacity <= 1.0);
|
|
}
|
|
|
|
/// 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 [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 [transferMode] arguments must not be null.
|
|
ShaderMask({
|
|
Key key,
|
|
@required this.shaderCallback,
|
|
this.transferMode: TransferMode.modulate,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(shaderCallback != null);
|
|
assert(transferMode != null);
|
|
}
|
|
|
|
/// 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 [TransferMode] to use when applying the shader to the child.
|
|
///
|
|
/// The default, [TransferMode.modulate], is useful for applying an alpha blend
|
|
/// to the child. Other transfer modes can be used to create other effects.
|
|
final TransferMode transferMode;
|
|
|
|
@override
|
|
RenderShaderMask createRenderObject(BuildContext context) {
|
|
return new RenderShaderMask(
|
|
shaderCallback: shaderCallback,
|
|
transferMode: transferMode
|
|
);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderShaderMask renderObject) {
|
|
renderObject
|
|
..shaderCallback = shaderCallback
|
|
..transferMode = transferMode;
|
|
}
|
|
}
|
|
|
|
/// 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.
|
|
BackdropFilter({
|
|
Key key,
|
|
@required this.filter,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(filter != null);
|
|
}
|
|
|
|
/// The image filter to apply to the existing painted content before painting the child.
|
|
///
|
|
/// For example, consider using [ui.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].
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [CustomPainter].
|
|
/// * [Canvas].
|
|
class CustomPaint extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that delegates its painting.
|
|
CustomPaint({ Key key, this.painter, this.foregroundPainter, this.size: Size.zero, Widget child })
|
|
: super(key: key, child: child) {
|
|
assert(size != null);
|
|
}
|
|
|
|
/// 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].
|
|
class ClipRect extends SingleChildRenderObjectWidget {
|
|
/// Creates a rectangular clip.
|
|
///
|
|
/// If [clipper] is null, the clip will match the layout size and position of
|
|
/// the child.
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// 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].
|
|
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.
|
|
ClipRRect({
|
|
Key key,
|
|
this.borderRadius,
|
|
this.clipper,
|
|
Widget child,
|
|
}) : super(key: key, child: child) {
|
|
assert(borderRadius != null || clipper != null);
|
|
}
|
|
|
|
/// 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;
|
|
}
|
|
}
|
|
|
|
/// 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].
|
|
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.
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// 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.
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
// 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.
|
|
Transform({
|
|
Key key,
|
|
@required this.transform,
|
|
this.origin,
|
|
this.alignment,
|
|
this.transformHitTests: true,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(transform != null);
|
|
}
|
|
|
|
/// 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.
|
|
FittedBox({
|
|
Key key,
|
|
this.fit: ImageFit.contain,
|
|
this.alignment: FractionalOffset.center,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(fit != null);
|
|
assert(alignment != null && alignment.dx != null && alignment.dy != null);
|
|
}
|
|
|
|
/// How to inscribe the child into the space allocated during layout.
|
|
final ImageFit 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.
|
|
FractionalTranslation({
|
|
Key key,
|
|
@required this.translation,
|
|
this.transformHitTests: true,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(translation != null);
|
|
}
|
|
|
|
/// 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.
|
|
RotatedBox({
|
|
Key key,
|
|
@required this.quarterTurns,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(quarterTurns != null);
|
|
}
|
|
|
|
/// 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.
|
|
class Padding extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that insets its child.
|
|
///
|
|
/// The [padding] argument must not be null.
|
|
Padding({
|
|
Key key,
|
|
@required this.padding,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(padding != null);
|
|
}
|
|
|
|
/// 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;
|
|
}
|
|
}
|
|
|
|
/// 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].
|
|
Align({
|
|
Key key,
|
|
this.alignment: FractionalOffset.center,
|
|
this.widthFactor,
|
|
this.heightFactor,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(alignment != null && alignment.dx != null && alignment.dy != null);
|
|
assert(widthFactor == null || widthFactor >= 0.0);
|
|
assert(heightFactor == null || heightFactor >= 0.0);
|
|
}
|
|
|
|
/// 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.
|
|
///
|
|
/// 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.
|
|
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.
|
|
CustomSingleChildLayout({
|
|
Key key,
|
|
@required this.delegate,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(delegate != null);
|
|
}
|
|
|
|
/// 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 Object id,
|
|
@required Widget child
|
|
}) : id = id, super(key: key ?? new ValueKey<Object>(id), child: child) {
|
|
assert(child != null);
|
|
assert(id != null);
|
|
}
|
|
|
|
/// 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;
|
|
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>[],
|
|
}) : super(key: key, children: children) {
|
|
assert(delegate != null);
|
|
}
|
|
|
|
/// 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 box of a specific size.
|
|
const SizedBox({ Key key, this.width, this.height, Widget child })
|
|
: super(key: key, child: child);
|
|
|
|
const SizedBox.expand({ Key key, Widget child })
|
|
: width = double.INFINITY,
|
|
height = double.INFINITY,
|
|
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() {
|
|
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
|
|
/// [additionalConstraints].
|
|
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
|
|
}) : super(key: key, child: child) {
|
|
assert(constraints != null);
|
|
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.
|
|
FractionallySizedBox({
|
|
Key key,
|
|
this.alignment: FractionalOffset.center,
|
|
this.widthFactor,
|
|
this.heightFactor,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(alignment != null && alignment.dx != null && alignment.dy != null);
|
|
assert(widthFactor == null || widthFactor >= 0.0);
|
|
assert(heightFactor == null || heightFactor >= 0.0);
|
|
}
|
|
|
|
/// 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.
|
|
LimitedBox({
|
|
Key key,
|
|
this.maxWidth: double.INFINITY,
|
|
this.maxHeight: double.INFINITY,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(maxWidth != null && maxWidth >= 0.0);
|
|
assert(maxHeight != null && maxHeight >= 0.0);
|
|
}
|
|
|
|
/// 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 [RenderOverflowBox] for details.
|
|
class OverflowBox extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that lets its child overflow itself.
|
|
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.
|
|
SizedOverflowBox({
|
|
Key key,
|
|
@required this.size,
|
|
this.alignment: FractionalOffset.center,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(size != null);
|
|
assert(alignment != null && alignment.dx != null && alignment.dy != null);
|
|
}
|
|
|
|
/// 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.
|
|
Offstage({ Key key, this.offstage: true, Widget child })
|
|
: super(key: key, child: child) {
|
|
assert(offstage != null);
|
|
}
|
|
|
|
/// 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.
|
|
AspectRatio({
|
|
Key key,
|
|
@required this.aspectRatio,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(aspectRatio != null);
|
|
}
|
|
|
|
/// 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.
|
|
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.
|
|
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.
|
|
Baseline({
|
|
Key key,
|
|
@required this.baseline,
|
|
@required this.baselineType,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(baseline != null);
|
|
assert(baselineType != null);
|
|
}
|
|
|
|
/// 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;
|
|
}
|
|
}
|
|
|
|
/// A widget that's bigger on the inside.
|
|
///
|
|
/// The child of a viewport can layout to a larger size along the viewport's
|
|
/// [mainAxis] than the viewport itself. If that happens, only a portion of the
|
|
/// child will be visible through the viewport. The portion of the child that is
|
|
/// visible is controlled by the scroll offset.
|
|
///
|
|
/// Viewport is the core scrolling primitive in the system, but it can be used
|
|
/// in other situations.
|
|
class Viewport extends SingleChildRenderObjectWidget {
|
|
/// Creates a widget that's bigger on the inside.
|
|
///
|
|
/// The [mainAxis] and [paintOffset] arguments must not be null.
|
|
Viewport({
|
|
Key key,
|
|
this.paintOffset: Offset.zero,
|
|
this.mainAxis: Axis.vertical,
|
|
this.anchor: ViewportAnchor.start,
|
|
this.onPaintOffsetUpdateNeeded,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(mainAxis != null);
|
|
assert(paintOffset != null);
|
|
}
|
|
|
|
/// The offset at which to paint the child.
|
|
///
|
|
/// The offset can be non-zero only in the [mainAxis].
|
|
final Offset paintOffset;
|
|
|
|
/// The direction in which the child is permitted to be larger than the viewport.
|
|
///
|
|
/// The child is given layout constraints that are fully unconstrained along
|
|
/// the main axis (e.g., the child can be as tall as it wants if the main axis
|
|
/// is vertical).
|
|
final Axis mainAxis;
|
|
|
|
/// The end of the viewport from which the paint offset is computed.
|
|
///
|
|
/// See [ViewportAnchor] for more detail.
|
|
final ViewportAnchor anchor;
|
|
|
|
/// Called when the interior or exterior dimensions of the viewport change.
|
|
final ViewportDimensionsChangeCallback onPaintOffsetUpdateNeeded;
|
|
|
|
@override
|
|
RenderViewport createRenderObject(BuildContext context) {
|
|
return new RenderViewport(
|
|
paintOffset: paintOffset,
|
|
mainAxis: mainAxis,
|
|
anchor: anchor,
|
|
onPaintOffsetUpdateNeeded: onPaintOffsetUpdateNeeded
|
|
);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderViewport renderObject) {
|
|
// Order dependency: RenderViewport validates scrollOffset based on mainAxis.
|
|
renderObject
|
|
..mainAxis = mainAxis
|
|
..anchor = anchor
|
|
..paintOffset = paintOffset
|
|
..onPaintOffsetUpdateNeeded = onPaintOffsetUpdateNeeded;
|
|
}
|
|
}
|
|
|
|
|
|
// LAYOUT NODES
|
|
|
|
/// A widget that uses the block layout algorithm for its children.
|
|
///
|
|
/// This widget is rarely used directly. Instead, consider using [Block], which
|
|
/// combines the block layout algorithm with scrolling behavior.
|
|
///
|
|
/// For details about the block layout algorithm, see [RenderBlockBase].
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [Block], which combines block layout with scrolling.
|
|
class BlockBody extends MultiChildRenderObjectWidget {
|
|
/// Creates a block layout widget.
|
|
///
|
|
/// By default, the [mainAxis] is [Axis.vertical].
|
|
BlockBody({
|
|
Key key,
|
|
this.mainAxis: Axis.vertical,
|
|
List<Widget> children: const <Widget>[],
|
|
}) : super(key: key, children: children) {
|
|
assert(mainAxis != null);
|
|
}
|
|
|
|
/// The direction to use as the main axis.
|
|
final Axis mainAxis;
|
|
|
|
@override
|
|
RenderBlock createRenderObject(BuildContext context) => new RenderBlock(mainAxis: mainAxis);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderBlock 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.
|
|
///
|
|
/// 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.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;
|
|
|
|
/// Whether overflowing children should be clipped. See [Overflow].
|
|
///
|
|
/// Some children in a stack might overflow its box. When this flag is set to
|
|
/// [Overflow.clipped], children cannot paint outside of the stack's box.
|
|
final Overflow overflow;
|
|
|
|
@override
|
|
RenderStack createRenderObject(BuildContext context) {
|
|
return new RenderStack(
|
|
alignment: alignment,
|
|
overflow: overflow
|
|
);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderStack renderObject) {
|
|
renderObject
|
|
..alignment = alignment
|
|
..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,
|
|
this.index: 0,
|
|
List<Widget> children: const <Widget>[],
|
|
}) : super(key: key, alignment: alignment, 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.
|
|
Positioned({
|
|
Key key,
|
|
this.left,
|
|
this.top,
|
|
this.right,
|
|
this.bottom,
|
|
this.width,
|
|
this.height,
|
|
@required Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(left == null || right == null || width == null);
|
|
assert(top == null || bottom == null || height == null);
|
|
}
|
|
|
|
/// 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,
|
|
Widget child,
|
|
Rect rect
|
|
}) : 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 [left], [top], [right], and [bottom] set
|
|
/// to 0.0 unless a value for them is passed.
|
|
Positioned.fill({
|
|
Key key,
|
|
Widget child,
|
|
this.left: 0.0,
|
|
this.top: 0.0,
|
|
this.right: 0.0,
|
|
this.bottom: 0.0
|
|
}) : 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) {
|
|
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 [MultiChildRenderObjectWidget] for use with [RenderGrid].
|
|
///
|
|
/// Typically not used directly. Instead, consider using one of its subclasses,
|
|
/// such as:
|
|
///
|
|
/// * [FixedColumnCountGrid] (which creates a grid with a fixed number of columns)
|
|
/// * [MaxTileWidthGrid] (which creates a grid whose tiles have a maximum width)
|
|
/// * [CustomGrid] (which lets you supply your own [GridDelegate])
|
|
abstract class GridRenderObjectWidget extends MultiChildRenderObjectWidget {
|
|
/// Initializes fields for subclasses.
|
|
GridRenderObjectWidget({
|
|
Key key,
|
|
List<Widget> children: const <Widget>[],
|
|
}) : super(key: key, children: children) {
|
|
_delegate = createDelegate();
|
|
}
|
|
|
|
GridDelegate _delegate;
|
|
|
|
/// The delegate that controls the layout of the children.
|
|
GridDelegate createDelegate();
|
|
|
|
@override
|
|
RenderGrid createRenderObject(BuildContext context) => new RenderGrid(delegate: _delegate);
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderGrid renderObject) {
|
|
renderObject.delegate = _delegate;
|
|
}
|
|
}
|
|
|
|
/// A widget that creates a grid using a custom [delegate].
|
|
///
|
|
/// For details about the grid layout algorithm, see [RenderGrid].
|
|
///
|
|
/// To pass data to your delegate's [GridDelegate.getChildPlacement] function,
|
|
/// consider using [GridPlacementData].
|
|
class CustomGrid extends GridRenderObjectWidget {
|
|
/// Creates a grid with a custom [delegate].
|
|
///
|
|
/// The [delegate] argument must not be null.
|
|
CustomGrid({
|
|
Key key,
|
|
@required this.delegate,
|
|
List<Widget> children: const <Widget>[],
|
|
}) : super(key: key, children: children) {
|
|
assert(delegate != null);
|
|
}
|
|
|
|
/// The delegate that controls the layout of the children.
|
|
///
|
|
/// For example, a [FixedColumnCountGridDelegate] for grids that have a fixed
|
|
/// number of columns or a [MaxTileWidthGridDelegate] for grids that have a
|
|
/// maximum tile width.
|
|
final GridDelegate delegate;
|
|
|
|
@override
|
|
GridDelegate createDelegate() => delegate;
|
|
}
|
|
|
|
/// A widget that uses a grid layout with a fixed column count.
|
|
///
|
|
/// For details about the grid layout algorithm, see [FixedColumnCountGridDelegate].
|
|
class FixedColumnCountGrid extends GridRenderObjectWidget {
|
|
/// Creates a grid with a fixed number of columns.
|
|
///
|
|
/// The [columnCount] argument must not be negative.
|
|
FixedColumnCountGrid({
|
|
Key key,
|
|
@required this.columnCount,
|
|
this.columnSpacing: 0.0,
|
|
this.rowSpacing: 0.0,
|
|
this.tileAspectRatio: 1.0,
|
|
this.padding: EdgeInsets.zero,
|
|
List<Widget> children: const <Widget>[],
|
|
}) : super(key: key, children: children) {
|
|
assert(columnCount != null && columnCount >= 0);
|
|
assert(tileAspectRatio != null && tileAspectRatio > 0.0);
|
|
}
|
|
|
|
/// The number of columns in the grid.
|
|
final int columnCount;
|
|
|
|
/// The horizontal distance between columns.
|
|
final double columnSpacing;
|
|
|
|
/// The vertical distance between rows.
|
|
final double rowSpacing;
|
|
|
|
/// The ratio of the width to the height of each tile in the grid.
|
|
final double tileAspectRatio;
|
|
|
|
/// The amount of padding to apply to each child.
|
|
final EdgeInsets padding;
|
|
|
|
@override
|
|
FixedColumnCountGridDelegate createDelegate() {
|
|
return new FixedColumnCountGridDelegate(
|
|
columnCount: columnCount,
|
|
columnSpacing: columnSpacing,
|
|
rowSpacing: rowSpacing,
|
|
tileAspectRatio: tileAspectRatio,
|
|
padding: padding
|
|
);
|
|
}
|
|
}
|
|
|
|
/// A widget that uses a grid layout with a maximum tile width.
|
|
///
|
|
/// For details about the grid layout algorithm, see [MaxTileWidthGridDelegate].
|
|
class MaxTileWidthGrid extends GridRenderObjectWidget {
|
|
/// Creates a grid with a maximum tile width.
|
|
///
|
|
/// The [columnCount] argument must not be negative.
|
|
MaxTileWidthGrid({
|
|
Key key,
|
|
@required this.maxTileWidth,
|
|
this.columnSpacing: 0.0,
|
|
this.rowSpacing: 0.0,
|
|
this.tileAspectRatio: 1.0,
|
|
this.padding: EdgeInsets.zero,
|
|
List<Widget> children: const <Widget>[],
|
|
}) : super(key: key, children: children) {
|
|
assert(maxTileWidth != null && maxTileWidth >= 0.0);
|
|
assert(tileAspectRatio != null && tileAspectRatio > 0.0);
|
|
}
|
|
|
|
/// The maximum width of a tile in the grid.
|
|
final double maxTileWidth;
|
|
|
|
/// The ratio of the width to the height of each tile in the grid.
|
|
final double tileAspectRatio;
|
|
|
|
/// The horizontal distance between columns.
|
|
final double columnSpacing;
|
|
|
|
/// The vertical distance between rows.
|
|
final double rowSpacing;
|
|
|
|
/// The amount of padding to apply to each child.
|
|
final EdgeInsets padding;
|
|
|
|
@override
|
|
MaxTileWidthGridDelegate createDelegate() {
|
|
return new MaxTileWidthGridDelegate(
|
|
maxTileWidth: maxTileWidth,
|
|
tileAspectRatio: tileAspectRatio,
|
|
columnSpacing: columnSpacing,
|
|
rowSpacing: rowSpacing,
|
|
padding: padding
|
|
);
|
|
}
|
|
}
|
|
|
|
/// A widget that controls the placement of a child in a grid.
|
|
///
|
|
/// The [placementData] this widget associates with the child is interpreted by
|
|
/// the grid's [GridDelegate] in its [GridDelegate.getChildPlacement] function.
|
|
/// During layout, the grid calls the delegate's
|
|
/// [GridDelegate.getChildPlacement] function for each child, passing the
|
|
/// [placementData] associated with that child as context. The return value of
|
|
/// [GridDelegate.getChildPlacement] is then used to determine the size and
|
|
/// position of that child within the grid.
|
|
///
|
|
/// A [GridPlacementData] widget must be a descendant of a
|
|
/// [GridRenderObjectWidget], and the path from the [GridPlacementData] widget
|
|
/// to its enclosing [GridRenderObjectWidget] must contain only
|
|
/// [StatelessWidget]s or [StatefulWidget]s (not other kinds of widgets, like
|
|
/// [RenderObjectWidget]s).
|
|
class GridPlacementData<DataType> extends ParentDataWidget<GridRenderObjectWidget> {
|
|
/// Creates a widget that controls placement of a child in a grid.
|
|
///
|
|
/// The [placementData] and [child] arguments are required.
|
|
GridPlacementData({
|
|
Key key,
|
|
@required this.placementData,
|
|
@required Widget child
|
|
}) : super(key: key, child: child);
|
|
|
|
/// Opaque data passed to the getChildPlacement method of the grid's [GridDelegate].
|
|
final DataType placementData;
|
|
|
|
@override
|
|
void applyParentData(RenderObject renderObject) {
|
|
assert(renderObject.parentData is GridParentData);
|
|
final GridParentData parentData = renderObject.parentData;
|
|
if (parentData.placementData != placementData) {
|
|
parentData.placementData = placementData;
|
|
AbstractNode targetParent = renderObject.parent;
|
|
if (targetParent is RenderObject)
|
|
targetParent.markNeedsLayout();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void debugFillDescription(List<String> description) {
|
|
super.debugFillDescription(description);
|
|
description.add('placementData: $placementData');
|
|
}
|
|
}
|
|
|
|
/// 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 [Block].
|
|
///
|
|
/// 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>[],
|
|
}) : super(key: key, children: children) {
|
|
assert(direction != null);
|
|
assert(mainAxisAlignment != null);
|
|
assert(mainAxisSize != null);
|
|
assert(crossAxisAlignment != null);
|
|
assert(crossAxisAlignment != CrossAxisAlignment.baseline || textBaseline != null);
|
|
}
|
|
|
|
/// 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, 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 [Block].
|
|
///
|
|
/// 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 [Block].
|
|
///
|
|
/// 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.
|
|
///
|
|
/// 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).
|
|
class Flexible extends ParentDataWidget<Flex> {
|
|
/// Creates a widget that controls how a child of a [Row], [Column], or [Flex]
|
|
/// flexes.
|
|
Flexible({
|
|
Key key,
|
|
this.flex: 1,
|
|
this.fit: FlexFit.tight,
|
|
@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) {
|
|
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 to make 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 [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).
|
|
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.
|
|
Expanded({
|
|
Key key,
|
|
int flex: 1,
|
|
@required Widget child,
|
|
}) : super(key: key, flex: flex, fit: FlexFit.tight, child: child) {
|
|
assert(flex > 0);
|
|
}
|
|
}
|
|
|
|
/// 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>[],
|
|
}) : super(key: key, children: RepaintBoundary.wrapAll(children)) {
|
|
assert(delegate != null);
|
|
}
|
|
|
|
/// 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,
|
|
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.
|
|
RichText({
|
|
Key key,
|
|
@required this.text,
|
|
this.textAlign,
|
|
this.softWrap: true,
|
|
this.overflow: TextOverflow.clip,
|
|
this.textScaleFactor: 1.0
|
|
}) : super(key: key) {
|
|
assert(text != null);
|
|
assert(softWrap != null);
|
|
assert(overflow != null);
|
|
assert(textScaleFactor != null);
|
|
}
|
|
|
|
/// 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;
|
|
|
|
@override
|
|
RenderParagraph createRenderObject(BuildContext context) {
|
|
return new RenderParagraph(text,
|
|
textAlign: textAlign,
|
|
softWrap: softWrap,
|
|
overflow: overflow,
|
|
textScaleFactor: textScaleFactor
|
|
);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderParagraph renderObject) {
|
|
renderObject
|
|
..text = text
|
|
..textAlign = textAlign
|
|
..softWrap = softWrap
|
|
..overflow = overflow
|
|
..textScaleFactor = textScaleFactor;
|
|
}
|
|
}
|
|
|
|
/// A widget that displays a [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.
|
|
RawImage({
|
|
Key key,
|
|
this.image,
|
|
this.width,
|
|
this.height,
|
|
this.scale: 1.0,
|
|
this.color,
|
|
this.fit,
|
|
this.alignment,
|
|
this.repeat: ImageRepeat.noRepeat,
|
|
this.centerSlice
|
|
}) : super(key: key) {
|
|
assert(scale != null);
|
|
assert(repeat != null);
|
|
}
|
|
|
|
/// 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, apply this color filter to the image before painting.
|
|
final Color color;
|
|
|
|
/// 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 ImageFit 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,
|
|
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
|
|
..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 (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.
|
|
DefaultAssetBundle({
|
|
Key key,
|
|
@required this.bundle,
|
|
@required Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(bundle != null);
|
|
assert(child != null);
|
|
}
|
|
|
|
/// 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) {
|
|
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 RenderBox renderBox,
|
|
this.onBuild
|
|
}) : renderBox = renderBox,
|
|
// 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)) {
|
|
assert(renderBox != null);
|
|
}
|
|
|
|
/// 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].
|
|
Listener({
|
|
Key key,
|
|
this.onPointerDown,
|
|
this.onPointerMove,
|
|
this.onPointerUp,
|
|
this.onPointerCancel,
|
|
this.behavior: HitTestBehavior.deferToChild,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(behavior != null);
|
|
}
|
|
|
|
/// 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);
|
|
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.
|
|
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) {
|
|
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) {
|
|
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 [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.
|
|
IgnorePointer({
|
|
Key key,
|
|
this.ignoring: true,
|
|
this.ignoringSemantics,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(ignoring != null);
|
|
}
|
|
|
|
/// 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
|
|
/// [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
|
|
AbsorbPointer({
|
|
Key key,
|
|
this.absorbing: true,
|
|
Widget child
|
|
}) : super(key: key, child: child) {
|
|
assert(absorbing != null);
|
|
}
|
|
|
|
/// 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].
|
|
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.
|
|
Semantics({
|
|
Key key,
|
|
Widget child,
|
|
this.container: false,
|
|
this.checked,
|
|
this.label
|
|
}) : super(key: key, child: child) {
|
|
assert(container != null);
|
|
}
|
|
|
|
/// 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.
|
|
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.
|
|
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.
|
|
KeyedSubtree({
|
|
Key key,
|
|
@required this.child
|
|
}) : super(key: key) {
|
|
assert(child != null);
|
|
}
|
|
|
|
/// 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) {
|
|
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;
|
|
|
|
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.
|
|
Builder({
|
|
Key key,
|
|
@required this.builder
|
|
}) : super(key: key) {
|
|
assert(builder != null);
|
|
}
|
|
|
|
/// 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.
|
|
StatefulBuilder({
|
|
Key key,
|
|
@required this.builder
|
|
}) : super(key: key) {
|
|
assert(builder != null);
|
|
}
|
|
|
|
/// 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) => config.builder(context, setState);
|
|
}
|