basic.dart 74.5 KB
Newer Older
1 2 3 4
// 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.

5
import 'dart:ui' as ui show Image;
6

7 8
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
9 10

import 'framework.dart';
11

12 13
export 'package:flutter/animation.dart';
export 'package:flutter/painting.dart';
14
export 'package:flutter/rendering.dart' show
15
    Axis,
16
    BoxConstraints,
17
    CustomClipper,
18
    CustomPainter,
19
    FixedColumnCountGridDelegate,
20 21 22
    FlexAlignItems,
    FlexDirection,
    FlexJustifyContent,
23
    FractionalOffsetTween,
Adam Barth's avatar
Adam Barth committed
24
    GridDelegate,
25
    GridDelegateWithInOrderChildPlacement,
26
    GridSpecification,
27
    HitTestBehavior,
28
    MaxTileWidthGridDelegate,
29
    MultiChildLayoutDelegate,
Adam Barth's avatar
Adam Barth committed
30
    OneChildLayoutDelegate,
31 32
    Painter,
    PaintingContext,
33
    PlainTextSpan,
34
    PointerCancelEvent,
35
    PointerCancelEventListener,
36
    PointerDownEvent,
37
    PointerDownEventListener,
38 39
    PointerEvent,
    PointerMoveEvent,
40
    PointerMoveEventListener,
41
    PointerUpEvent,
42 43 44
    PointerUpEventListener,
    RelativeRect,
    ShaderCallback,
45
    ValueChanged,
46 47 48
    ViewportAnchor,
    ViewportDimensions,
    ViewportDimensionsChangeCallback;
49 50 51

// PAINTING NODES

52 53 54 55 56 57 58
/// 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.
///
/// This class is relatively expensive because it requires painting the child
/// into an intermediate buffer.
59
class Opacity extends OneChildRenderObjectWidget {
60
  Opacity({ Key key, this.opacity, Widget child })
61 62 63
    : super(key: key, child: child) {
    assert(opacity >= 0.0 && opacity <= 1.0);
  }
64

65 66 67 68
  /// 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).
69 70
  final double opacity;

71
  RenderOpacity createRenderObject() => new RenderOpacity(opacity: opacity);
72

73
  void updateRenderObject(RenderOpacity renderObject, Opacity oldWidget) {
74
    renderObject.opacity = opacity;
75
  }
76 77 78 79 80

  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
    description.add('opacity: $opacity');
  }
81 82
}

Hans Muller's avatar
Hans Muller committed
83 84 85 86
class ShaderMask extends OneChildRenderObjectWidget {
  ShaderMask({
    Key key,
    this.shaderCallback,
87
    this.transferMode: TransferMode.modulate,
Hans Muller's avatar
Hans Muller committed
88 89 90 91 92 93 94
    Widget child
  }) : super(key: key, child: child) {
    assert(shaderCallback != null);
    assert(transferMode != null);
  }

  final ShaderCallback shaderCallback;
95
  final TransferMode transferMode;
Hans Muller's avatar
Hans Muller committed
96 97 98 99 100 101 102 103 104 105 106 107 108 109

  RenderShaderMask createRenderObject() {
    return new RenderShaderMask(
      shaderCallback: shaderCallback,
      transferMode: transferMode
    );
  }

  void updateRenderObject(RenderShaderMask renderObject, ShaderMask oldWidget) {
    renderObject.shaderCallback = shaderCallback;
    renderObject.transferMode = transferMode;
  }
}

110 111 112
/// Paints a [Decoration] either before or after its child paints.
///
/// Commonly used with [BoxDecoration].
113
class DecoratedBox extends OneChildRenderObjectWidget {
114 115 116
  DecoratedBox({
    Key key,
    this.decoration,
117
    this.position: DecorationPosition.background,
118
    Widget child
119 120 121 122
  }) : super(key: key, child: child) {
    assert(decoration != null);
    assert(position != null);
  }
123

124
  /// What decoration to paint.
125 126
  ///
  /// Commonly a [BoxDecoration].
127
  final Decoration decoration;
128 129

  /// Where to paint the box decoration.
130
  final DecorationPosition position;
131

132
  RenderDecoratedBox createRenderObject() => new RenderDecoratedBox(decoration: decoration, position: position);
133

134
  void updateRenderObject(RenderDecoratedBox renderObject, DecoratedBox oldWidget) {
135
    renderObject.decoration = decoration;
136
    renderObject.position = position;
137 138 139
  }
}

140 141 142 143 144 145 146 147 148 149 150 151 152
/// Delegates its painting.
///
/// When asked to paint, custom paint first asks painter to paint with the
/// current canvas and then paints its children. After painting its children,
/// custom paint asks foregroundPainter to paint. The coodinate system of the
/// canvas matches the coordinate system of the custom paint 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 paints outside
/// those bounds, there might be insufficient memory allocated to rasterize the
/// painting commands and the resulting behavior is undefined.
///
/// Because custom paint calls its painters during paint, you cannot dirty
/// layout or paint information during the callback.
153
class CustomPaint extends OneChildRenderObjectWidget {
154 155
  CustomPaint({ Key key, this.painter, this.foregroundPainter, Widget child })
    : super(key: key, child: child);
156

157
  /// The painter that paints before the children.
158
  final CustomPainter painter;
159 160

  /// The painter that paints after the children.
161
  final CustomPainter foregroundPainter;
Adam Barth's avatar
Adam Barth committed
162

163 164 165 166
  RenderCustomPaint createRenderObject() => new RenderCustomPaint(
    painter: painter,
    foregroundPainter: foregroundPainter
  );
167

168
  void updateRenderObject(RenderCustomPaint renderObject, CustomPaint oldWidget) {
169
    renderObject.painter = painter;
170
    renderObject.foregroundPainter = foregroundPainter;
171 172
  }

173
  void didUnmountRenderObject(RenderCustomPaint renderObject) {
174
    renderObject.painter = null;
175
    renderObject.foregroundPainter = null;
176 177 178
  }
}

179 180 181
/// Clips its child using a rectangle.
///
/// Prevents its child from painting outside its bounds.
182
class ClipRect extends OneChildRenderObjectWidget {
183 184
  ClipRect({ Key key, this.clipper, Widget child }) : super(key: key, child: child);

185
  /// If non-null, determines which clip to use.
186 187 188 189 190 191 192 193 194 195 196
  final CustomClipper<Rect> clipper;

  RenderClipRect createRenderObject() => new RenderClipRect(clipper: clipper);

  void updateRenderObject(RenderClipRect renderObject, ClipRect oldWidget) {
    renderObject.clipper = clipper;
  }

  void didUnmountRenderObject(RenderClipRect renderObject) {
    renderObject.clipper = null;
  }
197 198
}

199 200 201 202 203
/// Clips its child using a rounded rectangle.
///
/// Creates a rounded rectangle from its layout dimensions and the given x and
/// y radius values and prevents its child from painting outside that rounded
/// rectangle.
204
class ClipRRect extends OneChildRenderObjectWidget {
205
  ClipRRect({ Key key, this.xRadius, this.yRadius, Widget child })
206 207
    : super(key: key, child: child);

208 209 210 211
  /// The radius of the rounded corners in the horizontal direction in logical pixels.
  ///
  /// Values are clamped to be between zero and half the width of the render
  /// object.
212
  final double xRadius;
213 214 215 216 217

  /// The radius of the rounded corners in the vertical direction in logical pixels.
  ///
  /// Values are clamped to be between zero and half the height of the render
  /// object.
218 219
  final double yRadius;

220
  RenderClipRRect createRenderObject() => new RenderClipRRect(xRadius: xRadius, yRadius: yRadius);
221

222
  void updateRenderObject(RenderClipRRect renderObject, ClipRRect oldWidget) {
223 224
    renderObject.xRadius = xRadius;
    renderObject.yRadius = yRadius;
225 226 227
  }
}

228 229 230 231
/// Clips its child using an oval.
///
/// Inscribes an oval into its layout dimensions and prevents its child from
/// painting outside that oval.
232
class ClipOval extends OneChildRenderObjectWidget {
233 234
  ClipOval({ Key key, this.clipper, Widget child }) : super(key: key, child: child);

235
  /// If non-null, determines which clip to use.
236 237 238 239 240 241 242 243 244 245 246
  final CustomClipper<Rect> clipper;

  RenderClipOval createRenderObject() => new RenderClipOval(clipper: clipper);

  void updateRenderObject(RenderClipOval renderObject, ClipOval oldWidget) {
    renderObject.clipper = clipper;
  }

  void didUnmountRenderObject(RenderClipOval renderObject) {
    renderObject.clipper = null;
  }
247 248 249 250 251
}


// POSITIONING AND SIZING NODES

252
/// Applies a transformation before painting its child.
253
class Transform extends OneChildRenderObjectWidget {
254
  Transform({ Key key, this.transform, this.origin, this.alignment, this.transformHitTests: true, Widget child })
255 256 257
    : super(key: key, child: child) {
    assert(transform != null);
  }
258

259
  /// The matrix to transform the child by during painting.
260
  final Matrix4 transform;
261 262 263 264 265 266

