| // 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/widgets.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| class TestRoute extends PageRouteBuilder<void> { |
| TestRoute(Widget child) : super( |
| pageBuilder: (BuildContext _, Animation<double> __, Animation<double> ___) => child, |
| ); |
| } |
| |
| class IconTextBox extends StatelessWidget { |
| const IconTextBox(this.text, { Key? key }) : super(key: key); |
| final String text; |
| @override |
| Widget build(BuildContext context) { |
| return Container( |
| alignment: Alignment.center, |
| child: Row( |
| children: <Widget>[const Icon(IconData(0x41, fontFamily: 'Roboto')), Text(text)], |
| ), |
| ); |
| } |
| } |
| |
| void main() { |
| testWidgets('InheritedTheme.captureAll()', (WidgetTester tester) async { |
| const double fontSize = 32; |
| const double iconSize = 48; |
| const Color textColor = Color(0xFF00FF00); |
| const Color iconColor = Color(0xFF0000FF); |
| bool useCaptureAll = false; |
| late BuildContext navigatorContext; |
| |
| Widget buildFrame() { |
| return WidgetsApp( |
| color: const Color(0xFFFFFFFF), |
| onGenerateRoute: (RouteSettings settings) { |
| return TestRoute( |
| // The outer DefaultTextStyle and IconTheme widgets must have |
| // no effect on the test because InheritedTheme.captureAll() |
| // is required to only save the closest InheritedTheme ancestors. |
| DefaultTextStyle( |
| style: const TextStyle(fontSize: iconSize, color: iconColor), |
| child: IconTheme( |
| data: const IconThemeData(size: fontSize, color: textColor), |
| // The inner DefaultTextStyle and IconTheme widgets define |
| // InheritedThemes that captureAll() will wrap() around |
| // TestRoute's IconTextBox child. |
| child: DefaultTextStyle( |
| style: const TextStyle(fontSize: fontSize, color: textColor), |
| child: IconTheme( |
| data: const IconThemeData(size: iconSize, color: iconColor), |
| child: Builder( |
| builder: (BuildContext context) { |
| return GestureDetector( |
| behavior: HitTestBehavior.opaque, |
| onTap: () { |
| navigatorContext = context; |
| Navigator.of(context).push( |
| TestRoute( |
| useCaptureAll |
| ? InheritedTheme.captureAll(context, const IconTextBox('Hello')) |
| : const IconTextBox('Hello') |
| ), |
| ); |
| }, |
| child: const IconTextBox('Tap'), |
| ); |
| }, |
| ), |
| ), |
| ), |
| ), |
| ), |
| ); |
| }, |
| ); |
| } |
| |
| TextStyle getIconStyle() { |
| return tester.widget<RichText>( |
| find.descendant( |
| of: find.byType(Icon), |
| matching: find.byType(RichText), |
| ), |
| ).text.style!; |
| } |
| |
| TextStyle getTextStyle(String text) { |
| return tester.widget<RichText>( |
| find.descendant( |
| of: find.text(text), |
| matching: find.byType(RichText), |
| ), |
| ).text.style!; |
| } |
| |
| useCaptureAll = false; |
| await tester.pumpWidget(buildFrame()); |
| expect(find.text('Tap'), findsOneWidget); |
| expect(find.text('Hello'), findsNothing); |
| expect(getTextStyle('Tap').color, textColor); |
| expect(getTextStyle('Tap').fontSize, fontSize); |
| expect(getIconStyle().color, iconColor); |
| expect(getIconStyle().fontSize, iconSize); |
| |
| // Tap to show the TestRoute |
| await tester.tap(find.text('Tap')); |
| await tester.pumpAndSettle(); // route transition |
| expect(find.text('Tap'), findsNothing); |
| expect(find.text('Hello'), findsOneWidget); |
| // The new route's text and icons will NOT inherit the DefaultTextStyle or |
| // IconTheme values. |
| expect(getTextStyle('Hello').color, isNot(textColor)); |
| expect(getTextStyle('Hello').fontSize, isNot(fontSize)); |
| expect(getIconStyle().color, isNot(iconColor)); |
| expect(getIconStyle().fontSize, isNot(iconSize)); |
| |
| // Return to the home route |
| useCaptureAll = true; |
| Navigator.of(navigatorContext).pop(); |
| await tester.pumpAndSettle(); // route transition |
| |
| // Verify that all is the same as it was when the test started |
| expect(find.text('Tap'), findsOneWidget); |
| expect(find.text('Hello'), findsNothing); |
| expect(getTextStyle('Tap').color, textColor); |
| expect(getTextStyle('Tap').fontSize, fontSize); |
| expect(getIconStyle().color, iconColor); |
| expect(getIconStyle().fontSize, iconSize); |
| |
| // Tap to show the TestRoute. The test route's IconTextBox will have been |
| // wrapped with InheritedTheme.captureAll(). |
| await tester.tap(find.text('Tap')); |
| await tester.pumpAndSettle(); // route transition |
| expect(find.text('Tap'), findsNothing); |
| expect(find.text('Hello'), findsOneWidget); |
| // The new route's text and icons will inherit the DefaultTextStyle or |
| // IconTheme values because captureAll. |
| expect(getTextStyle('Hello').color, textColor); |
| expect(getTextStyle('Hello').fontSize, fontSize); |
| expect(getIconStyle().color, iconColor); |
| expect(getIconStyle().fontSize, iconSize); |
| }); |
| |
| testWidgets('InheritedTheme.captureAll() multiple IconTheme ancestors', (WidgetTester tester) async { |
| // This is a regression test for https://github.com/flutter/flutter/issues/39087 |
| |
| const Color outerColor = Color(0xFF0000FF); |
| const Color innerColor = Color(0xFF00FF00); |
| const double iconSize = 48; |
| final Key icon1 = UniqueKey(); |
| final Key icon2 = UniqueKey(); |
| |
| await tester.pumpWidget( |
| WidgetsApp( |
| color: const Color(0xFFFFFFFF), |
| onGenerateRoute: (RouteSettings settings) { |
| return TestRoute( |
| IconTheme( |
| data: const IconThemeData(color: outerColor), |
| child: IconTheme( |
| data: const IconThemeData(size: iconSize, color: innerColor), |
| child: Center( |
| child: Row( |
| mainAxisSize: MainAxisSize.min, |
| children: <Widget>[ |
| Icon(const IconData(0x41, fontFamily: 'Roboto'), key: icon1), |
| Builder( |
| builder: (BuildContext context) { |
| // The same IconThemes are visible from this context |
| // and the context that the widget returned by captureAll() |
| // is built in. So only the inner green IconTheme should |
| // apply to the icon, i.e. both icons will be big and green. |
| return InheritedTheme.captureAll( |
| context, |
| Icon(const IconData(0x41, fontFamily: 'Roboto'), key: icon2), |
| ); |
| }, |
| ), |
| ], |
| ), |
| ), |
| ), |
| ), |
| ); |
| }, |
| ), |
| ); |
| |
| TextStyle getIconStyle(Key key) { |
| return tester.widget<RichText>( |
| find.descendant( |
| of: find.byKey(key), |
| matching: find.byType(RichText), |
| ), |
| ).text.style!; |
| } |
| |
| expect(getIconStyle(icon1).color, innerColor); |
| expect(getIconStyle(icon1).fontSize, iconSize); |
| expect(getIconStyle(icon2).color, innerColor); |
| expect(getIconStyle(icon2).fontSize, iconSize); |
| }); |
| |
| testWidgets('InheritedTheme.captureAll() multiple DefaultTextStyle ancestors', (WidgetTester tester) async { |
| // This is a regression test for https://github.com/flutter/flutter/issues/39087 |
| |
| const Color textColor = Color(0xFF00FF00); |
| |
| await tester.pumpWidget( |
| WidgetsApp( |
| color: const Color(0xFFFFFFFF), |
| onGenerateRoute: (RouteSettings settings) { |
| return TestRoute( |
| DefaultTextStyle( |
| style: const TextStyle(fontSize: 48), |
| child: DefaultTextStyle( |
| style: const TextStyle(color: textColor), |
| child: Row( |
| children: <Widget>[ |
| const Text('Hello'), |
| Builder( |
| builder: (BuildContext context) { |
| return InheritedTheme.captureAll(context, const Text('World')); |
| }, |
| ), |
| ], |
| ), |
| ), |
| ), |
| ); |
| }, |
| ), |
| ); |
| |
| TextStyle getTextStyle(String text) { |
| return tester.widget<RichText>( |
| find.descendant( |
| of: find.text(text), |
| matching: find.byType(RichText), |
| ), |
| ).text.style!; |
| } |
| |
| expect(getTextStyle('Hello').fontSize, null); |
| expect(getTextStyle('Hello').color, textColor); |
| expect(getTextStyle('World').fontSize, null); |
| expect(getTextStyle('World').color, textColor); |
| }); |
| } |