| // 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 '../rendering/mock_canvas.dart'; |
| |
| void main() { |
| testWidgets('Theme.wrap()', (WidgetTester tester) async { |
| const Color primaryColor = Color(0xFF00FF00); |
| final Key primaryContainerKey = UniqueKey(); |
| |
| // Effectively the same as a StatelessWidget subclass. |
| final Widget primaryBox = Builder( |
| builder: (BuildContext context) { |
| return Container( |
| key: primaryContainerKey, |
| color: Theme.of(context).primaryColor, |
| ); |
| }, |
| ); |
| |
| late BuildContext navigatorContext; |
| |
| Widget buildFrame() { |
| return MaterialApp( |
| home: Scaffold( |
| body: Builder( // Introduce a context so the app's Theme is visible. |
| builder: (BuildContext context) { |
| navigatorContext = context; |
| return Theme( |
| data: Theme.of(context).copyWith(primaryColor: primaryColor), |
| child: Builder( // Introduce a context so the shadow Theme is visible to captureAll(). |
| builder: (BuildContext context) { |
| return Center( |
| child: Column( |
| mainAxisSize: MainAxisSize.min, |
| children: <Widget>[ |
| ElevatedButton( |
| child: const Text('push unwrapped'), |
| onPressed: () { |
| Navigator.of(context).push<void>( |
| MaterialPageRoute<void>( |
| // The primaryBox will see the default Theme when built. |
| builder: (BuildContext _) => primaryBox, |
| ), |
| ); |
| }, |
| ), |
| ElevatedButton( |
| child: const Text('push wrapped'), |
| onPressed: () { |
| Navigator.of(context).push<void>( |
| MaterialPageRoute<void>( |
| // Capture the shadow Theme. |
| builder: (BuildContext _) => InheritedTheme.captureAll(context, primaryBox), |
| ), |
| ); |
| }, |
| ), |
| ], |
| ), |
| ); |
| }, |
| ), |
| ); |
| }, |
| ), |
| ), |
| ); |
| } |
| |
| Color containerColor() { |
| return tester.widget<Container>(find.byKey(primaryContainerKey)).color!; |
| } |
| |
| await tester.pumpWidget(buildFrame()); |
| |
| // Show the route which contains primaryBox which was wrapped with |
| // InheritedTheme.captureAll(). |
| await tester.tap(find.text('push wrapped')); |
| await tester.pumpAndSettle(); // route animation |
| expect(containerColor(), primaryColor); |
| |
| Navigator.of(navigatorContext).pop(); |
| await tester.pumpAndSettle(); // route animation |
| |
| // Show the route which contains primaryBox |
| await tester.tap(find.text('push unwrapped')); |
| await tester.pumpAndSettle(); // route animation |
| expect(containerColor(), isNot(primaryColor)); |
| }); |
| |
| testWidgets('PopupMenuTheme.wrap()', (WidgetTester tester) async { |
| const double menuFontSize = 24; |
| const Color menuTextColor = Color(0xFF0000FF); |
| |
| Widget buildFrame() { |
| return MaterialApp( |
| home: Scaffold( |
| body: PopupMenuTheme( |
| data: const PopupMenuThemeData( |
| // The menu route's elevation, shape, and color are defined by the |
| // current context, so they're not affected by ThemeData.captureAll(). |
| textStyle: TextStyle(fontSize: menuFontSize, color: menuTextColor), |
| ), |
| child: Center( |
| child: PopupMenuButton<int>( |
| // The appearance of the menu items' text is defined by the |
| // PopupMenuTheme defined above. Popup menus use |
| // InheritedTheme.captureAll() by default. |
| child: const Text('show popupmenu'), |
| onSelected: (int result) { }, |
| itemBuilder: (BuildContext context) { |
| return const <PopupMenuEntry<int>>[ |
| PopupMenuItem<int>(value: 1, child: Text('One')), |
| PopupMenuItem<int>(value: 2, child: Text('Two')), |
| ]; |
| }, |
| ), |
| ), |
| ), |
| ), |
| ); |
| } |
| |
| TextStyle itemTextStyle(String text) { |
| return tester.widget<RichText>( |
| find.descendant(of: find.text(text), matching: find.byType(RichText)), |
| ).text.style!; |
| } |
| |
| await tester.pumpWidget(buildFrame()); |
| |
| await tester.tap(find.text('show popupmenu')); |
| await tester.pumpAndSettle(); // menu route animation |
| expect(itemTextStyle('One').fontSize, menuFontSize); |
| expect(itemTextStyle('One').color, menuTextColor); |
| expect(itemTextStyle('Two').fontSize, menuFontSize); |
| expect(itemTextStyle('Two').color, menuTextColor); |
| |
| // Dismiss the menu |
| await tester.tap(find.text('One')); |
| await tester.pumpAndSettle(); // menu route animation |
| }); |
| |
| testWidgets('BannerTheme.wrap()', (WidgetTester tester) async { |
| const Color bannerBackgroundColor = Color(0xFF0000FF); |
| const double bannerFontSize = 48; |
| const Color bannerTextColor = Color(0xFF00FF00); |
| |
| final Widget banner = MaterialBanner( |
| content: const Text('hello'), |
| actions: <Widget>[ |
| TextButton( |
| child: const Text('action'), |
| onPressed: () { }, |
| ), |
| ], |
| ); |
| |
| late BuildContext navigatorContext; |
| |
| Widget buildFrame() { |
| return MaterialApp( |
| home: Scaffold( |
| body: MaterialBannerTheme( |
| data: const MaterialBannerThemeData( |
| backgroundColor: bannerBackgroundColor, |
| contentTextStyle: TextStyle(fontSize: bannerFontSize, color: bannerTextColor), |
| ), |
| child: Builder( // Introduce a context so the shadow BannerTheme is visible to captureAll(). |
| builder: (BuildContext context) { |
| navigatorContext = context; |
| return Center( |
| child: Column( |
| mainAxisSize: MainAxisSize.min, |
| children: <Widget>[ |
| ElevatedButton( |
| child: const Text('push unwrapped'), |
| onPressed: () { |
| Navigator.of(context).push<void>( |
| MaterialPageRoute<void>( |
| // The Banner will see the default BannerTheme when built. |
| builder: (BuildContext _) => banner, |
| ), |
| ); |
| }, |
| ), |
| ElevatedButton( |
| child: const Text('push wrapped'), |
| onPressed: () { |
| Navigator.of(context).push<void>( |
| MaterialPageRoute<void>( |
| // Capture the shadow BannerTheme. |
| builder: (BuildContext _) => InheritedTheme.captureAll(context, banner), |
| ), |
| ); |
| }, |
| ), |
| ], |
| ), |
| ); |
| }, |
| ), |
| ), |
| ), |
| ); |
| } |
| |
| Color bannerColor() { |
| return tester.widget<Material>( |
| find.descendant(of: find.byType(MaterialBanner), matching: find.byType(Material)).first, |
| ).color!; |
| } |
| |
| TextStyle getTextStyle(String text) { |
| return tester.widget<RichText>( |
| find.descendant( |
| of: find.text(text), |
| matching: find.byType(RichText), |
| ), |
| ).text.style!; |
| } |
| |
| await tester.pumpWidget(buildFrame()); |
| |
| // Show the route which contains the banner. |
| await tester.tap(find.text('push wrapped')); |
| await tester.pumpAndSettle(); // route animation |
| expect(bannerColor(), bannerBackgroundColor); |
| expect(getTextStyle('hello').fontSize, bannerFontSize); |
| expect(getTextStyle('hello').color, bannerTextColor); |
| |
| Navigator.of(navigatorContext).pop(); |
| await tester.pumpAndSettle(); // route animation |
| |
| await tester.tap(find.text('push unwrapped')); |
| await tester.pumpAndSettle(); // route animation |
| expect(bannerColor(), isNot(bannerBackgroundColor)); |
| expect(getTextStyle('hello').fontSize, isNot(bannerFontSize)); |
| expect(getTextStyle('hello').color, isNot(bannerTextColor)); |
| }); |
| |
| testWidgets('DividerTheme.wrap()', (WidgetTester tester) async { |
| const Color dividerColor = Color(0xFF0000FF); |
| const double dividerSpace = 13; |
| const double dividerThickness = 7; |
| const Widget divider = Center(child: Divider()); |
| |
| late BuildContext navigatorContext; |
| |
| Widget buildFrame() { |
| return MaterialApp( |
| home: Scaffold( |
| body: DividerTheme( |
| data: const DividerThemeData( |
| color: dividerColor, |
| space: dividerSpace, |
| thickness: dividerThickness, |
| ), |
| child: Builder( // Introduce a context so the shadow DividerTheme is visible to captureAll(). |
| builder: (BuildContext context) { |
| navigatorContext = context; |
| return Center( |
| child: Column( |
| mainAxisSize: MainAxisSize.min, |
| children: <Widget>[ |
| ElevatedButton( |
| child: const Text('push unwrapped'), |
| onPressed: () { |
| Navigator.of(context).push<void>( |
| MaterialPageRoute<void>( |
| // The Banner will see the default BannerTheme when built. |
| builder: (BuildContext _) => divider, |
| ), |
| ); |
| }, |
| ), |
| ElevatedButton( |
| child: const Text('push wrapped'), |
| onPressed: () { |
| Navigator.of(context).push<void>( |
| MaterialPageRoute<void>( |
| // Capture the shadow BannerTheme. |
| builder: (BuildContext _) => InheritedTheme.captureAll(context, divider), |
| ), |
| ); |
| }, |
| ), |
| ], |
| ), |
| ); |
| }, |
| ), |
| ), |
| ), |
| ); |
| } |
| |
| BorderSide dividerBorder() { |
| final BoxDecoration decoration = tester.widget<Container>( |
| find.descendant(of: find.byType(Divider), matching: find.byType(Container)).first, |
| ).decoration! as BoxDecoration; |
| return decoration.border!.bottom; |
| } |
| |
| await tester.pumpWidget(buildFrame()); |
| |
| // Show a route which contains a divider. |
| await tester.tap(find.text('push wrapped')); |
| await tester.pumpAndSettle(); // route animation |
| expect(tester.getSize(find.byType(Divider)).height, dividerSpace); |
| expect(dividerBorder().color, dividerColor); |
| expect(dividerBorder().width, dividerThickness); |
| |
| Navigator.of(navigatorContext).pop(); |
| await tester.pumpAndSettle(); // route animation |
| |
| await tester.tap(find.text('push unwrapped')); |
| await tester.pumpAndSettle(); // route animation |
| expect(tester.getSize(find.byType(Divider)).height, isNot(dividerSpace)); |
| expect(dividerBorder().color, isNot(dividerColor)); |
| expect(dividerBorder().width, isNot(dividerThickness)); |
| }); |
| |
| testWidgets('ListTileTheme.wrap()', (WidgetTester tester) async { |
| const Color tileSelectedColor = Color(0xFF00FF00); |
| const Color tileIconColor = Color(0xFF0000FF); |
| const Color tileTextColor = Color(0xFFFF0000); |
| |
| final Key selectedIconKey = UniqueKey(); |
| final Key unselectedIconKey = UniqueKey(); |
| |
| final Widget listTiles = Scaffold( |
| body: Center( |
| child: Column( |
| mainAxisSize: MainAxisSize.min, |
| children: <Widget>[ |
| ListTile( |
| leading: Icon(Icons.computer, key: selectedIconKey), |
| title: const Text('selected'), |
| selected: true, |
| ), |
| ListTile( |
| leading: Icon(Icons.add, key: unselectedIconKey), |
| title: const Text('unselected'), |
| ), |
| ], |
| ), |
| ), |
| ); |
| |
| late BuildContext navigatorContext; |
| |
| Widget buildFrame() { |
| return MaterialApp( |
| home: Scaffold( |
| body: ListTileTheme( |
| selectedColor: tileSelectedColor, |
| textColor: tileTextColor, |
| iconColor: tileIconColor, |
| child: Builder( // Introduce a context so the shadow ListTileTheme is visible to captureAll(). |
| builder: (BuildContext context) { |
| navigatorContext = context; |
| return Center( |
| child: Column( |
| mainAxisSize: MainAxisSize.min, |
| children: <Widget>[ |
| ElevatedButton( |
| child: const Text('push unwrapped'), |
| onPressed: () { |
| Navigator.of(context).push<void>( |
| MaterialPageRoute<void>( |
| // The Banner will see the default BannerTheme when built. |
| builder: (BuildContext _) => listTiles, |
| ), |
| ); |
| }, |
| ), |
| ElevatedButton( |
| child: const Text('push wrapped'), |
| onPressed: () { |
| Navigator.of(context).push<void>( |
| MaterialPageRoute<void>( |
| // Capture the shadow BannerTheme. |
| builder: (BuildContext _) => InheritedTheme.captureAll(context, listTiles), |
| ), |
| ); |
| }, |
| ), |
| ], |
| ), |
| ); |
| }, |
| ), |
| ), |
| ), |
| ); |
| } |
| |
| TextStyle getTextStyle(String text) { |
| return tester.widget<RichText>( |
| find.descendant(of: find.text(text), matching: find.byType(RichText)), |
| ).text.style!; |
| } |
| |
| TextStyle getIconStyle(Key key) { |
| return tester.widget<RichText>( |
| find.descendant( |
| of: find.byKey(key), |
| matching: find.byType(RichText), |
| ), |
| ).text.style!; |
| } |
| |
| await tester.pumpWidget(buildFrame()); |
| |
| // Show a route which contains listTiles. |
| await tester.tap(find.text('push wrapped')); |
| await tester.pumpAndSettle(); // route animation |
| expect(getTextStyle('unselected').color, tileTextColor); |
| expect(getTextStyle('selected').color, tileSelectedColor); |
| expect(getIconStyle(selectedIconKey).color, tileSelectedColor); |
| expect(getIconStyle(unselectedIconKey).color, tileIconColor); |
| |
| Navigator.of(navigatorContext).pop(); |
| await tester.pumpAndSettle(); // route animation |
| |
| await tester.tap(find.text('push unwrapped')); |
| await tester.pumpAndSettle(); // route animation |
| expect(getTextStyle('unselected').color, isNot(tileTextColor)); |
| expect(getTextStyle('selected').color, isNot(tileSelectedColor)); |
| expect(getIconStyle(selectedIconKey).color, isNot(tileSelectedColor)); |
| expect(getIconStyle(unselectedIconKey).color, isNot(tileIconColor)); |
| }); |
| |
| testWidgets('SliderTheme.wrap()', (WidgetTester tester) async { |
| const Color activeTrackColor = Color(0xFF00FF00); |
| const Color inactiveTrackColor = Color(0xFF0000FF); |
| const Color thumbColor = Color(0xFFFF0000); |
| |
| final Widget slider = Scaffold( |
| body: Center( |
| child: Slider( |
| value: 0.5, |
| onChanged: (double value) { }, |
| ), |
| ), |
| ); |
| |
| late BuildContext navigatorContext; |
| |
| Widget buildFrame() { |
| return MaterialApp( |
| home: Scaffold( |
| body: SliderTheme( |
| data: const SliderThemeData( |
| activeTrackColor: activeTrackColor, |
| inactiveTrackColor: inactiveTrackColor, |
| thumbColor: thumbColor, |
| ), |
| child: Builder( // Introduce a context so the shadow SliderTheme is visible to captureAll(). |
| builder: (BuildContext context) { |
| navigatorContext = context; |
| return Center( |
| child: Column( |
| mainAxisSize: MainAxisSize.min, |
| children: <Widget>[ |
| ElevatedButton( |
| child: const Text('push unwrapped'), |
| onPressed: () { |
| Navigator.of(context).push<void>( |
| MaterialPageRoute<void>( |
| // The slider will see the default SliderTheme when built. |
| builder: (BuildContext _) => slider, |
| ), |
| ); |
| }, |
| ), |
| ElevatedButton( |
| child: const Text('push wrapped'), |
| onPressed: () { |
| Navigator.of(context).push<void>( |
| MaterialPageRoute<void>( |
| // Capture the shadow SliderTheme. |
| builder: (BuildContext _) => InheritedTheme.captureAll(context, slider), |
| ), |
| ); |
| }, |
| ), |
| ], |
| ), |
| ); |
| }, |
| ), |
| ), |
| ), |
| ); |
| } |
| |
| await tester.pumpWidget(buildFrame()); |
| |
| // Show a route which contains listTiles. |
| await tester.tap(find.text('push wrapped')); |
| await tester.pumpAndSettle(); // route animation |
| RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider)); |
| expect(sliderBox, paints..rrect(color: activeTrackColor)..rrect(color: inactiveTrackColor)); |
| expect(sliderBox, paints..circle(color: thumbColor)); |
| |
| Navigator.of(navigatorContext).pop(); |
| await tester.pumpAndSettle(); // route animation |
| |
| await tester.tap(find.text('push unwrapped')); |
| await tester.pumpAndSettle(); // route animation |
| sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider)); |
| expect(sliderBox, isNot(paints..rrect(color: activeTrackColor)..rrect(color: inactiveTrackColor))); |
| expect(sliderBox, isNot(paints..circle(color: thumbColor))); |
| }); |
| |
| testWidgets('ToggleButtonsTheme.wrap()', (WidgetTester tester) async { |
| const Color buttonColor = Color(0xFF00FF00); |
| const Color selectedButtonColor = Color(0xFFFF0000); |
| |
| final Widget toggleButtons = Scaffold( |
| body: Center( |
| child: ToggleButtons( |
| isSelected: const <bool>[true, false], |
| children: const <Widget>[ |
| Text('selected'), |
| Text('unselected'), |
| ], |
| onPressed: (int index) { }, |
| ), |
| ), |
| ); |
| |
| late BuildContext navigatorContext; |
| |
| Widget buildFrame() { |
| return MaterialApp( |
| home: Scaffold( |
| body: ToggleButtonsTheme( |
| data: const ToggleButtonsThemeData( |
| color: buttonColor, |
| selectedColor: selectedButtonColor, |
| ), |
| child: Builder( // Introduce a context so the shadow ToggleButtonsTheme is visible to captureAll(). |
| builder: (BuildContext context) { |
| navigatorContext = context; |
| return Center( |
| child: Column( |
| mainAxisSize: MainAxisSize.min, |
| children: <Widget>[ |
| ElevatedButton( |
| child: const Text('push unwrapped'), |
| onPressed: () { |
| Navigator.of(context).push<void>( |
| MaterialPageRoute<void>( |
| // The slider will see the default ToggleButtonsTheme when built. |
| builder: (BuildContext _) => toggleButtons, |
| ), |
| ); |
| }, |
| ), |
| ElevatedButton( |
| child: const Text('push wrapped'), |
| onPressed: () { |
| Navigator.of(context).push<void>( |
| MaterialPageRoute<void>( |
| // Capture the shadow toggleButtons. |
| builder: (BuildContext _) => InheritedTheme.captureAll(context, toggleButtons), |
| ), |
| ); |
| }, |
| ), |
| ], |
| ), |
| ); |
| }, |
| ), |
| ), |
| ), |
| ); |
| } |
| |
| Color getTextColor(String text) { |
| return tester.widget<RichText>( |
| find.descendant(of: find.text(text), matching: find.byType(RichText)), |
| ).text.style!.color!; |
| } |
| |
| await tester.pumpWidget(buildFrame()); |
| |
| // Show a route which contains toggleButtons. |
| await tester.tap(find.text('push wrapped')); |
| await tester.pumpAndSettle(); // route animation |
| expect(getTextColor('selected'), selectedButtonColor); |
| expect(getTextColor('unselected'), buttonColor); |
| |
| Navigator.of(navigatorContext).pop(); |
| await tester.pumpAndSettle(); // route animation |
| |
| await tester.tap(find.text('push unwrapped')); |
| await tester.pumpAndSettle(); // route animation |
| expect(getTextColor('selected'), isNot(selectedButtonColor)); |
| expect(getTextColor('unselected'), isNot(buttonColor)); |
| }); |
| } |