  /// 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.
267
  final Offset origin;
268 269 270 271 272

  /// 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 specificed at the same time as an offset, both are applied.
Hixie's avatar
Hixie committed
273
  final FractionalOffset alignment;
274

275 276 277 278 279 280 281 282 283
  /// Whether to apply the translation when performing hit tests.
  final bool transformHitTests;

  RenderTransform createRenderObject() => new RenderTransform(
    transform: transform,
    origin: origin,
    alignment: alignment,
    transformHitTests: transformHitTests
  );
284

285
  void updateRenderObject(RenderTransform renderObject, Transform oldWidget) {
286
    renderObject.transform = transform;
287
    renderObject.origin = origin;
Hixie's avatar
Hixie committed
288
    renderObject.alignment = alignment;
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
    renderObject.transformHitTests = transformHitTests;
  }
}

/// Applies a translation expressed as a fraction of the box's size before
/// painting its child.
class FractionalTranslation extends OneChildRenderObjectWidget {
  FractionalTranslation({ Key key, 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;

  RenderFractionalTranslation createRenderObject() => new RenderFractionalTranslation(translation: translation, transformHitTests: transformHitTests);

  void updateRenderObject(RenderFractionalTranslation renderObject, FractionalTranslation oldWidget) {
    renderObject.translation = translation;
    renderObject.transformHitTests = transformHitTests;
312 313 314
  }
}

315 316 317 318 319 320
/// 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.
321
class Padding extends OneChildRenderObjectWidget {
322
  Padding({ Key key, this.padding, Widget child })
323 324 325
    : super(key: key, child: child) {
    assert(padding != null);
  }
326

327
  /// The amount to pad the child in each dimension.
328 329
  final EdgeDims padding;

330
  RenderPadding createRenderObject() => new RenderPadding(padding: padding);
331

332
  void updateRenderObject(RenderPadding renderObject, Padding oldWidget) {
333
    renderObject.padding = padding;
334 335 336
  }
}

337 338 339 340
/// Aligns its child box within itself.
///
/// 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,
Adam Barth's avatar
Adam Barth committed
341 342 343 344 345
/// with an alignment of [const FractionalOffset(1.0, 1.0)].
///
/// By default, sizes to be as big as possible in both axes. If either axis is
/// unconstrained, then in that direction it will be sized to fit the child's
/// dimensions. Using widthFactor and heightFactor you can force this latter
346
/// behavior in all cases.
347
class Align extends OneChildRenderObjectWidget {
348 349
  Align({
    Key key,
350
    this.alignment: const FractionalOffset(0.5, 0.5),
351 352
    this.widthFactor,
    this.heightFactor,
353
    Widget child
354
  }) : super(key: key, child: child) {
355
    assert(alignment != null && alignment.dx != null && alignment.dy != null);
356 357 358
    assert(widthFactor == null || widthFactor >= 0.0);
    assert(heightFactor == null || heightFactor >= 0.0);
  }
359

360 361 362 363 364 365 366 367 368
  /// 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.
369
  final FractionalOffset alignment;
370 371 372 373

  /// 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.
374
  final double widthFactor;
375 376 377 378

  /// 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.
379
  final double heightFactor;
380

381
  RenderPositionedBox createRenderObject() => new RenderPositionedBox(alignment: alignment, widthFactor: widthFactor, heightFactor: heightFactor);
382

383
  void updateRenderObject(RenderPositionedBox renderObject, Align oldWidget) {
384
    renderObject.alignment = alignment;
385 386
    renderObject.widthFactor = widthFactor;
    renderObject.heightFactor = heightFactor;
387 388 389
  }
}

390
/// Centers its child within itself.
391
class Center extends Align {
392 393
  Center({ Key key, widthFactor, heightFactor, Widget child })
    : super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child);
394 395
}

396 397 398 399 400 401
/// 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.
Adam Barth's avatar
Adam Barth committed
402 403 404 405 406 407 408 409 410 411 412 413 414 415
class CustomOneChildLayout extends OneChildRenderObjectWidget {
  CustomOneChildLayout({
    Key key,
    this.delegate,
    Widget child
  }) : super(key: key, child: child) {
    assert(delegate != null);
  }

  final OneChildLayoutDelegate delegate;

  RenderCustomOneChildLayoutBox createRenderObject() => new RenderCustomOneChildLayoutBox(delegate: delegate);

  void updateRenderObject(RenderCustomOneChildLayoutBox renderObject, CustomOneChildLayout oldWidget) {
416 417 418 419
    renderObject.delegate = delegate;
  }
}

420
/// Metadata for identifying children in a [CustomMultiChildLayout].
421
class LayoutId extends ParentDataWidget<CustomMultiChildLayout> {
422 423 424
  LayoutId({
    Key key,
    Widget child,
425 426
    Object id
  }) : id = id, super(key: key ?? new ValueKey(id), child: child) {
427
    assert(child != null);
428
    assert(id != null);
429
  }
430

431
  /// An object representing the identity of this child.
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
  final Object id;

  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();
    }
  }

  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
    description.add('id: $id');
  }
}

451 452
const List<Widget> _emptyWidgetList = const <Widget>[];

453 454 455 456 457 458
/// Defers the layout of multiple children to a delegate.
///
/// 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.
459
class CustomMultiChildLayout extends MultiChildRenderObjectWidget {
460
  CustomMultiChildLayout({
461
    Key key,
462
    List<Widget> children: _emptyWidgetList,
463
    this.delegate
464 465 466 467
  }) : super(key: key, children: children) {
    assert(delegate != null);
  }

468
  /// The delegate that controls the layout of the children.
469 470 471 472 473 474 475
  final MultiChildLayoutDelegate delegate;

  RenderCustomMultiChildLayoutBox createRenderObject() {
    return new RenderCustomMultiChildLayoutBox(delegate: delegate);
  }

  void updateRenderObject(RenderCustomMultiChildLayoutBox renderObject, CustomMultiChildLayout oldWidget) {
Adam Barth's avatar
Adam Barth committed
476 477 478 479
    renderObject.delegate = delegate;
  }
}

480 481 482 483
/// A box with a specified size.
///
/// Forces its child to have a specific width and/or height and sizes itself to
/// match the size of its child.
484
class SizedBox extends OneChildRenderObjectWidget {
485
  SizedBox({ Key key, this.width, this.height, Widget child })
486 487
    : super(key: key, child: child);

488
  /// If non-null, requires the child to have exactly this width.
489
  final double width;
490 491

  /// If non-null, requires the child to have exactly this height.
492 493
  final double height;

494 495 496
  RenderConstrainedBox createRenderObject() => new RenderConstrainedBox(
    additionalConstraints: _additionalConstraints
  );
497

498
  BoxConstraints get _additionalConstraints {
499
    return new BoxConstraints.tightFor(width: width, height: height);
500 501
  }

502 503
  void updateRenderObject(RenderConstrainedBox renderObject, SizedBox oldWidget) {
    renderObject.additionalConstraints = _additionalConstraints;
504
  }
505 506 507 508 509 510 511 512

  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
    if (width != null)
      description.add('width: $width');
    if (height != null)
      description.add('height: $height');
  }
513 514
}

515 516 517 518 519
/// 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].
520
class ConstrainedBox extends OneChildRenderObjectWidget {
521
  ConstrainedBox({ Key key, this.constraints, Widget child })
522 523 524
    : super(key: key, child: child) {
    assert(constraints != null);
  }
525

526
  /// The additional constraints to impose on the child.
527 528
  final BoxConstraints constraints;

529
  RenderConstrainedBox createRenderObject() => new RenderConstrainedBox(additionalConstraints: constraints);
530

531
  void updateRenderObject(RenderConstrainedBox renderObject, ConstrainedBox oldWidget) {
532
    renderObject.additionalConstraints = constraints;
533
  }
534 535 536

  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
Hixie's avatar
Hixie committed
537
    description.add('$constraints');
538
  }
539 540
}

541 542 543
/// Sizes itself to a fraction of the total available space.
///
/// See [RenderFractionallySizedBox] for details.
Hixie's avatar
Hixie committed
544 545 546 547
class FractionallySizedBox extends OneChildRenderObjectWidget {
  FractionallySizedBox({ Key key, this.width, this.height, Widget child })
    : super(key: key, child: child);

548 549 550 551
  /// If non-null, the factor of the incoming width to use.
  ///
  /// If non-null, the child is given a tight width constraint that is the max
  /// incoming width constraint multipled by this factor.
Hixie's avatar
Hixie committed
552
  final double width;
553 554 555 556 557

  /// If non-null, the factor of the incoming height to use.
  ///
  /// If non-null, the child is given a tight height constraint that is the max
  /// incoming height constraint multipled by this factor.
Hixie's avatar
Hixie committed
558 559 560 561 562 563 564
  final double height;

