// Copyright 2014 The Flutter 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_test/flutter_test.dart'; void main() { testWidgets('ButtonBar default control smoketest', (WidgetTester tester) async { await tester.pumpWidget( const Directionality( textDirection: TextDirection.ltr, child: ButtonBar(), ), ); }); group('alignment', () { testWidgets('default alignment is MainAxisAlignment.end', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp( home: ButtonBar( children: <Widget>[ SizedBox(width: 10.0, height: 10.0), ], ), ), ); final Finder child = find.byType(SizedBox); // Should be positioned to the right of the bar, expect(tester.getRect(child).left, 782.0); // bar width - default padding - 10 expect(tester.getRect(child).right, 792.0); // bar width - default padding }); testWidgets('ButtonBarTheme.alignment overrides default', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp( home: ButtonBarTheme( data: ButtonBarThemeData( alignment: MainAxisAlignment.center, ), child: ButtonBar( children: <Widget>[ SizedBox(width: 10.0, height: 10.0), ], ), ), ), ); final Finder child = find.byType(SizedBox); // Should be positioned in the center expect(tester.getRect(child).left, 395.0); // (bar width - padding) / 2 - 10 / 2 expect(tester.getRect(child).right, 405.0); // (bar width - padding) / 2 - 10 / 2 + 10 }); testWidgets('ButtonBar.alignment overrides ButtonBarTheme.alignment and default', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp( home: ButtonBarTheme( data: ButtonBarThemeData( alignment: MainAxisAlignment.center, ), child: ButtonBar( alignment: MainAxisAlignment.start, children: <Widget>[ SizedBox(width: 10.0, height: 10.0), ], ), ), ), ); final Finder child = find.byType(SizedBox); // Should be positioned on the left expect(tester.getRect(child).left, 8.0); // padding expect(tester.getRect(child).right, 18.0); // padding + 10 }); }); group('mainAxisSize', () { testWidgets('Default mainAxisSize is MainAxisSize.max', (WidgetTester tester) async { const Key buttonBarKey = Key('row'); const Key child0Key = Key('child0'); const Key child1Key = Key('child1'); const Key child2Key = Key('child2'); await tester.pumpWidget( const MaterialApp( home: Center( child: ButtonBar( key: buttonBarKey, // buttonPadding set to zero to simplify test calculations. buttonPadding: EdgeInsets.zero, children: <Widget>[ SizedBox(key: child0Key, width: 100.0, height: 100.0), SizedBox(key: child1Key, width: 100.0, height: 100.0), SizedBox(key: child2Key, width: 100.0, height: 100.0), ], ), ), ), ); // ButtonBar should take up all the space it is provided by its parent. final Rect buttonBarRect = tester.getRect(find.byKey(buttonBarKey)); expect(buttonBarRect.size.width, equals(800.0)); expect(buttonBarRect.size.height, equals(100.0)); // The children of [ButtonBar] are aligned by [MainAxisAlignment.end] by // default. Rect childRect; childRect = tester.getRect(find.byKey(child0Key)); expect(childRect.size.width, equals(100.0)); expect(childRect.size.height, equals(100.0)); expect(childRect.right, 800.0 - 200.0); childRect = tester.getRect(find.byKey(child1Key)); expect(childRect.size.width, equals(100.0)); expect(childRect.size.height, equals(100.0)); expect(childRect.right, 800.0 - 100.0); childRect = tester.getRect(find.byKey(child2Key)); expect(childRect.size.width, equals(100.0)); expect(childRect.size.height, equals(100.0)); expect(childRect.right, 800.0); }); testWidgets('ButtonBarTheme.mainAxisSize overrides default', (WidgetTester tester) async { const Key buttonBarKey = Key('row'); const Key child0Key = Key('child0'); const Key child1Key = Key('child1'); const Key child2Key = Key('child2'); await tester.pumpWidget( const MaterialApp( home: ButtonBarTheme( data: ButtonBarThemeData( mainAxisSize: MainAxisSize.min, ), child: Center( child: ButtonBar( key: buttonBarKey, // buttonPadding set to zero to simplify test calculations. buttonPadding: EdgeInsets.zero, children: <Widget>[ SizedBox(key: child0Key, width: 100.0, height: 100.0), SizedBox(key: child1Key, width: 100.0, height: 100.0), SizedBox(key: child2Key, width: 100.0, height: 100.0), ], ), ), ), ), ); // ButtonBar should take up minimum space it requires. final Rect buttonBarRect = tester.getRect(find.byKey(buttonBarKey)); expect(buttonBarRect.size.width, equals(300.0)); expect(buttonBarRect.size.height, equals(100.0)); Rect childRect; childRect = tester.getRect(find.byKey(child0Key)); expect(childRect.size.width, equals(100.0)); expect(childRect.size.height, equals(100.0)); // Should be a center aligned because of [Center] widget. // First child is on the left side of the button bar. expect(childRect.left, (800.0 - buttonBarRect.width) / 2.0); childRect = tester.getRect(find.byKey(child1Key)); expect(childRect.size.width, equals(100.0)); expect(childRect.size.height, equals(100.0)); // Should be a center aligned because of [Center] widget. // Second child is on the center the button bar. expect(childRect.left, ((800.0 - buttonBarRect.width) / 2.0) + 100.0); childRect = tester.getRect(find.byKey(child2Key)); expect(childRect.size.width, equals(100.0)); expect(childRect.size.height, equals(100.0)); // Should be a center aligned because of [Center] widget. // Third child is on the right side of the button bar. expect(childRect.left, ((800.0 - buttonBarRect.width) / 2.0) + 200.0); }); testWidgets('ButtonBar.mainAxisSize overrides ButtonBarTheme.mainAxisSize and default', (WidgetTester tester) async { const Key buttonBarKey = Key('row'); const Key child0Key = Key('child0'); const Key child1Key = Key('child1'); const Key child2Key = Key('child2'); await tester.pumpWidget( const MaterialApp( home: ButtonBarTheme( data: ButtonBarThemeData( mainAxisSize: MainAxisSize.min, ), child: Center( child: ButtonBar( key: buttonBarKey, // buttonPadding set to zero to simplify test calculations. buttonPadding: EdgeInsets.zero, mainAxisSize: MainAxisSize.max, children: <Widget>[ SizedBox(key: child0Key, width: 100.0, height: 100.0), SizedBox(key: child1Key, width: 100.0, height: 100.0), SizedBox(key: child2Key, width: 100.0, height: 100.0), ], ), ), ), ), ); // ButtonBar should take up all the space it is provided by its parent. final Rect buttonBarRect = tester.getRect(find.byKey(buttonBarKey)); expect(buttonBarRect.size.width, equals(800.0)); expect(buttonBarRect.size.height, equals(100.0)); // The children of [ButtonBar] are aligned by [MainAxisAlignment.end] by // default. Rect childRect; childRect = tester.getRect(find.byKey(child0Key)); expect(childRect.size.width, equals(100.0)); expect(childRect.size.height, equals(100.0)); expect(childRect.right, 800.0 - 200.0); childRect = tester.getRect(find.byKey(child1Key)); expect(childRect.size.width, equals(100.0)); expect(childRect.size.height, equals(100.0)); expect(childRect.right, 800.0 - 100.0); childRect = tester.getRect(find.byKey(child2Key)); expect(childRect.size.width, equals(100.0)); expect(childRect.size.height, equals(100.0)); expect(childRect.right, 800.0); }); }); group('button properties override ButtonTheme', () { testWidgets('default button properties override ButtonTheme properties', (WidgetTester tester) async { late BuildContext capturedContext; await tester.pumpWidget( MaterialApp( home: ButtonBar( children: <Widget>[ Builder(builder: (BuildContext context) { capturedContext = context; return Container(); }), ], ), ), ); final ButtonThemeData buttonTheme = ButtonTheme.of(capturedContext); expect(buttonTheme.textTheme, equals(ButtonTextTheme.primary)); expect(buttonTheme.minWidth, equals(64.0)); expect(buttonTheme.height, equals(36.0)); expect(buttonTheme.padding, equals(const EdgeInsets.symmetric(horizontal: 8.0))); expect(buttonTheme.alignedDropdown, equals(false)); expect(buttonTheme.layoutBehavior, equals(ButtonBarLayoutBehavior.padded)); }); testWidgets('ButtonBarTheme button properties override defaults and ButtonTheme properties', (WidgetTester tester) async { late BuildContext capturedContext; await tester.pumpWidget( MaterialApp( home: ButtonBarTheme( data: const ButtonBarThemeData( buttonTextTheme: ButtonTextTheme.primary, buttonMinWidth: 42.0, buttonHeight: 84.0, buttonPadding: EdgeInsets.fromLTRB(10, 20, 30, 40), buttonAlignedDropdown: true, layoutBehavior: ButtonBarLayoutBehavior.constrained, ), child: ButtonBar( children: <Widget>[ Builder(builder: (BuildContext context) { capturedContext = context; return Container(); }), ], ), ), ), ); final ButtonThemeData buttonTheme = ButtonTheme.of(capturedContext); expect(buttonTheme.textTheme, equals(ButtonTextTheme.primary)); expect(buttonTheme.minWidth, equals(42.0)); expect(buttonTheme.height, equals(84.0)); expect(buttonTheme.padding, equals(const EdgeInsets.fromLTRB(10, 20, 30, 40))); expect(buttonTheme.alignedDropdown, equals(true)); expect(buttonTheme.layoutBehavior, equals(ButtonBarLayoutBehavior.constrained)); }); testWidgets('ButtonBar button properties override ButtonBarTheme, defaults and ButtonTheme properties', (WidgetTester tester) async { late BuildContext capturedContext; await tester.pumpWidget( MaterialApp( home: ButtonBarTheme( data: const ButtonBarThemeData( buttonTextTheme: ButtonTextTheme.accent, buttonMinWidth: 4242.0, buttonHeight: 8484.0, buttonPadding: EdgeInsets.fromLTRB(50, 60, 70, 80), buttonAlignedDropdown: false, layoutBehavior: ButtonBarLayoutBehavior.padded, ), child: ButtonBar( buttonTextTheme: ButtonTextTheme.primary, buttonMinWidth: 42.0, buttonHeight: 84.0, buttonPadding: const EdgeInsets.fromLTRB(10, 20, 30, 40), buttonAlignedDropdown: true, layoutBehavior: ButtonBarLayoutBehavior.constrained, children: <Widget>[ Builder(builder: (BuildContext context) { capturedContext = context; return Container(); }), ], ), ), ), ); final ButtonThemeData buttonTheme = ButtonTheme.of(capturedContext); expect(buttonTheme.textTheme, equals(ButtonTextTheme.primary)); expect(buttonTheme.minWidth, equals(42.0)); expect(buttonTheme.height, equals(84.0)); expect(buttonTheme.padding, equals(const EdgeInsets.fromLTRB(10, 20, 30, 40))); expect(buttonTheme.alignedDropdown, equals(true)); expect(buttonTheme.layoutBehavior, equals(ButtonBarLayoutBehavior.constrained)); }); }); group('layoutBehavior', () { testWidgets('ButtonBar has a min height of 52 when using ButtonBarLayoutBehavior.constrained', (WidgetTester tester) async { await tester.pumpWidget( SingleChildScrollView( child: ListBody( children: const <Widget>[ Directionality( textDirection: TextDirection.ltr, child: ButtonBar( layoutBehavior: ButtonBarLayoutBehavior.constrained, children: <Widget>[ SizedBox(width: 10.0, height: 10.0), ], ), ), ], ), ), ); final Finder buttonBar = find.byType(ButtonBar); expect(tester.getBottomRight(buttonBar).dy - tester.getTopRight(buttonBar).dy, 52.0); }); testWidgets('ButtonBar has padding applied when using ButtonBarLayoutBehavior.padded', (WidgetTester tester) async { await tester.pumpWidget( SingleChildScrollView( child: ListBody( children: const <Widget>[ Directionality( textDirection: TextDirection.ltr, child: ButtonBar( layoutBehavior: ButtonBarLayoutBehavior.padded, children: <Widget>[ SizedBox(width: 10.0, height: 10.0), ], ), ), ], ), ), ); final Finder buttonBar = find.byType(ButtonBar); expect(tester.getBottomRight(buttonBar).dy - tester.getTopRight(buttonBar).dy, 26.0); }); }); group("ButtonBar's children wrap when they overflow horizontally", () { testWidgets("ButtonBar's children wrap when buttons overflow", (WidgetTester tester) async { final Key keyOne = UniqueKey(); final Key keyTwo = UniqueKey(); await tester.pumpWidget( MaterialApp( home: ButtonBar( children: <Widget>[ SizedBox(key: keyOne, height: 50.0, width: 800.0), SizedBox(key: keyTwo, height: 50.0, width: 800.0), ], ), ), ); // Second [Container] should wrap around to the next column since // they take up max width constraint. final Rect containerOneRect = tester.getRect(find.byKey(keyOne)); final Rect containerTwoRect = tester.getRect(find.byKey(keyTwo)); expect(containerOneRect.bottom, containerTwoRect.top); expect(containerOneRect.left, containerTwoRect.left); }); testWidgets( "ButtonBar's children overflow defaults - MainAxisAlignment.end", (WidgetTester tester) async { final Key keyOne = UniqueKey(); final Key keyTwo = UniqueKey(); await tester.pumpWidget( MaterialApp( home: ButtonBar( // Set padding to zero to align buttons with edge of button bar. buttonPadding: EdgeInsets.zero, children: <Widget>[ SizedBox(key: keyOne, height: 50.0, width: 500.0), SizedBox(key: keyTwo, height: 50.0, width: 500.0), ], ), ), ); final Rect buttonBarRect = tester.getRect(find.byType(ButtonBar)); final Rect containerOneRect = tester.getRect(find.byKey(keyOne)); final Rect containerTwoRect = tester.getRect(find.byKey(keyTwo)); // Second [Container] should wrap around to the next row. expect(containerOneRect.bottom, containerTwoRect.top); // Second [Container] should align to the start of the ButtonBar. expect(containerOneRect.right, containerTwoRect.right); expect(containerOneRect.right, buttonBarRect.right); }, ); testWidgets("ButtonBar's children overflow - MainAxisAlignment.start", (WidgetTester tester) async { final Key keyOne = UniqueKey(); final Key keyTwo = UniqueKey(); await tester.pumpWidget( MaterialApp( home: ButtonBar( alignment: MainAxisAlignment.start, // Set padding to zero to align buttons with edge of button bar. buttonPadding: EdgeInsets.zero, children: <Widget>[ SizedBox(key: keyOne, height: 50.0, width: 500.0), SizedBox(key: keyTwo, height: 50.0, width: 500.0), ], ), ), ); final Rect buttonBarRect = tester.getRect(find.byType(ButtonBar)); final Rect containerOneRect = tester.getRect(find.byKey(keyOne)); final Rect containerTwoRect = tester.getRect(find.byKey(keyTwo)); // Second [Container] should wrap around to the next row. expect(containerOneRect.bottom, containerTwoRect.top); // [Container]s should align to the end of the ButtonBar. expect(containerOneRect.left, containerTwoRect.left); expect(containerOneRect.left, buttonBarRect.left); }); testWidgets("ButtonBar's children overflow - MainAxisAlignment.center", (WidgetTester tester) async { final Key keyOne = UniqueKey(); final Key keyTwo = UniqueKey(); await tester.pumpWidget( MaterialApp( home: ButtonBar( alignment: MainAxisAlignment.center, // Set padding to zero to align buttons with edge of button bar. buttonPadding: EdgeInsets.zero, children: <Widget>[ SizedBox(key: keyOne, height: 50.0, width: 500.0), SizedBox(key: keyTwo, height: 50.0, width: 500.0), ], ), ), ); final Rect buttonBarRect = tester.getRect(find.byType(ButtonBar)); final Rect containerOneRect = tester.getRect(find.byKey(keyOne)); final Rect containerTwoRect = tester.getRect(find.byKey(keyTwo)); // Second [Container] should wrap around to the next row. expect(containerOneRect.bottom, containerTwoRect.top); // [Container]s should center themselves in the ButtonBar. expect(containerOneRect.center.dx, containerTwoRect.center.dx); expect(containerOneRect.center.dx, buttonBarRect.center.dx); }); testWidgets( "ButtonBar's children default to MainAxisAlignment.start for horizontal " 'alignment when overflowing in spaceBetween, spaceAround and spaceEvenly ' 'cases when overflowing.', (WidgetTester tester) async { final Key keyOne = UniqueKey(); final Key keyTwo = UniqueKey(); await tester.pumpWidget( MaterialApp( home: ButtonBar( alignment: MainAxisAlignment.spaceEvenly, // Set padding to zero to align buttons with edge of button bar. buttonPadding: EdgeInsets.zero, children: <Widget>[ SizedBox(key: keyOne, height: 50.0, width: 500.0), SizedBox(key: keyTwo, height: 50.0, width: 500.0), ], ), ), ); Rect buttonBarRect = tester.getRect(find.byType(ButtonBar)); Rect containerOneRect = tester.getRect(find.byKey(keyOne)); Rect containerTwoRect = tester.getRect(find.byKey(keyTwo)); // Second [Container] should wrap around to the next row. expect(containerOneRect.bottom, containerTwoRect.top); // Should align horizontally to the start of the button bar. expect(containerOneRect.left, containerTwoRect.left); expect(containerOneRect.left, buttonBarRect.left); await tester.pumpWidget( MaterialApp( home: ButtonBar( alignment: MainAxisAlignment.spaceAround, // Set padding to zero to align buttons with edge of button bar. buttonPadding: EdgeInsets.zero, children: <Widget>[ SizedBox(key: keyOne, height: 50.0, width: 500.0), SizedBox(key: keyTwo, height: 50.0, width: 500.0), ], ), ), ); buttonBarRect = tester.getRect(find.byType(ButtonBar)); containerOneRect = tester.getRect(find.byKey(keyOne)); containerTwoRect = tester.getRect(find.byKey(keyTwo)); // Second [Container] should wrap around to the next row. expect(containerOneRect.bottom, containerTwoRect.top); // Should align horizontally to the start of the button bar. expect(containerOneRect.left, containerTwoRect.left); expect(containerOneRect.left, buttonBarRect.left); }, ); testWidgets( "ButtonBar's children respects verticalDirection when overflowing", (WidgetTester tester) async { final Key keyOne = UniqueKey(); final Key keyTwo = UniqueKey(); await tester.pumpWidget( MaterialApp( home: ButtonBar( alignment: MainAxisAlignment.center, // Set padding to zero to align buttons with edge of button bar. buttonPadding: EdgeInsets.zero, // Set the vertical direction to start from the bottom and lay // out upwards. overflowDirection: VerticalDirection.up, children: <Widget>[ SizedBox(key: keyOne, height: 50.0, width: 500.0), SizedBox(key: keyTwo, height: 50.0, width: 500.0), ], ), ), ); final Rect containerOneRect = tester.getRect(find.byKey(keyOne)); final Rect containerTwoRect = tester.getRect(find.byKey(keyTwo)); // Second [Container] should appear above first container. expect(containerTwoRect.bottom, lessThanOrEqualTo(containerOneRect.top)); }, ); testWidgets( 'ButtonBar has no spacing by default when overflowing', (WidgetTester tester) async { final Key keyOne = UniqueKey(); final Key keyTwo = UniqueKey(); await tester.pumpWidget( MaterialApp( home: ButtonBar( alignment: MainAxisAlignment.center, // Set padding to zero to align buttons with edge of button bar. buttonPadding: EdgeInsets.zero, children: <Widget>[ SizedBox(key: keyOne, height: 50.0, width: 500.0), SizedBox(key: keyTwo, height: 50.0, width: 500.0), ], ), ), ); final Rect containerOneRect = tester.getRect(find.byKey(keyOne)); final Rect containerTwoRect = tester.getRect(find.byKey(keyTwo)); expect(containerOneRect.bottom, containerTwoRect.top); }, ); testWidgets( "ButtonBar's children respects overflowButtonSpacing when overflowing", (WidgetTester tester) async { final Key keyOne = UniqueKey(); final Key keyTwo = UniqueKey(); await tester.pumpWidget( MaterialApp( home: ButtonBar( alignment: MainAxisAlignment.center, // Set padding to zero to align buttons with edge of button bar. buttonPadding: EdgeInsets.zero, // Set the overflow button spacing to ensure add some space between // buttons in an overflow case. overflowButtonSpacing: 10.0, children: <Widget>[ SizedBox(key: keyOne, height: 50.0, width: 500.0), SizedBox(key: keyTwo, height: 50.0, width: 500.0), ], ), ), ); final Rect containerOneRect = tester.getRect(find.byKey(keyOne)); final Rect containerTwoRect = tester.getRect(find.byKey(keyTwo)); expect(containerOneRect.bottom, containerTwoRect.top - 10.0); }, ); }); testWidgets('_RenderButtonBarRow.constraints does not work before layout', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp(home: ButtonBar()), Duration.zero, EnginePhase.build, ); final Finder buttonBar = find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_ButtonBarRow'); final RenderBox renderButtonBar = tester.renderObject(buttonBar) as RenderBox; expect(renderButtonBar.debugNeedsLayout, isTrue); expect(() => renderButtonBar.constraints, throwsStateError); }); }