| // 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/gestures.dart'; |
| import 'package:flutter/material.dart'; |
| import 'package:flutter/services.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| void main() { |
| RenderObject getOverlayColor(WidgetTester tester) { |
| return tester.allRenderObjects.firstWhere( |
| (RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures', |
| ); |
| } |
| |
| test('IconButtonThemeData lerp special cases', () { |
| expect(IconButtonThemeData.lerp(null, null, 0), null); |
| const data = IconButtonThemeData(); |
| expect(identical(IconButtonThemeData.lerp(data, data, 0.5), data), true); |
| }); |
| |
| testWidgets('Passing no IconButtonTheme returns defaults', (WidgetTester tester) async { |
| const colorScheme = ColorScheme.light(); |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData.from(colorScheme: colorScheme), |
| home: Scaffold( |
| body: Center( |
| child: IconButton(onPressed: () {}, icon: const Icon(Icons.ac_unit)), |
| ), |
| ), |
| ), |
| ); |
| |
| final Finder buttonMaterial = find.descendant( |
| of: find.byType(IconButton), |
| matching: find.byType(Material), |
| ); |
| |
| final Material material = tester.widget<Material>(buttonMaterial); |
| expect(material.animationDuration, const Duration(milliseconds: 200)); |
| expect(material.borderRadius, null); |
| expect(material.color, Colors.transparent); |
| expect(material.elevation, 0.0); |
| expect(material.shadowColor, Colors.transparent); |
| expect(material.shape, const StadiumBorder()); |
| expect(material.textStyle, null); |
| expect(material.type, MaterialType.button); |
| |
| final Align align = tester.firstWidget<Align>( |
| find.ancestor(of: find.byIcon(Icons.ac_unit), matching: find.byType(Align)), |
| ); |
| expect(align.alignment, Alignment.center); |
| }); |
| |
| group('[Theme, IconTheme, IconButton style overrides]', () { |
| const foregroundColor = Color(0xff000001); |
| const disabledForegroundColor = Color(0xff000002); |
| const backgroundColor = Color(0xff000003); |
| const shadowColor = Color(0xff000004); |
| const double elevation = 3; |
| const padding = EdgeInsets.all(3); |
| const minimumSize = Size(200, 200); |
| const side = BorderSide(color: Colors.green, width: 2); |
| const OutlinedBorder shape = RoundedRectangleBorder( |
| side: side, |
| borderRadius: BorderRadius.all(Radius.circular(2)), |
| ); |
| const MouseCursor enabledMouseCursor = SystemMouseCursors.text; |
| const MouseCursor disabledMouseCursor = SystemMouseCursors.grab; |
| const MaterialTapTargetSize tapTargetSize = MaterialTapTargetSize.shrinkWrap; |
| const animationDuration = Duration(milliseconds: 25); |
| const enableFeedback = false; |
| const AlignmentGeometry alignment = Alignment.centerLeft; |
| |
| final ButtonStyle style = IconButton.styleFrom( |
| foregroundColor: foregroundColor, |
| disabledForegroundColor: disabledForegroundColor, |
| backgroundColor: backgroundColor, |
| shadowColor: shadowColor, |
| elevation: elevation, |
| padding: padding, |
| minimumSize: minimumSize, |
| side: side, |
| shape: shape, |
| enabledMouseCursor: enabledMouseCursor, |
| disabledMouseCursor: disabledMouseCursor, |
| tapTargetSize: tapTargetSize, |
| animationDuration: animationDuration, |
| enableFeedback: enableFeedback, |
| alignment: alignment, |
| ); |
| |
| Widget buildFrame({ |
| ButtonStyle? buttonStyle, |
| ButtonStyle? themeStyle, |
| ButtonStyle? overallStyle, |
| }) { |
| final Widget child = Builder( |
| builder: (BuildContext context) { |
| return IconButton(style: buttonStyle, onPressed: () {}, icon: const Icon(Icons.ac_unit)); |
| }, |
| ); |
| return MaterialApp( |
| theme: ThemeData.from( |
| colorScheme: const ColorScheme.light(), |
| ).copyWith(iconButtonTheme: IconButtonThemeData(style: overallStyle)), |
| home: Scaffold( |
| body: Center( |
| // If the IconButtonTheme widget is present, it's used |
| // instead of the Theme's ThemeData.iconButtonTheme. |
| child: themeStyle == null |
| ? child |
| : IconButtonTheme( |
| data: IconButtonThemeData(style: themeStyle), |
| child: child, |
| ), |
| ), |
| ), |
| ); |
| } |
| |
| final Finder findMaterial = find.descendant( |
| of: find.byType(IconButton), |
| matching: find.byType(Material), |
| ); |
| |
| final Finder findInkWell = find.descendant( |
| of: find.byType(IconButton), |
| matching: find.byType(InkWell), |
| ); |
| |
| const enabled = <WidgetState>{}; |
| const disabled = <WidgetState>{WidgetState.disabled}; |
| const hovered = <WidgetState>{WidgetState.hovered}; |
| const focused = <WidgetState>{WidgetState.focused}; |
| const pressed = <WidgetState>{WidgetState.pressed}; |
| |
| void checkButton(WidgetTester tester) { |
| final Material material = tester.widget<Material>(findMaterial); |
| final InkWell inkWell = tester.widget<InkWell>(findInkWell); |
| expect(material.textStyle, null); |
| expect(material.color, backgroundColor); |
| expect(material.shadowColor, shadowColor); |
| expect(material.elevation, elevation); |
| expect( |
| WidgetStateProperty.resolveAs<MouseCursor?>(inkWell.mouseCursor, enabled), |
| enabledMouseCursor, |
| ); |
| expect( |
| WidgetStateProperty.resolveAs<MouseCursor?>(inkWell.mouseCursor, disabled), |
| disabledMouseCursor, |
| ); |
| expect(inkWell.overlayColor!.resolve(hovered), foregroundColor.withOpacity(0.08)); |
| expect(inkWell.overlayColor!.resolve(focused), foregroundColor.withOpacity(0.1)); |
| expect(inkWell.overlayColor!.resolve(pressed), foregroundColor.withOpacity(0.1)); |
| expect(inkWell.enableFeedback, enableFeedback); |
| expect(material.borderRadius, null); |
| expect(material.shape, shape); |
| expect(material.animationDuration, animationDuration); |
| expect(tester.getSize(find.byType(IconButton)), const Size(200, 200)); |
| final Align align = tester.firstWidget<Align>( |
| find.ancestor(of: find.byIcon(Icons.ac_unit), matching: find.byType(Align)), |
| ); |
| expect(align.alignment, alignment); |
| } |
| |
| testWidgets('Button style overrides defaults', (WidgetTester tester) async { |
| await tester.pumpWidget(buildFrame(buttonStyle: style)); |
| await tester.pumpAndSettle(); // allow the animations to finish |
| checkButton(tester); |
| }); |
| |
| testWidgets('Button theme style overrides defaults', (WidgetTester tester) async { |
| await tester.pumpWidget(buildFrame(themeStyle: style)); |
| await tester.pumpAndSettle(); |
| checkButton(tester); |
| }); |
| |
| testWidgets('Overall Theme button theme style overrides defaults', (WidgetTester tester) async { |
| await tester.pumpWidget(buildFrame(overallStyle: style)); |
| await tester.pumpAndSettle(); |
| checkButton(tester); |
| }); |
| |
| // Same as the previous tests with empty ButtonStyle's instead of null. |
| |
| testWidgets('Button style overrides defaults, empty theme and overall styles', ( |
| WidgetTester tester, |
| ) async { |
| await tester.pumpWidget( |
| buildFrame( |
| buttonStyle: style, |
| themeStyle: const ButtonStyle(), |
| overallStyle: const ButtonStyle(), |
| ), |
| ); |
| await tester.pumpAndSettle(); // allow the animations to finish |
| checkButton(tester); |
| }); |
| |
| testWidgets('Button theme style overrides defaults, empty button and overall styles', ( |
| WidgetTester tester, |
| ) async { |
| await tester.pumpWidget( |
| buildFrame( |
| buttonStyle: const ButtonStyle(), |
| themeStyle: style, |
| overallStyle: const ButtonStyle(), |
| ), |
| ); |
| await tester.pumpAndSettle(); // allow the animations to finish |
| checkButton(tester); |
| }); |
| |
| testWidgets( |
| 'Overall Theme button theme style overrides defaults, null theme and empty overall style', |
| (WidgetTester tester) async { |
| await tester.pumpWidget(buildFrame(buttonStyle: const ButtonStyle(), overallStyle: style)); |
| await tester.pumpAndSettle(); // allow the animations to finish |
| checkButton(tester); |
| }, |
| ); |
| }); |
| |
| testWidgets('Theme shadowColor', (WidgetTester tester) async { |
| const colorScheme = ColorScheme.light(); |
| const shadowColor = Color(0xff000001); |
| const overriddenColor = Color(0xff000002); |
| |
| Widget buildFrame({Color? overallShadowColor, Color? themeShadowColor, Color? shadowColor}) { |
| return MaterialApp( |
| theme: ThemeData.from(colorScheme: colorScheme).copyWith(shadowColor: overallShadowColor), |
| home: Scaffold( |
| body: Center( |
| child: IconButtonTheme( |
| data: IconButtonThemeData(style: IconButton.styleFrom(shadowColor: themeShadowColor)), |
| child: Builder( |
| builder: (BuildContext context) { |
| return IconButton( |
| style: IconButton.styleFrom(shadowColor: shadowColor), |
| onPressed: () {}, |
| icon: const Icon(Icons.add), |
| ); |
| }, |
| ), |
| ), |
| ), |
| ), |
| ); |
| } |
| |
| final Finder buttonMaterialFinder = find.descendant( |
| of: find.byType(IconButton), |
| matching: find.byType(Material), |
| ); |
| |
| await tester.pumpWidget(buildFrame()); |
| Material material = tester.widget<Material>(buttonMaterialFinder); |
| expect(material.shadowColor, Colors.transparent); //default |
| |
| await tester.pumpWidget(buildFrame(overallShadowColor: shadowColor)); |
| await tester.pumpAndSettle(); // theme animation |
| material = tester.widget<Material>(buttonMaterialFinder); |
| expect(material.shadowColor, Colors.transparent); |
| |
| await tester.pumpWidget(buildFrame(themeShadowColor: shadowColor)); |
| await tester.pumpAndSettle(); // theme animation |
| material = tester.widget<Material>(buttonMaterialFinder); |
| expect(material.shadowColor, shadowColor); |
| |
| await tester.pumpWidget(buildFrame(shadowColor: shadowColor)); |
| await tester.pumpAndSettle(); // theme animation |
| material = tester.widget<Material>(buttonMaterialFinder); |
| expect(material.shadowColor, shadowColor); |
| |
| await tester.pumpWidget( |
| buildFrame(overallShadowColor: overriddenColor, themeShadowColor: shadowColor), |
| ); |
| await tester.pumpAndSettle(); // theme animation |
| material = tester.widget<Material>(buttonMaterialFinder); |
| expect(material.shadowColor, shadowColor); |
| |
| await tester.pumpWidget( |
| buildFrame(themeShadowColor: overriddenColor, shadowColor: shadowColor), |
| ); |
| await tester.pumpAndSettle(); // theme animation |
| material = tester.widget<Material>(buttonMaterialFinder); |
| expect(material.shadowColor, shadowColor); |
| }); |
| |
| testWidgets('IconButtonTheme IconButton.styleFrom overlayColor overrides default overlay color', ( |
| WidgetTester tester, |
| ) async { |
| const overlayColor = Color(0xffff0000); |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| body: Center( |
| child: IconButtonTheme( |
| data: IconButtonThemeData(style: IconButton.styleFrom(overlayColor: overlayColor)), |
| child: IconButton(onPressed: () {}, icon: const Icon(Icons.add)), |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| // Hovered. |
| final Offset center = tester.getCenter(find.byType(IconButton)); |
| final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); |
| await gesture.addPointer(); |
| await gesture.moveTo(center); |
| await tester.pumpAndSettle(); |
| expect(getOverlayColor(tester), paints..rect(color: overlayColor.withOpacity(0.08))); |
| |
| // Highlighted (pressed). |
| await gesture.down(center); |
| await tester.pumpAndSettle(); |
| expect( |
| getOverlayColor(tester), |
| paints |
| ..rect(color: overlayColor.withOpacity(0.08)) |
| ..rect(color: overlayColor.withOpacity(0.1)), |
| ); |
| // Remove pressed and hovered states, |
| await gesture.up(); |
| await tester.pumpAndSettle(); |
| await gesture.moveTo(const Offset(0, 50)); |
| await tester.pumpAndSettle(); |
| |
| // Focused. |
| await tester.sendKeyEvent(LogicalKeyboardKey.tab); |
| await tester.pumpAndSettle(); |
| expect(getOverlayColor(tester), paints..rect(color: overlayColor.withOpacity(0.1))); |
| }); |
| } |