From f38743593d00f13b21f49d8d8ee2d2206bf820d3 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Tue, 15 May 2018 19:37:21 -0700 Subject: [PATCH] Add Spacer Widget (#16642) This adds a simple spacer widget first suggested by Scott Stoll (@ScottS2017) in the abandoned PR #15802 This PR replaces that one, cleans it up, and adds a test. --- packages/flutter/lib/src/widgets/basic.dart | 5 ++ packages/flutter/lib/src/widgets/spacer.dart | 67 +++++++++++++++++++ packages/flutter/lib/widgets.dart | 1 + .../flutter/test/widgets/spacer_test.dart | 55 +++++++++++++++ 4 files changed, 128 insertions(+) create mode 100644 packages/flutter/lib/src/widgets/spacer.dart create mode 100644 packages/flutter/test/widgets/spacer_test.dart diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart index 8e71298974..616fdd36c8 100644 --- a/packages/flutter/lib/src/widgets/basic.dart +++ b/packages/flutter/lib/src/widgets/basic.dart @@ -3233,6 +3233,7 @@ class PositionedDirectional extends StatelessWidget { /// * [Column], for a version of this widget that is always vertical. /// * [Expanded], to indicate children that should take all the remaining room. /// * [Flexible], to indicate children that should share the remaining room but +/// * [Spacer], a widget that takes up space proportional to it's flex value. /// that may be sized smaller (leaving some remaining room unused). /// * The [catalog of layout widgets](https://flutter.io/widgets/layout/). class Flex extends MultiChildRenderObjectWidget { @@ -3565,6 +3566,7 @@ class Flex extends MultiChildRenderObjectWidget { /// * [Expanded], to indicate children that should take all the remaining room. /// * [Flexible], to indicate children that should share the remaining room but /// that may by sized smaller (leaving some remaining room unused). +/// * [Spacer], a widget that takes up space proportional to it's flex value. /// * The [catalog of layout widgets](https://flutter.io/widgets/layout/). class Row extends Flex { /// Creates a horizontal array of children. @@ -3759,6 +3761,7 @@ class Row extends Flex { /// that may size smaller (leaving some remaining room unused). /// * [SingleChildScrollView], whose documentation discusses some ways to /// use a [Column] inside a scrolling container. +/// * [Spacer], a widget that takes up space proportional to it's flex value. /// * The [catalog of layout widgets](https://flutter.io/widgets/layout/). class Column extends Flex { /// Creates a vertical array of children. @@ -3810,6 +3813,7 @@ class Column extends Flex { /// See also: /// /// * [Expanded], which forces the child to expand to fill the available space. +/// * [Spacer], a widget that takes up space proportional to it's flex value. /// * The [catalog of layout widgets](https://flutter.io/widgets/layout/). class Flexible extends ParentDataWidget { /// Creates a widget that controls how a child of a [Row], [Column], or [Flex] @@ -3883,6 +3887,7 @@ class Flexible extends ParentDataWidget { /// See also: /// /// * [Flexible], which does not force the child to fill the available space. +/// * [Spacer], a widget that takes up space proportional to it's flex value. /// * The [catalog of layout widgets](https://flutter.io/widgets/layout/). class Expanded extends Flexible { /// Creates a widget that expands a child of a [Row], [Column], or [Flex] diff --git a/packages/flutter/lib/src/widgets/spacer.dart b/packages/flutter/lib/src/widgets/spacer.dart new file mode 100644 index 0000000000..3df7dd991a --- /dev/null +++ b/packages/flutter/lib/src/widgets/spacer.dart @@ -0,0 +1,67 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/rendering.dart'; + +import 'basic.dart'; +import 'framework.dart'; + +/// Spacer creates an adjustable, empty spacer that can be used to tune the +/// spacing between widgets in a [Flex] container, like [Row] or [Column]. +/// +/// The [Spacer] widget will take up any available space, so setting the +/// [Flex.mainAxisAlignment] on a flex container that contains a [Spacer] to +/// [MainAxisAlignment.spaceAround], [MainAxisAlignment.spaceBetween], or +/// [MainAxisAlignment.spaceEvenly] will not have any visible effect: the +/// [Spacer] has taken up all of the additional space, so there is none left to +/// redistribute. +/// +/// ## Sample code +/// +/// ```dart +/// new Row( +/// children: [ +/// new Text('Begin'), +/// new Spacer(), // Defaults to a flex of one. +/// new Text('Middle'), +/// // Gives twice the space between Middle and End than Begin and Middle. +/// new Spacer(flex: 2), +/// new Text('End'), +/// ], +/// ) +/// ``` +/// +/// See also: +/// +/// * [Row] and [Column], which are the most common containers to use a Spacer +/// in. +/// * [SizedBox], to create a box with a specific size and an optional child. +class Spacer extends StatelessWidget { + /// Creates a flexible space to insert into a [Flexible] widget. + /// + /// The [flex] parameter may not be null or less than one. + const Spacer({Key key, this.flex: 1}) + : assert(flex != null), + assert(flex > 0), + super(key: key); + + /// The flex factor to use in determining how much space to take up. + /// + /// The amount of space the [Spacer] can occupy in the main axis is determined + /// by dividing the free space proportionately, after placing the inflexible + /// children, according to the flex factors of the flexible children. + /// + /// Defaults to one. + final int flex; + + @override + Widget build(BuildContext context) { + return new Expanded( + flex: flex, + child: new ConstrainedBox( + constraints: const BoxConstraints.expand(), + ), + ); + } +} \ No newline at end of file diff --git a/packages/flutter/lib/widgets.dart b/packages/flutter/lib/widgets.dart index 7eb216b8e5..95aad444ad 100644 --- a/packages/flutter/lib/widgets.dart +++ b/packages/flutter/lib/widgets.dart @@ -86,6 +86,7 @@ export 'src/widgets/size_changed_layout_notifier.dart'; export 'src/widgets/sliver.dart'; export 'src/widgets/sliver_persistent_header.dart'; export 'src/widgets/sliver_prototype_extent_list.dart'; +export 'src/widgets/spacer.dart'; export 'src/widgets/status_transitions.dart'; export 'src/widgets/table.dart'; export 'src/widgets/text.dart'; diff --git a/packages/flutter/test/widgets/spacer_test.dart b/packages/flutter/test/widgets/spacer_test.dart new file mode 100644 index 0000000000..b9c0ed0d36 --- /dev/null +++ b/packages/flutter/test/widgets/spacer_test.dart @@ -0,0 +1,55 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/widgets.dart'; + +void main() { + testWidgets('Spacer takes up space.', (WidgetTester tester) async { + await tester.pumpWidget(new Column( + children: const [ + const SizedBox(width: 10.0, height: 10.0), + const Spacer(), + const SizedBox(width: 10.0, height: 10.0), + ], + )); + final Rect spacerRect = tester.getRect(find.byType(Spacer)); + expect(spacerRect.size, const Size(800.0, 580.0)); + expect(spacerRect.topLeft, const Offset(0.0, 10.0)); + }); + + testWidgets('Spacer takes up space proportional to flex.', (WidgetTester tester) async { + const Spacer spacer1 = const Spacer(); + const Spacer spacer2 = const Spacer(flex: 1); + const Spacer spacer3 = const Spacer(flex: 2); + const Spacer spacer4 = const Spacer(flex: 4); + await tester.pumpWidget(new Row( + textDirection: TextDirection.rtl, + children: const [ + const SizedBox(width: 10.0, height: 10.0), + spacer1, + const SizedBox(width: 10.0, height: 10.0), + spacer2, + const SizedBox(width: 10.0, height: 10.0), + spacer3, + const SizedBox(width: 10.0, height: 10.0), + spacer4, + const SizedBox(width: 10.0, height: 10.0), + ], + )); + final Rect spacer1Rect = tester.getRect(find.byType(Spacer).at(0)); + final Rect spacer2Rect = tester.getRect(find.byType(Spacer).at(1)); + final Rect spacer3Rect = tester.getRect(find.byType(Spacer).at(2)); + final Rect spacer4Rect = tester.getRect(find.byType(Spacer).at(3)); + expect(spacer1Rect.size.height, 600.0); + expect(spacer1Rect.size.width, closeTo(93.8, 0.1)); + expect(spacer1Rect.left, closeTo(696.3, 0.1)); + expect(spacer2Rect.size.width, closeTo(93.8, 0.1)); + expect(spacer2Rect.left, closeTo(592.5, 0.1)); + expect(spacer3Rect.size.width, spacer2Rect.size.width * 2.0); + expect(spacer3Rect.left, closeTo(395.0, 0.1)); + expect(spacer4Rect.size.width, spacer3Rect.size.width * 2.0); + expect(spacer4Rect.left, closeTo(10.0, 0.1)); + }); +}