  RenderFractionallySizedBox createRenderObject() => new RenderFractionallySizedBox(
    widthFactor: width,
    heightFactor: height
  );

565
  void updateRenderObject(RenderFractionallySizedBox renderObject, FractionallySizedBox oldWidget) {
Hixie's avatar
Hixie committed
566 567 568
    renderObject.widthFactor = width;
    renderObject.heightFactor = height;
  }
569 570 571 572 573 574 575 576

  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
    if (width != null)
      description.add('width: $width');
    if (height != null)
      description.add('height: $height');
  }
Hixie's avatar
Hixie committed
577 578
}

579 580 581 582
/// A render object 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.
583
class OverflowBox extends OneChildRenderObjectWidget {
584 585 586 587 588 589 590 591 592
  OverflowBox({
    Key key,
    this.minWidth,
    this.maxWidth,
    this.minHeight,
    this.maxHeight,
    this.alignment: const FractionalOffset(0.5, 0.5),
    Widget child
  }) : super(key: key, child: child);
593

594 595
  /// The minimum width constraint to give the child. Set this to null (the
  /// default) to use the constraint from the parent instead.
596
  final double minWidth;
597 598 599

  /// The maximum width constraint to give the child. Set this to null (the
  /// default) to use the constraint from the parent instead.
600
  final double maxWidth;
601 602 603

  /// The minimum height constraint to give the child. Set this to null (the
  /// default) to use the constraint from the parent instead.
604
  final double minHeight;
605 606 607

  /// The maximum height constraint to give the child. Set this to null (the
  /// default) to use the constraint from the parent instead.
608 609
  final double maxHeight;

610 611 612 613 614 615 616 617 618 619 620
  /// 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;

621 622 623 624
  RenderOverflowBox createRenderObject() => new RenderOverflowBox(
    minWidth: minWidth,
    maxWidth: maxWidth,
    minHeight: minHeight,
625 626
    maxHeight: maxHeight,
    alignment: alignment
627 628 629
  );

  void updateRenderObject(RenderOverflowBox renderObject, OverflowBox oldWidget) {
630 631 632 633 634 635
    renderObject
      ..minWidth = minWidth
      ..maxWidth = maxWidth
      ..minHeight = minHeight
      ..maxHeight = maxHeight
      ..alignment = alignment;
636
  }
637 638 639 640 641 642 643 644 645 646 647 648

  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
    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');
  }
649 650
}

651 652 653 654 655 656 657 658 659 660 661 662 663
class SizedOverflowBox extends OneChildRenderObjectWidget {
  SizedOverflowBox({ Key key, this.size, Widget child })
    : super(key: key, child: child);

  final Size size;

  RenderSizedOverflowBox createRenderObject() => new RenderSizedOverflowBox(requestedSize: size);

  void updateRenderObject(RenderSizedOverflowBox renderObject, SizedOverflowBox oldWidget) {
    renderObject.requestedSize = size;
  }
}

664 665 666
/// 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.
Hixie's avatar
Hixie committed
667 668 669 670 671 672 673
class OffStage extends OneChildRenderObjectWidget {
  OffStage({ Key key, Widget child })
    : super(key: key, child: child);

  RenderOffStage createRenderObject() => new RenderOffStage();
}

674 675 676
/// Forces child to layout at a specific aspect ratio.
///
/// See [RenderAspectRatio] for details.
677
class AspectRatio extends OneChildRenderObjectWidget {
678
  AspectRatio({ Key key, this.aspectRatio, Widget child })
679 680 681
    : super(key: key, child: child) {
    assert(aspectRatio != null);
  }
682

683 684 685 686
  /// The aspect ratio to use when computing the height from the width.
  ///
  /// 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.
687 688
  final double aspectRatio;

689
  RenderAspectRatio createRenderObject() => new RenderAspectRatio(aspectRatio: aspectRatio);
690

691
  void updateRenderObject(RenderAspectRatio renderObject, AspectRatio oldWidget) {
692
    renderObject.aspectRatio = aspectRatio;
693
  }
694 695 696 697 698

  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
    description.add('aspectRatio: $aspectRatio');
  }
699 700
}

701 702 703 704 705 706 707 708 709 710 711 712
/// 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.
713
class IntrinsicWidth extends OneChildRenderObjectWidget {
714
  IntrinsicWidth({ Key key, this.stepWidth, this.stepHeight, Widget child })
715 716
    : super(key: key, child: child);

717
  /// If non-null, force the child's width to be a multiple of this value.
718
  final double stepWidth;
719 720

  /// If non-null, force the child's height to be a multiple of this value.
721 722
  final double stepHeight;

723
  RenderIntrinsicWidth createRenderObject() => new RenderIntrinsicWidth(stepWidth: stepWidth, stepHeight: stepHeight);
724

725
  void updateRenderObject(RenderIntrinsicWidth renderObject, IntrinsicWidth oldWidget) {
726 727
    renderObject.stepWidth = stepWidth;
    renderObject.stepHeight = stepHeight;
728 729 730
  }
}

731 732 733 734 735 736 737
/// 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.
738 739 740
class IntrinsicHeight extends OneChildRenderObjectWidget {
  IntrinsicHeight({ Key key, Widget child }) : super(key: key, child: child);
  RenderIntrinsicHeight createRenderObject() => new RenderIntrinsicHeight();
Hixie's avatar
Hixie committed
741 742
}

743
/// Positions its child vertically according to the child's baseline.
744
class Baseline extends OneChildRenderObjectWidget {
745
  Baseline({ Key key, this.baseline, this.baselineType: TextBaseline.alphabetic, Widget child })
746 747 748 749
    : super(key: key, child: child) {
    assert(baseline != null);
    assert(baselineType != null);
  }
750

751 752 753 754 755
  /// 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.
756 757
  final TextBaseline baselineType;

758
  RenderBaseline createRenderObject() => new RenderBaseline(baseline: baseline, baselineType: baselineType);
759

760
  void updateRenderObject(RenderBaseline renderObject, Baseline oldWidget) {
761 762
    renderObject.baseline = baseline;
    renderObject.baselineType = baselineType;
763 764 765
  }
}

766 767 768 769 770 771 772 773 774
/// A widget that's bigger on the inside.
///
/// The child of a viewport can layout to a larger size 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.
775
class Viewport extends OneChildRenderObjectWidget {
776 777
  Viewport({
    Key key,
778
    this.paintOffset: Offset.zero,
779 780
    this.scrollDirection: Axis.vertical,
    this.scrollAnchor: ViewportAnchor.start,
781
    this.overlayPainter,
782
    this.onPaintOffsetUpdateNeeded,
783
    Widget child
784 785
  }) : super(key: key, child: child) {
    assert(scrollDirection != null);
786
    assert(paintOffset != null);
787
  }
788

789 790 791 792 793
  /// The offset at which to paint the child.
  ///
  /// The offset can be non-zero only in the [scrollDirection].
  final Offset paintOffset;

794 795 796 797 798
  /// The direction in which the child is permitted to be larger than the viewport
  ///
  /// If the viewport is scrollable in a particular direction (e.g., vertically),
  /// the child is given layout constraints that are fully unconstrainted in
  /// that direction (e.g., the child can be as tall as it wants).
799
  final Axis scrollDirection;
800

801
  final ViewportAnchor scrollAnchor;
802

803 804 805 806 807
  /// Paints an overlay over the viewport.
  ///
  /// Often used to paint scroll bars.
  final Painter overlayPainter;

808 809
  final ViewportDimensionsChangeCallback onPaintOffsetUpdateNeeded;

810 811 812 813 814
  RenderViewport createRenderObject() {
    return new RenderViewport(
      paintOffset: paintOffset,
      scrollDirection: scrollDirection,
      scrollAnchor: scrollAnchor,
815
      onPaintOffsetUpdateNeeded: onPaintOffsetUpdateNeeded,
816 817 818
      overlayPainter: overlayPainter
    );
  }
819

820
  void updateRenderObject(RenderViewport renderObject, Viewport oldWidget) {
821
    // Order dependency: RenderViewport validates scrollOffset based on scrollDirection.
822 823
    renderObject
      ..scrollDirection = scrollDirection
824
      ..scrollAnchor = scrollAnchor
825
      ..paintOffset = paintOffset
826
      ..onPaintOffsetUpdateNeeded = onPaintOffsetUpdateNeeded
827
      ..overlayPainter = overlayPainter;
828 829 830
  }
}

831 832 833 834
/// Calls [onSizeChanged] whenever the child's layout size changes
///
/// Because size observer calls its callback during layout, you cannot modify
/// layout information during the callback.
835
class SizeObserver extends OneChildRenderObjectWidget {
836
  SizeObserver({ Key key, this.onSizeChanged, Widget child })
837
    : super(key: key, child: child) {
838
    assert(onSizeChanged != null);
839
  }
840

841
  /// The callback to call whenever the child's layout size changes
842
  final ValueChanged<Size> onSizeChanged;
843

844
  RenderSizeObserver createRenderObject() => new RenderSizeObserver(onSizeChanged: onSizeChanged);
845

846
  void updateRenderObject(RenderSizeObserver renderObject, SizeObserver oldWidget) {
847
    renderObject.onSizeChanged = onSizeChanged;
848 849
  }

850
  void didUnmountRenderObject(RenderSizeObserver renderObject) {
851
    renderObject.onSizeChanged = null;
852 853 854 855
  }
}


856
// CONTAINER
857

858
/// A convenience widget that combines common painting, positioning, and sizing widgets.
859
class Container extends StatelessComponent {
860
  Container({
861
    Key key,
862
    this.child,
863
    BoxConstraints constraints,
864
    this.decoration,
865
    this.foregroundDecoration,
866 867
    this.margin,
    this.padding,
Adam Barth's avatar
Adam Barth committed
868
    this.transform,
869 870
    double width,
    double height
871
  }) : constraints =
872 873 874 875 876
        (width != null || height != null)
          ? constraints?.tighten(width: width, height: height)
            ?? new BoxConstraints.tightFor(width: width, height: height)
          : constraints,
       super(key: key) {
877 878
    assert(margin == null || margin.isNonNegative);
    assert(padding == null || padding.isNonNegative);
879
    assert(decoration == null || decoration.debugAssertValid());
880
  }
881

882 883 884
  /// The child to contain in the container.
  ///
  /// If null, the container will expand to fill all available space in its parent.
885
  final Widget child;
886 887

  /// Additional constraints to apply to the child.
888
  final BoxConstraints constraints;
889 890

  /// The decoration to paint behind the child.
891
  final Decoration decoration;
892 893

  /// The decoration to paint in front of the child.
894
  final Decoration foregroundDecoration;
895 896

  /// Empty space to surround the decoration.
897
  final EdgeDims margin;
898 899

  /// Empty space to inscribe inside the decoration.
900
  final EdgeDims padding;
901

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

905 906
  EdgeDims get _paddingIncludingDecoration {
    if (decoration == null || decoration.padding == null)
907
      return padding;
908
    EdgeDims decorationPadding = decoration.padding;
909
    if (padding == null)
910 911
      return decorationPadding;
    return padding + decorationPadding;
912 913
  }

914
  Widget build(BuildContext context) {
915 916
    Widget current = child;

917
    if (child == null && (constraints == null || !constraints.isTight))
918
      current = new ConstrainedBox(constraints: const BoxConstraints.expand());
919

920
    EdgeDims effectivePadding = _paddingIncludingDecoration;
921 922
    if (effectivePadding != null)
      current = new Padding(padding: effectivePadding, child: current);
923 924 925 926

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

927 928 929
    if (foregroundDecoration != null) {
      current = new DecoratedBox(
        decoration: foregroundDecoration,
930
        position: DecorationPosition.foreground,
931 932 933 934
        child: current
      );
    }

935 936 937 938 939 940 941 942 943 944 945 946
    if (constraints != null)
      current = new ConstrainedBox(constraints: constraints, child: current);

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

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

    return current;
  }

Hixie's avatar
Hixie committed
947 948 949 950 951
  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
    if (constraints != null)
      description.add('$constraints');
    if (decoration != null)
952
      description.add('bg: $decoration');
Hixie's avatar
Hixie committed
953
    if (foregroundDecoration != null)
954
      description.add('fg: $foregroundDecoration');
Hixie's avatar
Hixie committed
955 956 957 958 959 960 961
    if (margin != null)
      description.add('margin: $margin');
    if (padding != null)
      description.add('padding: $padding');
    if (transform != null)
      description.add('has transform');
  }
962 963 964 965 966
}


// LAYOUT NODES

967 968 969 970 971 972
/// 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].
973
class BlockBody extends MultiChildRenderObjectWidget {
974
  BlockBody({
975
    Key key,
976
    List<Widget> children: _emptyWidgetList,
977
    this.direction: Axis.vertical
978 979 980
  }) : super(key: key, children: children) {
    assert(direction != null);
  }
981

982
  /// The direction to use as the main axis.
983
  final Axis direction;
984

985
  RenderBlock createRenderObject() => new RenderBlock(direction: direction);
986

987
  void updateRenderObject(RenderBlock renderObject, BlockBody oldWidget) {
988
    renderObject.direction = direction;
989
  }
990 991
}

992 993
abstract class StackRenderObjectWidgetBase extends MultiChildRenderObjectWidget {
  StackRenderObjectWidgetBase({
994
    List<Widget> children: _emptyWidgetList,
995 996 997 998
    Key key
  }) : super(key: key, children: children);
}

Adam Barth's avatar
Adam Barth committed
999 1000 1001 1002
/// Uses the stack layout algorithm for its children.
///
/// For details about the stack layout algorithm, see [RenderStack]. To control
/// the position of child widgets, see the [Positioned] widget.
1003
class Stack extends StackRenderObjectWidgetBase {
1004
  Stack({
Hans Muller's avatar
Hans Muller committed
1005
    Key key,
1006
    List<Widget> children: _emptyWidgetList,
1007 1008
    this.alignment: const FractionalOffset(0.0, 0.0)
  }) : super(key: key, children: children);
Hans Muller's avatar
Hans Muller committed
1009

Adam Barth's avatar
Adam Barth committed
1010
  /// How to align the non-positioned children in the stack.
1011
  final FractionalOffset alignment;
Hans Muller's avatar
Hans Muller committed
1012

1013
  RenderStack createRenderObject() => new RenderStack(alignment: alignment);
Hans Muller's avatar
Hans Muller committed
1014 1015

  void updateRenderObject(RenderStack renderObject, Stack oldWidget) {
1016
    renderObject.alignment = alignment;
Hans Muller's avatar
Hans Muller committed
1017 1018 1019
  }
}

Adam Barth's avatar
Adam Barth committed
1020
/// A [Stack] that shows a single child at once.
1021
class IndexedStack extends StackRenderObjectWidgetBase {
1022
  IndexedStack({
Hans Muller's avatar
Hans Muller committed
1023
    Key key,
1024
    List<Widget> children: _emptyWidgetList,
1025
    this.alignment: const FractionalOffset(0.0, 0.0),
Hans Muller's avatar
Hans Muller committed
1026
    this.index: 0
1027 1028 1029
  }) : super(key: key, children: children) {
    assert(index != null);
  }
Hans Muller's avatar
Hans Muller committed
1030

Adam Barth's avatar
Adam Barth committed
1031
  /// The index of the child to show.
Hans Muller's avatar
Hans Muller committed
1032
  final int index;
Adam Barth's avatar
Adam Barth committed
1033 1034

  /// How to align the non-positioned children in the stack.
1035 1036 1037
  final FractionalOffset alignment;

  RenderIndexedStack createRenderObject() => new RenderIndexedStack(index: index, alignment: alignment);
Hans Muller's avatar
Hans Muller committed
1038 1039 1040 1041

  void updateRenderObject(RenderIndexedStack renderObject, IndexedStack oldWidget) {
    super.updateRenderObject(renderObject, oldWidget);
    renderObject.index = index;
1042
    renderObject.alignment = alignment;
Hans Muller's avatar
Hans Muller committed
1043
  }
1044 1045
}

Adam Barth's avatar
Adam Barth committed
1046 1047 1048 1049 1050
/// Controls where a child of a [Stack] is positioned.
///
/// This widget must be a descendant of a [Stack], and the path from this widget
/// to its enclosing [Stack] must contain only components (e.g., not other
/// kinds of widgets, like [RenderObjectWidget]s).
1051
class Positioned extends ParentDataWidget<StackRenderObjectWidgetBase> {
1052 1053 1054
  Positioned({
    Key key,
    Widget child,
1055
    this.left,
1056 1057 1058
    this.top,
    this.right,
    this.bottom,
1059 1060 1061 1062
    this.width,
    this.height
  }) : super(key: key, child: child) {
    assert(left == null || right == null || width == null);
1063
    assert(top == null || bottom == null || height == null);
1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
  }

  Positioned.fromRect({
    Key key,
    Widget child,
    Rect rect
  }) : left = rect.left,
       top = rect.top,
       width = rect.width,
       height = rect.height,
1074 1075
       right = null,
       bottom = null,
1076
       super(key: key, child: child);
1077

1078 1079 1080
  /// The offset of the child's left edge from the left of the stack.
  final double left;

Adam Barth's avatar
Adam Barth committed
1081
  /// The offset of the child's top edge from the top of the stack.
1082
  final double top;
Adam Barth's avatar
Adam Barth committed
1083 1084

  /// The offset of the child's right edge from the right of the stack.
1085
  final double right;
Adam Barth's avatar
Adam Barth committed
1086 1087

  /// The offset of the child's bottom edge from the bottom of the stack.
1088
  final double bottom;
Adam Barth's avatar
Adam Barth committed
1089 1090 1091

