Commit 25164e10 authored by Hans Muller's avatar Hans Muller

Make IconButton gesture targets as big as possible (#3423)

* Make IconButtons as big as possible
parent 1b9476c4
...@@ -174,6 +174,8 @@ class _ButtonsDemoState extends State<ButtonsDemo> { ...@@ -174,6 +174,8 @@ class _ButtonsDemoState extends State<ButtonsDemo> {
icon: Icons.thumb_up icon: Icons.thumb_up
) )
] ]
.map((Widget button) => new SizedBox(width: 64.0, height: 64.0, child: button))
.toList()
) )
); );
} }
......
...@@ -38,6 +38,8 @@ class IconButton extends StatelessWidget { ...@@ -38,6 +38,8 @@ class IconButton extends StatelessWidget {
const IconButton({ const IconButton({
Key key, Key key,
this.size: 24.0, this.size: 24.0,
this.padding: const EdgeInsets.all(8.0),
this.alignment: FractionalOffset.center,
this.icon, this.icon,
this.color, this.color,
this.disabledColor, this.disabledColor,
...@@ -46,11 +48,15 @@ class IconButton extends StatelessWidget { ...@@ -46,11 +48,15 @@ class IconButton extends StatelessWidget {
}) : super(key: key); }) : super(key: key);
/// The size of the icon inside the button. /// The size of the icon inside the button.
///
/// The button itself will be larger than the icon by 8.0 logical pixels in
/// each direction.
final double size; final double size;
/// The padding around the button's icon. The entire padded icon will react
/// to input gestures.
final EdgeInsets padding;
/// Defines how the icon is positioned within the IconButton.
final FractionalOffset alignment;
/// The icon to display inside the button. /// The icon to display inside the button.
final IconData icon; final IconData icon;
...@@ -90,11 +96,18 @@ class IconButton extends StatelessWidget { ...@@ -90,11 +96,18 @@ class IconButton extends StatelessWidget {
else else
currentColor = disabledColor ?? Theme.of(context).disabledColor; currentColor = disabledColor ?? Theme.of(context).disabledColor;
Widget result = new Padding( Widget result = new Padding(
padding: const EdgeInsets.all(8.0), padding: padding,
child: new Icon( child: new LimitedBox(
size: size, maxWidth: size,
icon: icon, maxHeight: size,
color: currentColor child: new Align(
alignment: alignment,
child: new Icon(
size: size,
icon: icon,
color: currentColor
)
)
) )
); );
if (tooltip != null) { if (tooltip != null) {
......
...@@ -460,6 +460,7 @@ class ScaffoldState extends State<Scaffold> { ...@@ -460,6 +460,7 @@ class ScaffoldState extends State<Scaffold> {
if (config.drawer != null) { if (config.drawer != null) {
leading = new IconButton( leading = new IconButton(
icon: Icons.menu, icon: Icons.menu,
alignment: FractionalOffset.centerLeft,
onPressed: openDrawer, onPressed: openDrawer,
tooltip: 'Open navigation menu' // TODO(ianh): Figure out how to localize this string tooltip: 'Open navigation menu' // TODO(ianh): Figure out how to localize this string
); );
...@@ -468,6 +469,7 @@ class ScaffoldState extends State<Scaffold> { ...@@ -468,6 +469,7 @@ class ScaffoldState extends State<Scaffold> {
if (_shouldShowBackArrow) { if (_shouldShowBackArrow) {
leading = new IconButton( leading = new IconButton(
icon: Icons.arrow_back, icon: Icons.arrow_back,
alignment: FractionalOffset.centerLeft,
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
tooltip: 'Back' // TODO(ianh): Figure out how to localize this string tooltip: 'Back' // TODO(ianh): Figure out how to localize this string
); );
......
...@@ -248,6 +248,101 @@ class RenderConstrainedBox extends RenderProxyBox { ...@@ -248,6 +248,101 @@ class RenderConstrainedBox extends RenderProxyBox {
} }
} }
/// Constrains the child's maxWidth and maxHeight if they're otherwise
/// unconstrained.
class RenderLimitedBox extends RenderProxyBox {
RenderLimitedBox({
RenderBox child,
double maxWidth: double.INFINITY,
double maxHeight: double.INFINITY
}) : _maxWidth = maxWidth, _maxHeight = maxHeight, super(child) {
assert(maxWidth != null && maxWidth >= 0.0);
assert(maxHeight != null && maxHeight >= 0.0);
}
/// The value to use for maxWidth if the incoming maxWidth constraint is infinite.
double get maxWidth => _maxWidth;
double _maxWidth;
void set maxWidth (double value) {
assert(value != null && value >= 0.0);
if (_maxWidth == value)
return;
_maxWidth = value;
markNeedsLayout();
}
/// The value to use for maxHeight if the incoming maxHeight constraint is infinite.
double get maxHeight => _maxHeight;
double _maxHeight;
void set maxHeight (double value) {
assert(value != null && value >= 0.0);
if (_maxHeight == value)
return;
_maxHeight = value;
markNeedsLayout();
}
BoxConstraints _limitConstraints(BoxConstraints constraints) {
return new BoxConstraints(
minWidth: constraints.minWidth,
maxWidth: constraints.hasBoundedWidth ? constraints.maxWidth : constraints.constrainWidth(maxWidth),
minHeight: constraints.minHeight,
maxHeight: constraints.hasBoundedHeight ? constraints.maxHeight : constraints.constrainHeight(maxHeight)
);
}
@override
double getMinIntrinsicWidth(BoxConstraints constraints) {
assert(constraints.debugAssertIsValid());
if (child != null)
return child.getMinIntrinsicWidth(_limitConstraints(constraints));
return _limitConstraints(constraints).constrainWidth(0.0);
}
@override
double getMaxIntrinsicWidth(BoxConstraints constraints) {
assert(constraints.debugAssertIsValid());
if (child != null)
return child.getMaxIntrinsicWidth(_limitConstraints(constraints));
return _limitConstraints(constraints).constrainWidth(0.0);
}
@override
double getMinIntrinsicHeight(BoxConstraints constraints) {
assert(constraints.debugAssertIsValid());
if (child != null)
return child.getMinIntrinsicHeight(_limitConstraints(constraints));
return _limitConstraints(constraints).constrainHeight(0.0);
}
@override
double getMaxIntrinsicHeight(BoxConstraints constraints) {
assert(constraints.debugAssertIsValid());
if (child != null)
return child.getMaxIntrinsicHeight(_limitConstraints(constraints));
return _limitConstraints(constraints).constrainHeight(0.0);
}
@override
void performLayout() {
if (child != null) {
child.layout(_limitConstraints(constraints), parentUsesSize: true);
size = constraints.constrain(child.size);
} else {
size = _limitConstraints(constraints).constrain(Size.zero);
}
}
@override
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
if (maxWidth != double.INFINITY)
description.add('maxWidth: $maxWidth');
if (maxHeight != double.INFINITY)
description.add('maxHeight: $maxHeight');
}
}
/// Attempts to size the child to a specific aspect ratio. /// Attempts to size the child to a specific aspect ratio.
/// ///
/// The render object first tries the largest width permited by the layout /// The render object first tries the largest width permited by the layout
......
...@@ -724,6 +724,47 @@ class FractionallySizedBox extends SingleChildRenderObjectWidget { ...@@ -724,6 +724,47 @@ class FractionallySizedBox extends SingleChildRenderObjectWidget {
} }
} }
/// A box that limits its size only when it's unconstrained.
///
/// If this widget's maximum width is unconstrained then its child's width is
/// limited to maxWidth. Similarly, if this widget's maximum height is unconstrained
/// then its child's height is limited to to maxHeight.
class LimitedBox extends SingleChildRenderObjectWidget {
LimitedBox({ Key key, this.maxWidth: double.INFINITY, this.maxHeight: double.INFINITY, Widget child })
: super(key: key, child: child) {
assert(maxWidth != null && maxWidth >= 0.0);
assert(maxHeight != null && maxHeight >= 0.0);
}
/// The maximum width limit to apply in the absence of a maxWidth constraint.
final double maxWidth;
/// The maximum height limit to apply in the absence of a maxHeight constraint.
final double maxHeight;
@override
RenderLimitedBox createRenderObject(BuildContext context) => new RenderLimitedBox(
maxWidth: maxWidth,
maxHeight: maxHeight
);
@override
void updateRenderObject(BuildContext context, RenderLimitedBox renderObject) {
renderObject
..maxWidth = maxWidth
..maxHeight = maxHeight;
}
@override
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
if (maxWidth != double.INFINITY)
description.add('maxWidth: $maxWidth');
if (maxHeight != double.INFINITY)
description.add('maxHeight: $maxHeight');
}
}
/// A render object that imposes different constraints on its child than it gets /// A render object that imposes different constraints on its child than it gets
/// from its parent, possibly allowing the child to overflow the parent. /// from its parent, possibly allowing the child to overflow the parent.
/// ///
......
// Copyright 2016 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/material.dart';
import 'package:flutter/rendering.dart';
import 'package:test/test.dart';
import 'rendering_tester.dart';
void main() {
test('parent max size is unconstrained', () {
RenderBox child = new RenderConstrainedBox(
additionalConstraints: new BoxConstraints.tightFor(width: 300.0, height: 400.0)
);
RenderBox parent = new RenderConstrainedOverflowBox(
minWidth: 0.0,
maxWidth: double.INFINITY,
minHeight: 0.0,
maxHeight: double.INFINITY,
child: new RenderLimitedBox(
maxWidth: 100.0,
maxHeight: 200.0,
child: child
)
);
layout(parent);
expect(child.size.width, 100.0);
expect(child.size.height, 200.0);
});
test('parent maxWidth is unconstrained', () {
RenderBox child = new RenderConstrainedBox(
additionalConstraints: new BoxConstraints.tightFor(width: 300.0, height: 400.0)
);
RenderBox parent = new RenderConstrainedOverflowBox(
minWidth: 0.0,
maxWidth: double.INFINITY,
minHeight: 500.0,
maxHeight: 500.0,
child: new RenderLimitedBox(
maxWidth: 100.0,
maxHeight: 200.0,
child: child
)
);
layout(parent);
expect(child.size.width, 100.0);
expect(child.size.height, 500.0);
});
test('parent maxHeight is unconstrained', () {
RenderBox child = new RenderConstrainedBox(
additionalConstraints: new BoxConstraints.tightFor(width: 300.0, height: 400.0)
);
RenderBox parent = new RenderConstrainedOverflowBox(
minWidth: 500.0,
maxWidth: 500.0,
minHeight: 0.0,
maxHeight: double.INFINITY,
child: new RenderLimitedBox(
maxWidth: 100.0,
maxHeight: 200.0,
child: child
)
);
layout(parent);
expect(child.size.width, 500.0);
expect(child.size.height, 200.0);
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment