Unverified Commit eb69f594 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Allow material button to grow as wide as its constraints allow (#19416)

parent 73960e75
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:math' as math;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
...@@ -166,7 +168,7 @@ class _RawMaterialButtonState extends State<RawMaterialButton> { ...@@ -166,7 +168,7 @@ class _RawMaterialButtonState extends State<RawMaterialButton> {
? (_highlight ? widget.highlightElevation : widget.elevation) ? (_highlight ? widget.highlightElevation : widget.elevation)
: widget.disabledElevation; : widget.disabledElevation;
Widget result = new ConstrainedBox( final Widget result = new ConstrainedBox(
constraints: widget.constraints, constraints: widget.constraints,
child: new Material( child: new Material(
elevation: elevation, elevation: elevation,
...@@ -194,29 +196,24 @@ class _RawMaterialButtonState extends State<RawMaterialButton> { ...@@ -194,29 +196,24 @@ class _RawMaterialButtonState extends State<RawMaterialButton> {
), ),
), ),
); );
BoxConstraints constraints; Size minSize;
switch (widget.materialTapTargetSize) { switch (widget.materialTapTargetSize) {
case MaterialTapTargetSize.padded: case MaterialTapTargetSize.padded:
constraints = const BoxConstraints(minWidth: 48.0, minHeight: 48.0); minSize = const Size(48.0, 48.0);
break; break;
case MaterialTapTargetSize.shrinkWrap: case MaterialTapTargetSize.shrinkWrap:
constraints = const BoxConstraints(); minSize = Size.zero;
break; break;
} }
result = new _ButtonRedirectingHitDetectionWidget(
constraints: constraints,
child: new Center(
child: result,
widthFactor: 1.0,
heightFactor: 1.0,
),
);
return new Semantics( return new Semantics(
container: true, container: true,
button: true, button: true,
enabled: widget.enabled, enabled: widget.enabled,
child: result, child: new _InputPadding(
minSize: minSize,
child: result,
),
); );
} }
} }
...@@ -446,40 +443,88 @@ class MaterialButton extends StatelessWidget { ...@@ -446,40 +443,88 @@ class MaterialButton extends StatelessWidget {
} }
} }
/// Redirects the position passed to [RenderBox.hitTest] to the center of the /// A widget to pad the area around a [MaterialButton]'s inner [Material].
/// widget if the child hit test would have failed otherwise. ///
/// /// Redirect taps that occur in the padded area around the child to the center
/// The primary purpose of this widget is to allow padding around [Material] widgets /// of the child. This increases the size of the button and the button's
/// to trigger the child ink feature without increasing the size of the material. /// "tap target", but not its material or its ink splashes.
class _ButtonRedirectingHitDetectionWidget extends SingleChildRenderObjectWidget { class _InputPadding extends SingleChildRenderObjectWidget {
const _ButtonRedirectingHitDetectionWidget({ const _InputPadding({
Key key, Key key,
Widget child, Widget child,
this.constraints this.minSize,
}) : super(key: key, child: child); }) : super(key: key, child: child);
final BoxConstraints constraints; final Size minSize;
@override @override
RenderObject createRenderObject(BuildContext context) { RenderObject createRenderObject(BuildContext context) {
return new _RenderButtonRedirectingHitDetection(constraints); return new _RenderInputPadding(minSize);
} }
@override @override
void updateRenderObject(BuildContext context, covariant _RenderButtonRedirectingHitDetection renderObject) { void updateRenderObject(BuildContext context, covariant _RenderInputPadding renderObject) {
renderObject.additionalConstraints = constraints; renderObject.minSize = minSize;
} }
} }
class _RenderButtonRedirectingHitDetection extends RenderConstrainedBox { class _RenderInputPadding extends RenderShiftedBox {
_RenderButtonRedirectingHitDetection (BoxConstraints additionalConstraints) : super(additionalConstraints: additionalConstraints); _RenderInputPadding(this._minSize, [RenderBox child]) : super(child) ;
Size get minSize => _minSize;
Size _minSize;
set minSize(Size value) {
if (_minSize == value)
return;
_minSize = value;
markNeedsLayout();
}
@override
double computeMinIntrinsicWidth(double height) {
if (child != null)
return math.max(child.computeMinIntrinsicWidth(height), minSize.width);
return 0.0;
}
@override
double computeMinIntrinsicHeight(double width) {
if (child != null)
return math.max(child.computeMinIntrinsicHeight(width), minSize.height);
return 0.0;
}
@override
double computeMaxIntrinsicWidth(double height) {
if (child != null)
return math.max(child.computeMaxIntrinsicWidth(height), minSize.width);
return 0.0;
}
@override
double computeMaxIntrinsicHeight(double width) {
if (child != null)
return math.max(child.computeMaxIntrinsicHeight(width), minSize.height);
return 0.0;
}
@override
void performLayout() {
if (child != null) {
child.layout(constraints, parentUsesSize: true);
final double height = math.max(child.size.width, minSize.width);
final double width = math.max(child.size.height, minSize.height);
size = constraints.constrain(new Size(height, width));
final BoxParentData childParentData = child.parentData;
childParentData.offset = Alignment.center.alongOffset(size - child.size);
} else {
size = Size.zero;
}
}
@override @override
bool hitTest(HitTestResult result, {Offset position}) { bool hitTest(HitTestResult result, {Offset position}) {
if (!size.contains(position)) return super.hitTest(result, position: position) ||
return false; child.hitTest(result, position: child.size.center(Offset.zero));
if (child.hitTest(result, position: position))
return true;
return child.hitTest(result, position: size.center(Offset.zero));
} }
} }
...@@ -66,14 +66,16 @@ void main() { ...@@ -66,14 +66,16 @@ void main() {
const Color fillColor = const Color(0xFFEF5350); const Color fillColor = const Color(0xFFEF5350);
await tester.pumpWidget( await tester.pumpWidget(
new RawMaterialButton( new Center(
materialTapTargetSize: MaterialTapTargetSize.padded, child: new RawMaterialButton(
onPressed: () {}, materialTapTargetSize: MaterialTapTargetSize.padded,
fillColor: fillColor, onPressed: () {},
highlightColor: highlightColor, fillColor: fillColor,
splashColor: splashColor, highlightColor: highlightColor,
child: const SizedBox(), splashColor: splashColor,
) child: const SizedBox(),
),
),
); );
final Offset center = tester.getCenter(find.byType(InkWell)); final Offset center = tester.getCenter(find.byType(InkWell));
...@@ -93,14 +95,16 @@ void main() { ...@@ -93,14 +95,16 @@ void main() {
const Color fillColor = const Color(0xFFEF5350); const Color fillColor = const Color(0xFFEF5350);
await tester.pumpWidget( await tester.pumpWidget(
new RawMaterialButton( new Center(
materialTapTargetSize: MaterialTapTargetSize.padded, child: new RawMaterialButton(
onPressed: () {}, materialTapTargetSize: MaterialTapTargetSize.padded,
fillColor: fillColor, onPressed: () {},
highlightColor: highlightColor, fillColor: fillColor,
splashColor: splashColor, highlightColor: highlightColor,
child: const SizedBox(), splashColor: splashColor,
) child: const SizedBox(),
),
),
); );
final Offset top = tester.getRect(find.byType(InkWell)).topCenter; final Offset top = tester.getRect(find.byType(InkWell)).topCenter;
...@@ -117,6 +121,7 @@ void main() { ...@@ -117,6 +121,7 @@ void main() {
await tester.pumpWidget( await tester.pumpWidget(
new MaterialApp( new MaterialApp(
home: new Column( home: new Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[ children: <Widget>[
new RawMaterialButton( new RawMaterialButton(
materialTapTargetSize: MaterialTapTargetSize.padded, materialTapTargetSize: MaterialTapTargetSize.padded,
...@@ -165,4 +170,24 @@ void main() { ...@@ -165,4 +170,24 @@ void main() {
); );
expect(find.byKey(key).hitTestable(), findsOneWidget); expect(find.byKey(key).hitTestable(), findsOneWidget);
}); });
}
\ No newline at end of file testWidgets('RawMaterialButton can be expanded by parent constraints', (WidgetTester tester) async {
const Key key = const Key('test');
await tester.pumpWidget(
new MaterialApp(
home: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new RawMaterialButton(
key: key,
onPressed: () {},
child: const SizedBox(),
)
],
),
),
);
expect(tester.getSize(find.byKey(key)), const Size(800.0, 48.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