  /// The child's width.
  ///
1092 1093
  /// Only two out of the three horizontal values (left, right, width) can be
  /// set. The third must be null.
1094
  final double width;
Adam Barth's avatar
Adam Barth committed
1095 1096 1097

  /// The child's height.
  ///
1098 1099
  /// Only two out of the three vertical values (top, bottom, height) can be
  /// set. The third must be null.
1100 1101
  final double height;

1102 1103 1104
  void applyParentData(RenderObject renderObject) {
    assert(renderObject.parentData is StackParentData);
    final StackParentData parentData = renderObject.parentData;
1105
    bool needsLayout = false;
1106

1107 1108 1109 1110 1111
    if (parentData.left != left) {
      parentData.left = left;
      needsLayout = true;
    }

1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126
    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;
    }

1127 1128 1129 1130 1131 1132 1133 1134 1135 1136
    if (parentData.width != width) {
      parentData.width = width;
      needsLayout = true;
    }

    if (parentData.height != height) {
      parentData.height = height;
      needsLayout = true;
    }

1137 1138 1139 1140 1141
    if (needsLayout) {
      AbstractNode targetParent = renderObject.parent;
      if (targetParent is RenderObject)
        targetParent.markNeedsLayout();
    }
1142
  }
1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153

  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');
1154 1155 1156 1157
    if (width != null)
      description.add('width: $width');
    if (height != null)
      description.add('height: $height');
1158
  }
1159 1160
}

Adam Barth's avatar
Adam Barth committed
1161 1162
abstract class GridRenderObjectWidgetBase extends MultiChildRenderObjectWidget {
  GridRenderObjectWidgetBase({
1163
    List<Widget> children: _emptyWidgetList,
Adam Barth's avatar
Adam Barth committed
1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
    Key key
  }) : super(key: key, children: children) {
    _delegate = createDelegate();
  }

  GridDelegate _delegate;

  /// The delegate that controls the layout of the children.
  GridDelegate createDelegate();

  RenderGrid createRenderObject() => new RenderGrid(delegate: _delegate);

  void updateRenderObject(RenderGrid renderObject, GridRenderObjectWidgetBase oldWidget) {
    renderObject.delegate = _delegate;
  }
}

Adam Barth's avatar
Adam Barth committed
1181 1182 1183
/// Uses the grid layout algorithm for its children.
///
/// For details about the grid layout algorithm, see [RenderGrid].
Adam Barth's avatar
Adam Barth committed
1184
class CustomGrid extends GridRenderObjectWidgetBase {
1185
  CustomGrid({ Key key, List<Widget> children: _emptyWidgetList, this.delegate })
1186
    : super(key: key, children: children) {
Adam Barth's avatar
Adam Barth committed
1187
    assert(delegate != null);
1188 1189
  }

Adam Barth's avatar
Adam Barth committed
1190 1191
  /// The delegate that controls the layout of the children.
  final GridDelegate delegate;
1192

Adam Barth's avatar
Adam Barth committed
1193 1194 1195 1196 1197 1198 1199
  GridDelegate createDelegate() => delegate;
}

/// Uses a grid layout with a fixed column count.
///
/// For details about the grid layout algorithm, see [MaxTileWidthGridDelegate].
class FixedColumnCountGrid extends GridRenderObjectWidgetBase {
1200
  FixedColumnCountGrid({
Adam Barth's avatar
Adam Barth committed
1201
    Key key,
1202
    List<Widget> children: _emptyWidgetList,
Adam Barth's avatar
Adam Barth committed
1203
    this.columnCount,
1204 1205
    this.columnSpacing,
    this.rowSpacing,
Adam Barth's avatar
Adam Barth committed
1206 1207 1208 1209 1210 1211 1212 1213
    this.tileAspectRatio: 1.0,
    this.padding: EdgeDims.zero
  }) : super(key: key, children: children) {
    assert(columnCount != null);
  }

  /// The number of columns in the grid.
  final int columnCount;
1214

1215 1216 1217 1218 1219 1220
  /// The horizontal distance between columns.
  final double columnSpacing;

  /// The vertical distance between rows.
  final double rowSpacing;

Adam Barth's avatar
Adam Barth committed
1221 1222 1223 1224 1225 1226 1227 1228 1229
  /// 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 EdgeDims padding;

  FixedColumnCountGridDelegate createDelegate() {
    return new FixedColumnCountGridDelegate(
      columnCount: columnCount,
1230 1231
      columnSpacing: columnSpacing,
      rowSpacing: rowSpacing,
Adam Barth's avatar
Adam Barth committed
1232 1233 1234 1235 1236 1237 1238 1239 1240 1241
      tileAspectRatio: tileAspectRatio,
      padding: padding
    );
  }
}

/// Uses a grid layout with a max tile width.
///
/// For details about the grid layout algorithm, see [MaxTileWidthGridDelegate].
class MaxTileWidthGrid extends GridRenderObjectWidgetBase {
1242
  MaxTileWidthGrid({
Adam Barth's avatar
Adam Barth committed
1243
    Key key,
1244
    List<Widget> children: _emptyWidgetList,
Adam Barth's avatar
Adam Barth committed
1245
    this.maxTileWidth,
1246 1247
    this.columnSpacing,
    this.rowSpacing,
Adam Barth's avatar
Adam Barth committed
1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259
    this.tileAspectRatio: 1.0,
    this.padding: EdgeDims.zero
  }) : super(key: key, children: children) {
    assert(maxTileWidth != null);
  }

  /// 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;

1260 1261 1262 1263 1264 1265
  /// The horizontal distance between columns.
  final double columnSpacing;

  /// The vertical distance between rows.
  final double rowSpacing;

Adam Barth's avatar
Adam Barth committed
1266 1267 1268 1269 1270 1271 1272
  /// The amount of padding to apply to each child.
  final EdgeDims padding;

  MaxTileWidthGridDelegate createDelegate() {
    return new MaxTileWidthGridDelegate(
      maxTileWidth: maxTileWidth,
      tileAspectRatio: tileAspectRatio,
1273 1274
      columnSpacing: columnSpacing,
      rowSpacing: rowSpacing,
Adam Barth's avatar
Adam Barth committed
1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301
      padding: padding
    );
  }
}

/// Supplies per-child data to the grid's [GridDelegate].
class GridPlacementData<DataType, WidgetType extends RenderObjectWidget> extends ParentDataWidget<WidgetType> {
  GridPlacementData({ Key key, this.placementData, Widget child })
    : super(key: key, child: child);

  /// Opaque data passed to the getChildPlacement method of the grid's [GridDelegate].
  final DataType placementData;

  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();
    }
  }

  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
    description.add('placementData: $placementData');
1302 1303
  }
}
1304

Adam Barth's avatar
Adam Barth committed
1305 1306 1307 1308
/// Uses the flex layout algorithm for its children.
///
/// For details about the flex layout algorithm, see [RenderFlex]. To control
/// the flex of child widgets, see the [Flexible] widget.
1309
class Flex extends MultiChildRenderObjectWidget {
1310
  Flex({
1311
    Key key,
1312
    List<Widget> children: _emptyWidgetList,
1313 1314
    this.direction: FlexDirection.horizontal,
    this.justifyContent: FlexJustifyContent.start,
1315 1316
    this.alignItems: FlexAlignItems.center,
    this.textBaseline
1317 1318 1319 1320 1321
  }) : super(key: key, children: children) {
    assert(direction != null);
    assert(justifyContent != null);
    assert(alignItems != null);
  }
1322 1323 1324 1325

  final FlexDirection direction;
  final FlexJustifyContent justifyContent;
  final FlexAlignItems alignItems;
1326
  final TextBaseline textBaseline;
1327

1328
  RenderFlex createRenderObject() => new RenderFlex(direction: direction, justifyContent: justifyContent, alignItems: alignItems, textBaseline: textBaseline);
1329

1330
  void updateRenderObject(RenderFlex renderObject, Flex oldWidget) {
1331 1332 1333 1334
    renderObject.direction = direction;
    renderObject.justifyContent = justifyContent;
    renderObject.alignItems = alignItems;
    renderObject.textBaseline = textBaseline;
1335 1336 1337
  }
}

Adam Barth's avatar
Adam Barth committed
1338 1339 1340 1341
/// Lays out child elements in a row.
///
/// For details about the flex layout algorithm, see [RenderFlex]. To control
/// the flex of child widgets, see the [Flexible] widget.
1342
class Row extends Flex {
1343
  Row({
1344
    Key key,
1345
    List<Widget> children: _emptyWidgetList,
1346 1347 1348
    justifyContent: FlexJustifyContent.start,
    alignItems: FlexAlignItems.center,
    textBaseline
1349 1350 1351 1352 1353 1354 1355 1356
  }) : super(
    children: children,
    key: key,
    direction: FlexDirection.horizontal,
    justifyContent: justifyContent,
    alignItems: alignItems,
    textBaseline: textBaseline
  );
1357 1358
}

