anchor.diff:    }
   }
   
  +class TableBorder extends Border {
  +  const TableBorder({
  +    BorderSide top: BorderSide.none,
  +    BorderSide right: BorderSide.none,
  +    BorderSide bottom: BorderSide.none,
  +    BorderSide left: BorderSide.none,
  +    this.horizontalInside: BorderSide.none,
  +    this.verticalInside: BorderSide.none
  +  }) : super(
  +    top: top,
  +    right: right,
  +    bottom: bottom,
  +    left: left
  +  );
  +
  +  factory TableBorder.all({
  +    Color color: const Color(0xFF000000),
  +    double width: 1.0
  +  }) {
  +    final BorderSide side = new BorderSide(color: color, width: width);
  +    return new TableBorder(top: side, right: side, bottom: side, left: side, horizontalInside: side, verticalInside: side);
  +  }
  +
  +  factory TableBorder.symmetric({
  +    BorderSide inside: BorderSide.none,
  +    BorderSide outside: BorderSide.none
  +  }) {
  +    return new TableBorder(
  +      top: outside,
  +      right: outside,
  +      bottom: outside,
  +      left: outside,
  +      horizontalInside: inside,
  +      verticalInside: inside
  +    );
  +  }
  +
  +  final BorderSide horizontalInside;
  +
  +  final BorderSide verticalInside;
  +
  +  @override
  +  TableBorder scale(double t) {
  +    return new TableBorder(
  +      top: top.copyWith(width: t * top.width),
  +      right: right.copyWith(width: t * right.width),
  +      bottom: bottom.copyWith(width: t * bottom.width),
  +      left: left.copyWith(width: t * left.width),
  +      horizontalInside: horizontalInside.copyWith(width: t * horizontalInside.width),
  +      verticalInside: verticalInside.copyWith(width: t * verticalInside.width)
  +    );
  +  }
  +
  +  static TableBorder lerp(TableBorder a, TableBorder b, double t) {
  +    if (a == null && b == null)
  +      return null;
  +    if (a == null)
  +      return b.scale(t);
  +    if (b == null)
  +      return a.scale(1.0 - t);
  +    return new TableBorder(
  +      top: BorderSide.lerp(a.top, b.top, t),
  +      right: BorderSide.lerp(a.right, b.right, t),
  +      bottom: BorderSide.lerp(a.bottom, b.bottom, t),
  +      left: BorderSide.lerp(a.left, b.left, t),
  +      horizontalInside: BorderSide.lerp(a.horizontalInside, b.horizontalInside, t),
  +      verticalInside: BorderSide.lerp(a.verticalInside, b.verticalInside, t)
  +    );
  +  }
  +
  +  @override
  +  bool operator ==(dynamic other) {
  +    if (super != other)
  +      return false;
  +    final TableBorder typedOther = other;
  +    return horizontalInside == typedOther.horizontalInside &&
  +           verticalInside == typedOther.verticalInside;
  +  }
  +
  +  @override
  +  int get hashCode => hashValues(super.hashCode, horizontalInside, verticalInside);
  +
  +  @override
  +  String toString() => 'TableBorder($top, $right, $bottom, $left, $horizontalInside, $verticalInside)';
  +}
  +
  +enum CellVerticalAlignment {
  +  /// Cells with this alignment are placed with their top at the top of the row.
  +  top,
  +
  +  /// Cells with this alignment are vertically centered in the row.
  +  middle,
  +
  +  /// Cells with this alignment are placed with their bottom at the bottom of the row.
  +  bottom,
  +
  +  /// Cells with this alignment are aligned such that they all share the same
  +  /// baseline. Cells with no baseline are top-aligned instead. The baseline
  +  /// used is specified by [RenderTable.baseline]. It is not valid to use the
  +  /// baseline value if [RenderTable.baseline] is not specified.
  +  baseline,
  +
  +  /// Cells with this alignment are sized to be as tall as the row, then made to fit the row.
  +  /// If all the cells have this alignment, then the row will have zero height.
  +  fill
  +}
  +
  +/// Parent data used by [RenderTable] for its children.
  +class TableCellParentData extends BoxParentData {
  +  CellVerticalAlignment verticalAlignment = CellVerticalAlignment.top;
  +
  +  @override
  +  String toString() => '${super.toString()}; $verticalAlignment';
  +}
  +
   class RenderTable extends RenderBox {
     RenderTable({
  -    int columns: 0,
  +    int columns,
       int rows,
       Map<int, ColumnWidth> columnWidths,
       ColumnWidth defaultColumnWidth: const FlexColumnWidth(1.0),
  +    TableBorder border,
  +    TextBaseline textBaseline,
       List<List<RenderBox>> children
     }) {
  -    assert(columns != null);
  -    assert(columns >= 0);
  +    assert(columns != null || (children != null && children.isNotEmpty));
  +    assert(columns == null || columns >= 0);
       assert(rows == null || rows >= 0);
       assert(rows == null || children == null);
       assert(defaultColumnWidth != null);
  -    _columns = columns;
  +    _columns = columns ?? children.first.length;
       _rows = rows ?? 0;
       _children = new List<RenderBox>()..length = _columns * _rows;
       _columnWidths = columnWidths ?? new HashMap<int, ColumnWidth>();
       _defaultColumnWidth = defaultColumnWidth;
  +    _border = border;
  +    _textBaseline = textBaseline;
       for (List<RenderBox> row in children)
         addRow(row);
     }
   
  -  // TODO(ianh): Add a 'borders' field on the table, to paint the borders.
  -  // use a Border subclass with "insideVertical" and "insideHorizontal" fields?
  -
     // TODO(ianh): Add a 'decoration' field to the children's parent data, to paint on each cell.
  -
  -  // TODO(ianh): Add a 'verticalAlignment' field to the children's parent data, to align the cells.
  -  // Values would be baseline, top, bottom, middle.
   
     // Children are stored in row-major order.
     // _children.length must be rows * columns
  
positive.code:   }
  
    @override
    void attach() {
      super.attach();
      for (RenderBox child in _children)
        child?.attach();
    }
  
    @override
  
negative.code: 
    @override
    void paint(PaintingContext context, Offset offset) {
      assert(_rowTops.length == rows);
      for (int index = 0; index < _children.length; index += 1) {
        RenderBox child = _children[index];
        if (child != null) {
  