// 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'; import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { test('IconButtonThemeData lerp special cases', () { expect(IconButtonThemeData.lerp(null, null, 0), null); const IconButtonThemeData data = IconButtonThemeData(); expect(identical(IconButtonThemeData.lerp(data, data, 0.5), data), true); }); testWidgetsWithLeakTracking('Passing no IconButtonTheme returns defaults', (WidgetTester tester) async { const ColorScheme colorScheme = ColorScheme.light(); await tester.pumpWidget( MaterialApp( theme: ThemeData.from(colorScheme: colorScheme, useMaterial3: true), 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 Color foregroundColor = Color(0xff000001); const Color disabledForegroundColor = Color(0xff000002); const Color backgroundColor = Color(0xff000003); const Color shadowColor = Color(0xff000004); const double elevation = 3; const EdgeInsets padding = EdgeInsets.all(3); const Size minimumSize = Size(200, 200); const BorderSide 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 Duration animationDuration = Duration(milliseconds: 25); const bool 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(), useMaterial3: true).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 Set<MaterialState> enabled = <MaterialState>{}; const Set<MaterialState> disabled = <MaterialState>{ MaterialState.disabled }; const Set<MaterialState> hovered = <MaterialState>{ MaterialState.hovered }; const Set<MaterialState> focused = <MaterialState>{ MaterialState.focused }; const Set<MaterialState> pressed = <MaterialState>{ MaterialState.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(MaterialStateProperty.resolveAs<MouseCursor?>(inkWell.mouseCursor, enabled), enabledMouseCursor); expect(MaterialStateProperty.resolveAs<MouseCursor?>(inkWell.mouseCursor, disabled), disabledMouseCursor); expect(inkWell.overlayColor!.resolve(hovered), foregroundColor.withOpacity(0.08)); expect(inkWell.overlayColor!.resolve(focused), foregroundColor.withOpacity(0.08)); expect(inkWell.overlayColor!.resolve(pressed), foregroundColor.withOpacity(0.12)); 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); } testWidgetsWithLeakTracking('Button style overrides defaults', (WidgetTester tester) async { await tester.pumpWidget(buildFrame(buttonStyle: style)); await tester.pumpAndSettle(); // allow the animations to finish checkButton(tester); }); testWidgetsWithLeakTracking('Button theme style overrides defaults', (WidgetTester tester) async { await tester.pumpWidget(buildFrame(themeStyle: style)); await tester.pumpAndSettle(); checkButton(tester); }); testWidgetsWithLeakTracking('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. testWidgetsWithLeakTracking('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); }); testWidgetsWithLeakTracking('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); }); testWidgetsWithLeakTracking('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); }); }); testWidgetsWithLeakTracking('Theme shadowColor', (WidgetTester tester) async { const ColorScheme colorScheme = ColorScheme.light(); const Color shadowColor = Color(0xff000001); const Color overriddenColor = Color(0xff000002); Widget buildFrame({ Color? overallShadowColor, Color? themeShadowColor, Color? shadowColor }) { return MaterialApp( theme: ThemeData.from(colorScheme: colorScheme, useMaterial3: true).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); }); }