Adam Barth's avatar
Adam Barth committed
1359 1360 1361 1362
/// Lays out child elements in a column.
///
/// For details about the flex layout algorithm, see [RenderFlex]. To control
/// the flex of child widgets, see the [Flexible] widget.
1363
class Column extends Flex {
1364
  Column({
1365
    Key key,
1366
    List<Widget> children: _emptyWidgetList,
1367 1368 1369
    justifyContent: FlexJustifyContent.start,
    alignItems: FlexAlignItems.center,
    textBaseline
1370 1371 1372 1373 1374 1375 1376 1377
  }) : super(
    children: children,
    key: key,
    direction: FlexDirection.vertical,
    justifyContent: justifyContent,
    alignItems: alignItems,
    textBaseline: textBaseline
  );
1378 1379
}

Adam Barth's avatar
Adam Barth committed
1380 1381 1382 1383 1384 1385
/// Controls how a child of a [Flex], [Row], or [Column] flexes.
///
/// This widget must be a descendant of a [Flex], [Row], or [Column], and the
/// path from this widget to its enclosing [Flex], [Row], or [Column] must
/// contain only components (e.g., not other kinds of widgets, like
/// [RenderObjectWidget]s).
1386
class Flexible extends ParentDataWidget<Flex> {
1387 1388 1389
  Flexible({ Key key, this.flex: 1, Widget child })
    : super(key: key, child: child);

Adam Barth's avatar
Adam Barth committed
1390 1391 1392 1393 1394 1395
  /// The flex factor to use for this child
  ///
  /// If null, the child is inflexible and determines its own size. If non-null,
  /// the child is flexible and its extent 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.
1396 1397
  final int flex;

1398 1399 1400 1401 1402
  void applyParentData(RenderObject renderObject) {
    assert(renderObject.parentData is FlexParentData);
    final FlexParentData parentData = renderObject.parentData;
    if (parentData.flex != flex) {
      parentData.flex = flex;
1403 1404 1405
      AbstractNode targetParent = renderObject.parent;
      if (targetParent is RenderObject)
        targetParent.markNeedsLayout();
1406 1407
    }
  }
1408 1409 1410 1411 1412

  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
    description.add('flex: $flex');
  }
1413 1414
}

Adam Barth's avatar
Adam Barth committed
1415
/// A paragraph of rich text.
1416 1417 1418
///
/// This class is rarely used directly. Instead, consider using [Text], which
/// integrates with [DefaultTextStyle].
Adam Barth's avatar
Adam Barth committed
1419 1420
class RichText extends LeafRenderObjectWidget {
  RichText({ Key key, this.text }) : super(key: key) {
1421 1422
    assert(text != null);
  }
1423

1424
  final TextSpan text;
1425

1426
  RenderParagraph createRenderObject() => new RenderParagraph(text);
1427

Adam Barth's avatar
Adam Barth committed
1428
  void updateRenderObject(RenderParagraph renderObject, RichText oldWidget) {
1429
    renderObject.text = text;
1430 1431 1432
  }
}

1433
/// The text style to apply to descendant [Text] widgets without explicit style.
1434 1435 1436 1437 1438 1439 1440 1441 1442 1443
class DefaultTextStyle extends InheritedWidget {
  DefaultTextStyle({
    Key key,
    this.style,
    Widget child
  }) : super(key: key, child: child) {
    assert(style != null);
    assert(child != null);
  }

1444
  /// The text style to apply.
1445 1446
  final TextStyle style;

1447
  /// The style from the closest instance of this class that encloses the given context.
1448
  static TextStyle of(BuildContext context) {
Ian Hickson's avatar
Ian Hickson committed
1449
    DefaultTextStyle result = context.inheritFromWidgetOfExactType(DefaultTextStyle);
1450 1451 1452 1453
    return result?.style;
  }

  bool updateShouldNotify(DefaultTextStyle old) => style != old.style;
1454 1455 1456 1457 1458

  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
    '$style'.split('\n').forEach(description.add);
  }
1459 1460
}

1461 1462 1463 1464
/// A run of text.
///
/// By default, the text will be styled using the closest enclosing
/// [DefaultTextStyle].
1465
class Text extends StatelessComponent {
Hixie's avatar
Hixie committed
1466
  Text(this.data, { Key key, this.style }) : super(key: key) {
1467 1468
    assert(data != null);
  }
1469

1470
  /// The text to display.
1471
  final String data;
1472 1473 1474 1475 1476 1477

  /// If non-null, the style to use for this text.
  ///
  /// If the style's "inherit" property is true, the style will be merged with
  /// the closest enclosing [DefaultTextStyle]. Otherwise, the style will
  /// replace the closest enclosing [DefaultTextStyle].
1478 1479
  final TextStyle style;

Adam Barth's avatar
Adam Barth committed
1480 1481 1482 1483 1484 1485 1486
  TextStyle _getEffectiveStyle(BuildContext context) {
    if (style == null || style.inherit)
      return DefaultTextStyle.of(context)?.merge(style) ?? style;
    else
      return style;
  }

1487
  Widget build(BuildContext context) {
Adam Barth's avatar
Adam Barth committed
1488 1489 1490 1491 1492 1493
    return new RichText(
      text: new TextSpan(
        style: _getEffectiveStyle(context),
        text: data
      )
    );
1494
  }
1495 1496 1497 1498 1499 1500 1501

  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
    description.add('"$data"');
    if (style != null)
      '$style'.split('\n').forEach(description.add);
  }
1502 1503
}

1504 1505 1506 1507 1508 1509 1510
/// Displays a raw image.
///
/// This widget is rarely used directly. Instead, consider using [AssetImage] or
/// [NetworkImage], depending on whather you wish to display an image from the
/// assert bundle or from the network.
class RawImage extends LeafRenderObjectWidget {
  RawImage({
1511 1512 1513 1514
    Key key,
    this.image,
    this.width,
    this.height,
1515
    this.scale: 1.0,
Adam Barth's avatar
Adam Barth committed
1516
    this.color,
1517
    this.fit,
1518
    this.alignment,
1519 1520
    this.repeat: ImageRepeat.noRepeat,
    this.centerSlice
1521
  }) : super(key: key);
1522

1523
  /// The image to display.
1524
  final ui.Image image;
1525 1526 1527 1528 1529

  /// 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.
1530
  final double width;
1531 1532 1533 1534 1535

  /// 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.
1536
  final double height;
1537

1538
  /// Specifies the image's scale.
1539 1540 1541 1542
  ///
  /// Used when determining the best display size for the image.
  final double scale;

Adam Barth's avatar
Adam Barth committed
1543 1544
  /// If non-null, apply this color filter to the image before painting.
  final Color color;
1545 1546

  /// How to inscribe the image into the place allocated during layout.
1547
  final ImageFit fit;
1548 1549 1550 1551 1552 1553

  /// 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.
1554
  final FractionalOffset alignment;
1555 1556

  /// How to paint any portions of the layout bounds not covered by the image.
1557
  final ImageRepeat repeat;
1558 1559 1560 1561 1562 1563 1564 1565

  /// 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.
1566
  final Rect centerSlice;
1567

1568
  RenderImage createRenderObject() => new RenderImage(
1569 1570 1571
    image: image,
    width: width,
    height: height,
1572
    scale: scale,
Adam Barth's avatar
Adam Barth committed
1573
    color: color,
1574
    fit: fit,
1575
    alignment: alignment,
1576
    repeat: repeat,
1577 1578
    centerSlice: centerSlice
  );
1579

1580
  void updateRenderObject(RenderImage renderObject, RawImage oldWidget) {
1581 1582 1583
    renderObject.image = image;
    renderObject.width = width;
    renderObject.height = height;
1584
    renderObject.scale = scale;
Adam Barth's avatar
Adam Barth committed
1585
    renderObject.color = color;
1586
    renderObject.alignment = alignment;
1587 1588
    renderObject.fit = fit;
    renderObject.repeat = repeat;
1589
    renderObject.centerSlice = centerSlice;
1590
  }
1591 1592 1593

  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
1594
    description.add('image: $image');
1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611
    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');
  }
1612 1613
}

1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624
/// Displays an [ImageResource].
///
/// An image resource differs from an image in that it might yet let be loaded
/// from the underlying storage (e.g., the asset bundle or the network) and it
/// might change over time (e.g., an animated image).
///
/// This widget is rarely used directly. Instead, consider using [AssetImage] or
/// [NetworkImage], depending on whather you wish to display an image from the
/// assert bundle or from the network.
class RawImageResource extends StatefulComponent {
  RawImageResource({
1625 1626 1627 1628
    Key key,
    this.image,
    this.width,
    this.height,
Adam Barth's avatar
Adam Barth committed
1629
    this.color,
1630
    this.fit,
1631
    this.alignment,
1632 1633
    this.repeat: ImageRepeat.noRepeat,
    this.centerSlice
1634 1635 1636
  }) : super(key: key) {
    assert(image != null);
  }
1637

1638
  /// The image to display.
1639
  final ImageResource image;
1640 1641 1642 1643 1644

  /// 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.
1645
  final double width;
1646 1647 1648 1649 1650

  /// 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.
1651
  final double height;
1652

Adam Barth's avatar
Adam Barth committed
1653 1654
  /// If non-null, apply this color filter to the image before painting.
  final Color color;
1655 1656

  /// How to inscribe the image into the place allocated during layout.
1657
  final ImageFit fit;
1658 1659 1660 1661 1662 1663

  /// 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.
1664
  final FractionalOffset alignment;
1665 1666

  /// How to paint any portions of the layout bounds not covered by the image.
1667
  final ImageRepeat repeat;
1668 1669 1670 1671 1672 1673 1674 1675

  /// 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.
1676
  final Rect centerSlice;
1677

1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697
  _RawImageResourceState createState() => new _RawImageResourceState();

  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 (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');
  }
1698 1699
}

1700
class _RawImageResourceState extends State<RawImageResource> {
1701 1702 1703 1704
  void initState() {
    super.initState();
    config.image.addListener(_handleImageChanged);
  }
1705

1706
  ImageInfo _resolvedImage;
1707

1708
  void _handleImageChanged(ImageInfo resolvedImage) {
1709 1710
    setState(() {
      _resolvedImage = resolvedImage;
1711 1712 1713
    });
  }

1714 1715 1716
  void dispose() {
    config.image.removeListener(_handleImageChanged);
    super.dispose();
1717 1718
  }

1719
  void didUpdateConfig(RawImageResource oldConfig) {
1720 1721 1722 1723
    if (config.image != oldConfig.image) {
      oldConfig.image.removeListener(_handleImageChanged);
      config.image.addListener(_handleImageChanged);
    }
1724 1725
  }

1726
  Widget build(BuildContext context) {
1727
    return new RawImage(
1728
      image: _resolvedImage?.image,
1729 1730
      width: config.width,
      height: config.height,
1731
      scale: _resolvedImage == null ? 1.0 : _resolvedImage.scale,
Adam Barth's avatar
Adam Barth committed
1732
      color: config.color,
1733
      fit: config.fit,
1734
      alignment: config.alignment,
1735 1736
      repeat: config.repeat,
      centerSlice: config.centerSlice
1737
    );
1738 1739 1740
  }
}

1741
/// Displays an image loaded from the network.
1742
class NetworkImage extends StatelessComponent {
1743 1744 1745 1746 1747
  NetworkImage({
    Key key,
    this.src,
    this.width,
    this.height,
1748
    this.scale : 1.0,
Adam Barth's avatar
Adam Barth committed
1749
    this.color,
1750
    this.fit,
1751
    this.alignment,
1752 1753
    this.repeat: ImageRepeat.noRepeat,
    this.centerSlice
1754
  }) : super(key: key);
1755

1756
  /// The URL from which to load the image.
1757
  final String src;
1758 1759 1760 1761 1762

  /// 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.
1763
  final double width;
1764 1765 1766 1767 1768

  /// 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.
1769
  final double height;
1770

1771 1772 1773 1774 1775
  /// Specifies the image's scale.
  ///
  /// Used when determining the best display size for the image.
  final double scale;

Adam Barth's avatar
Adam Barth committed
1776 1777
  /// If non-null, apply this color filter to the image before painting.
  final Color color;
1778 1779

  /// How to inscribe the image into the place allocated during layout.
1780
  final ImageFit fit;
1781 1782 1783 1784 1785 1786

  /// 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.
1787
  final FractionalOffset alignment;
1788 1789

  /// How to paint any portions of the layout bounds not covered by the image.
1790
  final ImageRepeat repeat;
1791 1792 1793 1794 1795 1796 1797 1798

  /// 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.
1799
  final Rect centerSlice;
1800

1801
  Widget build(BuildContext context) {
1802
    return new RawImageResource(
1803
      image: imageCache.load(src, scale: scale),
1804 1805
      width: width,
      height: height,
Adam Barth's avatar
Adam Barth committed
1806
      color: color,
1807
      fit: fit,
1808
      alignment: alignment,
1809 1810
      repeat: repeat,
      centerSlice: centerSlice
1811 1812
    );
  }
1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833

  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
    description.add('src: $src');
    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');
  }
1834 1835
}

1836 1837 1838 1839
/// Sets a default asset bundle for its descendants.
///
/// For example, used by [AssetImage] to determine which bundle to use if no
/// bundle is specified explicitly.
Adam Barth's avatar
Adam Barth committed
1840 1841 1842 1843 1844 1845 1846 1847 1848 1849
class DefaultAssetBundle extends InheritedWidget {
  DefaultAssetBundle({
    Key key,
    this.bundle,
    Widget child
  }) : super(key: key, child: child) {
    assert(bundle != null);
    assert(child != null);
  }

1850
  /// The bundle to use as a default.
Adam Barth's avatar
Adam Barth committed
1851 1852
  final AssetBundle bundle;

1853
  /// The bundle from the closest instance of this class that encloses the given context.
Adam Barth's avatar
Adam Barth committed
1854
  static AssetBundle of(BuildContext context) {
Ian Hickson's avatar
Ian Hickson committed
1855
    DefaultAssetBundle result = context.inheritFromWidgetOfExactType(DefaultAssetBundle);
Adam Barth's avatar
Adam Barth committed
1856 1857 1858 1859 1860 1861
    return result?.bundle;
  }

  bool updateShouldNotify(DefaultAssetBundle old) => bundle != old.bundle;
}

1862 1863 1864 1865 1866
/// Displays an image provided by an [ImageProvider].
///
/// This widget lets you customize how images are loaded by supplying your own
/// image provider. Internally, [NetworkImage] uses an [ImageProvider] that
/// loads the image from the network.
1867 1868
class AsyncImage extends StatelessComponent {
  AsyncImage({
Ali Ghassemi's avatar
Ali Ghassemi committed
1869
    Key key,
1870
    this.provider,
Ali Ghassemi's avatar
Ali Ghassemi committed
1871 1872
    this.width,
    this.height,
Adam Barth's avatar
Adam Barth committed
1873
    this.color,
Ali Ghassemi's avatar
Ali Ghassemi committed
1874
    this.fit,
1875
    this.alignment,
Ali Ghassemi's avatar
Ali Ghassemi committed
1876 1877 1878
    this.repeat: ImageRepeat.noRepeat,
    this.centerSlice
  }) : super(key: key);
1879

1880
  /// The object that will provide the image.
1881
  final ImageProvider provider;
1882 1883 1884 1885 1886

  /// 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.
1887
  final double width;
1888 1889 1890 1891 1892

  /// 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.
1893
  final double height;
1894

Adam Barth's avatar
Adam Barth committed
1895 1896
  /// If non-null, apply this color filter to the image before painting.
  final Color color;
1897 1898

  /// How to inscribe the image into the place allocated during layout.
1899
  final ImageFit fit;
1900 1901 1902 1903 1904 1905

  /// 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.
1906
  final FractionalOffset alignment;
1907 1908

  /// How to paint any portions of the layout bounds not covered by the image.
1909
  final ImageRepeat repeat;
1910 1911 1912 1913 1914 1915 1916 1917

  /// 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.
1918 1919 1920
  final Rect centerSlice;

  Widget build(BuildContext context) {
1921
    return new RawImageResource(
1922
      image: imageCache.loadProvider(provider),
Ali Ghassemi's avatar
Ali Ghassemi committed
1923 1924
      width: width,
      height: height,
Adam Barth's avatar
Adam Barth committed
1925
      color: color,
Ali Ghassemi's avatar
Ali Ghassemi committed
1926
      fit: fit,
1927
      alignment: alignment,
Ali Ghassemi's avatar
Ali Ghassemi committed
1928 1929 1930
      repeat: repeat,
      centerSlice: centerSlice
    );
1931
  }
1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950

  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
    description.add('provider: $provider');
    if (width != null)
      description.add('width: $width');
    if (height != null)
      description.add('height: $height');
    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');
  }
1951 1952
}

1953 1954
/// Displays an image from an [AssetBundle].
///
1955
/// By default, asset image will load the image from the closest enclosing
1956
/// [DefaultAssetBundle].
1957
class AssetImage extends StatelessComponent {
1958 1959 1960
  // Don't add asserts here unless absolutely necessary, since it will
  // require removing the const constructor, which is an API change.
  const AssetImage({
1961 1962 1963 1964 1965
    Key key,
    this.name,
    this.bundle,
    this.width,
    this.height,
Adam Barth's avatar
Adam Barth committed
1966
    this.color,
1967
    this.fit,
1968
    this.alignment,
1969 1970
    this.repeat: ImageRepeat.noRepeat,
    this.centerSlice
1971
  }) : super(key: key);
1972

1973
  /// The name of the image in the assert bundle.
1974
  final String name;
1975 1976 1977 1978 1979

  /// The bundle from which to load the image.
  ///
  /// If null, the image will be loaded from the closest enclosing
  /// [DefaultAssetBundle].
1980
  final AssetBundle bundle;
1981 1982 1983 1984 1985

  /// 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.
1986
  final double width;
1987 1988 1989 1990 1991

  /// 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.
1992
  final double height;
1993

Adam Barth's avatar
Adam Barth committed
1994 1995
  /// If non-null, apply this color filter to the image before painting.
  final Color color;
1996 1997

  /// How to inscribe the image into the place allocated during layout.
1998
  final ImageFit fit;
1999 2000 2001 2002 2003 2004

  /// 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.
2005
  final FractionalOffset alignment;
2006 2007

  /// How to paint any portions of the layout bounds not covered by the image.
2008
  final ImageRepeat repeat;
2009 2010 2011 2012 2013 2014 2015 2016

  /// 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.
2017
  final Rect centerSlice;
2018

2019
  Widget build(BuildContext context) {
2020
    return new RawImageResource(
Adam Barth's avatar
Adam Barth committed
2021
      image: (bundle ?? DefaultAssetBundle.of(context)).loadImage(name),
2022 2023
      width: width,
      height: height,
Adam Barth's avatar
Adam Barth committed
2024
      color: color,
2025
      fit: fit,
2026
      alignment: alignment,
2027 2028
      repeat: repeat,
      centerSlice: centerSlice
2029 2030
    );
  }
2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051

  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
    description.add('name: $name');
    if (width != null)
      description.add('width: $width');
    if (height != null)
      description.add('height: $height');
    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');
    if (bundle != null)
      description.add('bundle: $bundle');
  }
2052 2053
}

2054 2055 2056 2057 2058
/// 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.
Hixie's avatar
Hixie committed
2059
class WidgetToRenderBoxAdapter extends LeafRenderObjectWidget {
2060
  WidgetToRenderBoxAdapter({ RenderBox renderBox, this.onBuild })
Hixie's avatar
Hixie committed
2061 2062 2063 2064 2065 2066 2067 2068 2069
    : 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);
  }

2070
  /// The render box to place in the widget tree.
Hixie's avatar
Hixie committed
2071 2072
  final RenderBox renderBox;

2073 2074 2075 2076 2077 2078
  /// 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;

Hixie's avatar
Hixie committed
2079
  RenderBox createRenderObject() => renderBox;
2080 2081 2082 2083 2084

  void updateRenderObject(RenderBox renderObject, WidgetToRenderBoxAdapter oldWidget) {
    if (onBuild != null)
      onBuild();
  }
Hixie's avatar
Hixie committed
2085 2086
}

2087

2088
// EVENT HANDLING
2089

2090 2091 2092 2093 2094 2095 2096
class Listener extends OneChildRenderObjectWidget {
  Listener({
    Key key,
    Widget child,
    this.onPointerDown,
    this.onPointerMove,
    this.onPointerUp,
2097 2098 2099 2100 2101
    this.onPointerCancel,
    this.behavior: HitTestBehavior.deferToChild
  }) : super(key: key, child: child) {
    assert(behavior != null);
  }
2102

Ian Hickson's avatar
Ian Hickson committed
2103 2104 2105 2106
  final PointerDownEventListener onPointerDown;
  final PointerMoveEventListener onPointerMove;
  final PointerUpEventListener onPointerUp;
  final PointerCancelEventListener onPointerCancel;
2107
  final HitTestBehavior behavior;
2108 2109 2110 2111 2112

  RenderPointerListener createRenderObject() => new RenderPointerListener(
    onPointerDown: onPointerDown,
    onPointerMove: onPointerMove,
    onPointerUp: onPointerUp,
2113 2114
    onPointerCancel: onPointerCancel,
    behavior: behavior
2115
  );
2116

2117 2118 2119 2120 2121
  void updateRenderObject(RenderPointerListener renderObject, Listener oldWidget) {
    renderObject.onPointerDown = onPointerDown;
    renderObject.onPointerMove = onPointerMove;
    renderObject.onPointerUp = onPointerUp;
    renderObject.onPointerCancel = onPointerCancel;
2122
    renderObject.behavior = behavior;
2123
  }
2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150

  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;
    }
  }
2151
}
2152

2153 2154 2155 2156 2157 2158 2159 2160 2161
/// 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.
2162 2163 2164
class RepaintBoundary extends OneChildRenderObjectWidget {
  RepaintBoundary({ Key key, Widget child }) : super(key: key, child: child);
  RenderRepaintBoundary createRenderObject() => new RenderRepaintBoundary();
2165 2166
}

2167
class IgnorePointer extends OneChildRenderObjectWidget {
Hixie's avatar
Hixie committed
2168
  IgnorePointer({ Key key, Widget child, this.ignoring: true, this.ignoringSemantics })
2169
    : super(key: key, child: child);
2170 2171

  final bool ignoring;
Hixie's avatar
Hixie committed
2172
  final bool ignoringSemantics; // if null, defaults to value of ignoring
2173

Hixie's avatar
Hixie committed
2174 2175 2176 2177
  RenderIgnorePointer createRenderObject() => new RenderIgnorePointer(
    ignoring: ignoring,
    ignoringSemantics: ignoringSemantics
  );
2178

2179
  void updateRenderObject(RenderIgnorePointer renderObject, IgnorePointer oldWidget) {
2180
    renderObject.ignoring = ignoring;
Hixie's avatar
Hixie committed
2181
    renderObject.ignoringSemantics = ignoringSemantics;
2182
  }
2183
}
2184

2185 2186 2187

// UTILITY NODES

Hixie's avatar
Hixie committed
2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205
/// The Semantics widget annotates the widget tree with a description
/// of the meaning of the widgets, so that accessibility tools, search
/// engines, and other semantic analysis software can determine the
/// meaning of the application.
class Semantics extends OneChildRenderObjectWidget {
  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.
2206
  ///
Hixie's avatar
Hixie committed
2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238
  /// 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;

  RenderSemanticAnnotations createRenderObject() => new RenderSemanticAnnotations(
    container: container,
    checked: checked,
    label: label
  );

  void updateRenderObject(RenderSemanticAnnotations renderObject, Semantics oldWidget) {
    renderObject.container = container;
    renderObject.checked = checked;
    renderObject.label = label;
  }

  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
    description.add('container: $container');
    if (checked != null);
2239
      description.add('checked: $checked');
Hixie's avatar
Hixie committed
2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277
    if (label != null);
      description.add('label: "$label"');
  }
}

/// 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 component 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 OneChildRenderObjectWidget {
  MergeSemantics({ Key key, Widget child }) : super(key: key, child: child);
  RenderMergeSemantics createRenderObject() => new RenderMergeSemantics();
}

/// Drops all semantics in this subtree.
///
/// 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 OneChildRenderObjectWidget {
  ExcludeSemantics({ Key key, Widget child }) : super(key: key, child: child);
  RenderExcludeSemantics createRenderObject() => new RenderExcludeSemantics();
}

2278
class MetaData extends OneChildRenderObjectWidget {
Hixie's avatar
Hixie committed
2279 2280 2281 2282 2283 2284
  MetaData({
    Key key,
    Widget child,
    this.metaData,
    this.behavior: HitTestBehavior.deferToChild
  }) : super(key: key, child: child);
2285 2286

  final dynamic metaData;
Hixie's avatar
Hixie committed
2287
  final HitTestBehavior behavior;
2288

Hixie's avatar
Hixie committed
2289 2290 2291 2292
  RenderMetaData createRenderObject() => new RenderMetaData(
    metaData: metaData,
    behavior: behavior
  );
2293 2294

  void updateRenderObject(RenderMetaData renderObject, MetaData oldWidget) {
Hixie's avatar
Hixie committed
2295 2296 2297
    renderObject
      ..metaData = metaData
      ..behavior = behavior;
2298
  }
2299 2300 2301

  void debugFillDescription(List<String> description) {
    super.debugFillDescription(description);
Hixie's avatar
Hixie committed
2302 2303
    description.add('behavior: $behavior');
    description.add('metaData: $metaData');
2304
  }
2305
}
2306 2307 2308 2309 2310 2311 2312 2313

class KeyedSubtree extends StatelessComponent {
  KeyedSubtree({ Key key, this.child })
    : super(key: key);

  final Widget child;

  Widget build(BuildContext context) => child;
Adam Barth's avatar
Adam Barth committed
2314
}
2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329

class Builder extends StatelessComponent {
  Builder({ Key key, this.builder }) : super(key: key);
  final WidgetBuilder builder;
  Widget build(BuildContext context) => builder(context);
}

typedef Widget StatefulWidgetBuilder(BuildContext context, StateSetter setState);
class StatefulBuilder extends StatefulComponent {
  StatefulBuilder({ Key key, this.builder }) : super(key: key);
  final StatefulWidgetBuilder builder;
  _StatefulBuilderState createState() => new _StatefulBuilderState();
}
class _StatefulBuilderState extends State<StatefulBuilder> {
  Widget build(BuildContext context) => config.builder(context, setState);
2330
}