| // 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 'dart:async'; |
| import 'dart:ui'; |
| |
| import 'package:flutter/cupertino.dart'; |
| import 'package:flutter/foundation.dart'; |
| import 'package:flutter/material.dart'; |
| import 'package:flutter/rendering.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| void main() { |
| tearDown(() { |
| LicenseRegistry.reset(); |
| }); |
| |
| testWidgets('Material3 has sentence case labels', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| MaterialApp( |
| builder: (BuildContext context, Widget? child) { |
| return MediaQuery( |
| // Display has a vertical hinge down the middle |
| data: const MediaQueryData( |
| size: Size(800, 600), |
| displayFeatures: <DisplayFeature>[ |
| DisplayFeature( |
| bounds: Rect.fromLTRB(390, 0, 410, 600), |
| type: DisplayFeatureType.hinge, |
| state: DisplayFeatureState.unknown, |
| ), |
| ], |
| ), |
| child: child!, |
| ); |
| }, |
| home: Builder( |
| builder: (BuildContext context) => ElevatedButton( |
| onPressed: () { |
| showAboutDialog(context: context, useRootNavigator: false, applicationName: 'A'); |
| }, |
| child: const Text('Show About Dialog'), |
| ), |
| ), |
| ), |
| ); |
| |
| // Open the dialog. |
| await tester.tap(find.byType(ElevatedButton)); |
| await tester.pumpAndSettle(); |
| expect(find.text('Close'), findsOneWidget); |
| expect(find.text('View licenses'), findsOneWidget); |
| }); |
| |
| testWidgets('Material2 - AboutListTile control test', (WidgetTester tester) async { |
| const logo = FlutterLogo(); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| title: 'Pirate app', |
| home: Scaffold( |
| appBar: AppBar(title: const Text('Home')), |
| drawer: Drawer( |
| child: ListView( |
| children: const <Widget>[ |
| AboutListTile( |
| applicationVersion: '0.1.2', |
| applicationIcon: logo, |
| applicationLegalese: 'I am the very model of a modern major general.', |
| aboutBoxChildren: <Widget>[Text('About box')], |
| ), |
| ], |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(find.text('About Pirate app'), findsNothing); |
| expect(find.text('0.1.2'), findsNothing); |
| expect(find.byWidget(logo), findsNothing); |
| expect(find.text('I am the very model of a modern major general.'), findsNothing); |
| expect(find.text('About box'), findsNothing); |
| |
| await tester.tap(find.byType(IconButton)); |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('About Pirate app'), findsOneWidget); |
| expect(find.text('0.1.2'), findsNothing); |
| expect(find.byWidget(logo), findsNothing); |
| expect(find.text('I am the very model of a modern major general.'), findsNothing); |
| expect(find.text('About box'), findsNothing); |
| |
| await tester.tap(find.text('About Pirate app')); |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('About Pirate app'), findsOneWidget); |
| expect(find.text('0.1.2'), findsOneWidget); |
| expect(find.byWidget(logo), findsOneWidget); |
| expect(find.text('I am the very model of a modern major general.'), findsOneWidget); |
| expect(find.text('About box'), findsOneWidget); |
| |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['Pirate package '], 'Pirate license'), |
| ]); |
| }); |
| |
| await tester.tap(find.text('VIEW LICENSES')); |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('Pirate app'), findsOneWidget); |
| expect(find.text('0.1.2'), findsOneWidget); |
| expect(find.byWidget(logo), findsOneWidget); |
| expect(find.text('I am the very model of a modern major general.'), findsOneWidget); |
| await tester.tap(find.text('Pirate package ')); |
| await tester.pumpAndSettle(); |
| expect(find.text('Pirate license'), findsOneWidget); |
| }); |
| |
| testWidgets('Material3 - AboutListTile control test', (WidgetTester tester) async { |
| const logo = FlutterLogo(); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| title: 'Pirate app', |
| home: Scaffold( |
| appBar: AppBar(title: const Text('Home')), |
| drawer: Drawer( |
| child: ListView( |
| children: const <Widget>[ |
| AboutListTile( |
| applicationVersion: '0.1.2', |
| applicationIcon: logo, |
| applicationLegalese: 'I am the very model of a modern major general.', |
| aboutBoxChildren: <Widget>[Text('About box')], |
| ), |
| ], |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(find.text('About Pirate app'), findsNothing); |
| expect(find.text('0.1.2'), findsNothing); |
| expect(find.byWidget(logo), findsNothing); |
| expect(find.text('I am the very model of a modern major general.'), findsNothing); |
| expect(find.text('About box'), findsNothing); |
| |
| await tester.tap(find.byType(IconButton)); |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('About Pirate app'), findsOneWidget); |
| expect(find.text('0.1.2'), findsNothing); |
| expect(find.byWidget(logo), findsNothing); |
| expect(find.text('I am the very model of a modern major general.'), findsNothing); |
| expect(find.text('About box'), findsNothing); |
| |
| await tester.tap(find.text('About Pirate app')); |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('About Pirate app'), findsOneWidget); |
| expect(find.text('0.1.2'), findsOneWidget); |
| expect(find.byWidget(logo), findsOneWidget); |
| expect(find.text('I am the very model of a modern major general.'), findsOneWidget); |
| expect(find.text('About box'), findsOneWidget); |
| |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['Pirate package '], 'Pirate license'), |
| ]); |
| }); |
| |
| await tester.tap(find.text('View licenses')); |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('Pirate app'), findsOneWidget); |
| expect(find.text('0.1.2'), findsOneWidget); |
| expect(find.byWidget(logo), findsOneWidget); |
| expect(find.text('I am the very model of a modern major general.'), findsOneWidget); |
| await tester.tap(find.text('Pirate package ')); |
| await tester.pumpAndSettle(); |
| expect(find.text('Pirate license'), findsOneWidget); |
| }); |
| |
| testWidgets('About box logic defaults to executable name for app name', ( |
| WidgetTester tester, |
| ) async { |
| await tester.pumpWidget( |
| const MaterialApp( |
| title: 'flutter_tester', |
| home: Material(child: AboutListTile()), |
| ), |
| ); |
| expect(find.text('About flutter_tester'), findsOneWidget); |
| }); |
| |
| testWidgets('LicensePage control test', (WidgetTester tester) async { |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['AAA'], 'BBB'), |
| ]); |
| }); |
| |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['Another package'], 'Another license'), |
| ]); |
| }); |
| |
| await tester.pumpWidget(const MaterialApp(home: Center(child: LicensePage()))); |
| |
| expect(find.text('AAA'), findsNothing); |
| expect(find.text('BBB'), findsNothing); |
| expect(find.text('Another package'), findsNothing); |
| expect(find.text('Another license'), findsNothing); |
| |
| await tester.pumpAndSettle(); |
| |
| // Check for packages. |
| expect(find.text('AAA'), findsOneWidget); |
| expect(find.text('Another package'), findsOneWidget); |
| |
| // Check license is displayed after entering into license page for 'AAA'. |
| await tester.tap(find.text('AAA')); |
| await tester.pumpAndSettle(); |
| expect(find.text('BBB'), findsOneWidget); |
| |
| /// Go back to list of packages. |
| await tester.pageBack(); |
| await tester.pumpAndSettle(); |
| |
| /// Check license is displayed after entering into license page for |
| /// 'Another package'. |
| await tester.tap(find.text('Another package')); |
| await tester.pumpAndSettle(); |
| expect(find.text('Another license'), findsOneWidget); |
| }); |
| |
| testWidgets('LicensePage control test with all properties', (WidgetTester tester) async { |
| const logo = FlutterLogo(); |
| |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['AAA'], 'BBB'), |
| ]); |
| }); |
| |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['Another package'], 'Another license'), |
| ]); |
| }); |
| |
| await tester.pumpWidget( |
| const MaterialApp( |
| title: 'Pirate app', |
| home: Center( |
| child: LicensePage( |
| applicationName: 'LicensePage test app', |
| applicationVersion: '0.1.2', |
| applicationIcon: logo, |
| applicationLegalese: 'I am the very model of a modern major general.', |
| ), |
| ), |
| ), |
| ); |
| |
| expect(find.text('Pirate app'), findsNothing); |
| expect(find.text('LicensePage test app'), findsOneWidget); |
| expect(find.text('0.1.2'), findsOneWidget); |
| expect(find.byWidget(logo), findsOneWidget); |
| expect(find.text('I am the very model of a modern major general.'), findsOneWidget); |
| expect(find.text('AAA'), findsNothing); |
| expect(find.text('BBB'), findsNothing); |
| expect(find.text('Another package'), findsNothing); |
| expect(find.text('Another license'), findsNothing); |
| |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('Pirate app'), findsNothing); |
| expect(find.text('LicensePage test app'), findsOneWidget); |
| expect(find.text('0.1.2'), findsOneWidget); |
| expect(find.byWidget(logo), findsOneWidget); |
| expect(find.text('I am the very model of a modern major general.'), findsOneWidget); |
| |
| // Check for packages. |
| expect(find.text('AAA'), findsOneWidget); |
| expect(find.text('Another package'), findsOneWidget); |
| |
| // Check license is displayed after entering into license page for 'AAA'. |
| await tester.tap(find.text('AAA')); |
| await tester.pumpAndSettle(); |
| expect(find.text('BBB'), findsOneWidget); |
| |
| /// Go back to list of packages. |
| await tester.pageBack(); |
| await tester.pumpAndSettle(); |
| |
| /// Check license is displayed after entering into license page for |
| /// 'Another package'. |
| await tester.tap(find.text('Another package')); |
| await tester.pumpAndSettle(); |
| expect(find.text('Another license'), findsOneWidget); |
| }); |
| |
| testWidgets('Material2 - _PackageLicensePage title style without AppBarTheme', ( |
| WidgetTester tester, |
| ) async { |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['AAA'], 'BBB'), |
| ]); |
| }); |
| |
| const titleTextStyle = TextStyle(fontSize: 20, color: Colors.black, inherit: false); |
| const subtitleTextStyle = TextStyle(fontSize: 15, color: Colors.red, inherit: false); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData( |
| useMaterial3: false, |
| primaryTextTheme: const TextTheme( |
| titleLarge: titleTextStyle, |
| titleSmall: subtitleTextStyle, |
| ), |
| ), |
| home: const Center(child: LicensePage()), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| |
| // Check for packages. |
| expect(find.text('AAA'), findsOneWidget); |
| |
| // Check license is displayed after entering into license page for 'AAA'. |
| await tester.tap(find.text('AAA')); |
| await tester.pumpAndSettle(); |
| |
| // Check for titles style. |
| final Text title = tester.widget(find.text('AAA')); |
| expect(title.style, titleTextStyle); |
| final Text subtitle = tester.widget(find.text('1 license.')); |
| expect(subtitle.style, subtitleTextStyle); |
| }); |
| |
| testWidgets('Material3 - _PackageLicensePage title style without AppBarTheme', ( |
| WidgetTester tester, |
| ) async { |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['AAA'], 'BBB'), |
| ]); |
| }); |
| |
| const titleTextStyle = TextStyle(fontSize: 20, color: Colors.black, inherit: false); |
| const subtitleTextStyle = TextStyle(fontSize: 15, color: Colors.red, inherit: false); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData( |
| textTheme: const TextTheme(titleLarge: titleTextStyle, titleSmall: subtitleTextStyle), |
| ), |
| home: const Center(child: LicensePage()), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| |
| // Check for packages. |
| expect(find.text('AAA'), findsOneWidget); |
| |
| // Check license is displayed after entering into license page for 'AAA'. |
| await tester.tap(find.text('AAA')); |
| await tester.pumpAndSettle(); |
| |
| // Check for titles style. |
| final Text title = tester.widget(find.text('AAA')); |
| expect(title.style, titleTextStyle); |
| final Text subtitle = tester.widget(find.text('1 license.')); |
| expect(subtitle.style, subtitleTextStyle); |
| }); |
| |
| testWidgets('_PackageLicensePage title style with AppBarTheme', (WidgetTester tester) async { |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['AAA'], 'BBB'), |
| ]); |
| }); |
| |
| const titleTextStyle = TextStyle(fontSize: 20, color: Colors.indigo); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData( |
| // Not used because appBarTheme is prioritized. |
| primaryTextTheme: const TextTheme( |
| titleLarge: TextStyle(fontSize: 12, color: Colors.grey), |
| titleSmall: TextStyle(fontSize: 10, color: Colors.grey), |
| ), |
| appBarTheme: const AppBarTheme( |
| titleTextStyle: titleTextStyle, |
| foregroundColor: Colors.indigo, |
| ), |
| ), |
| home: const Center(child: LicensePage()), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| |
| // Check for packages. |
| expect(find.text('AAA'), findsOneWidget); |
| |
| // Check license is displayed after entering into license page for 'AAA'. |
| await tester.tap(find.text('AAA')); |
| await tester.pumpAndSettle(); |
| |
| // Check for titles style. |
| final Text title = tester.widget(find.text('AAA')); |
| expect(title.style, titleTextStyle); |
| }); |
| |
| testWidgets('Material2 - LicensePage respects the notch', (WidgetTester tester) async { |
| const safeareaPadding = 27.0; |
| |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['ABC'], 'DEF'), |
| ]); |
| }); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: const MediaQuery( |
| data: MediaQueryData(padding: EdgeInsets.all(safeareaPadding)), |
| child: LicensePage(), |
| ), |
| ), |
| ); |
| |
| await tester.pumpAndSettle(); |
| |
| // The position of the top left of app bar title should indicate whether |
| // the safe area is sufficiently respected. |
| expect( |
| tester.getTopLeft(find.text('Licenses')), |
| const Offset(16.0 + safeareaPadding, 18.0 + safeareaPadding), |
| ); |
| }); |
| |
| testWidgets('Material3 - LicensePage respects the notch', (WidgetTester tester) async { |
| const safeareaPadding = 27.0; |
| |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['ABC'], 'DEF'), |
| ]); |
| }); |
| |
| await tester.pumpWidget( |
| const MaterialApp( |
| home: MediaQuery( |
| data: MediaQueryData(padding: EdgeInsets.all(safeareaPadding)), |
| child: LicensePage(), |
| ), |
| ), |
| ); |
| |
| await tester.pumpAndSettle(); |
| |
| // The position of the top left of app bar title should indicate whether |
| // the safe area is sufficiently respected. |
| expect( |
| tester.getTopLeft(find.text('Licenses')), |
| const Offset(16.0 + safeareaPadding, 14.0 + safeareaPadding), |
| ); |
| }); |
| |
| testWidgets('LicensePage returns early if unmounted', (WidgetTester tester) async { |
| final licenseCompleter = Completer<LicenseEntry>(); |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromFuture(licenseCompleter.future); |
| }); |
| |
| await tester.pumpWidget(const MaterialApp(home: LicensePage())); |
| await tester.pump(); |
| |
| await tester.pumpWidget(const MaterialApp(home: Placeholder())); |
| |
| await tester.pumpAndSettle(); |
| final licenseEntry = FakeLicenseEntry(); |
| licenseCompleter.complete(licenseEntry); |
| expect(licenseEntry.packagesCalled, false); |
| }); |
| |
| testWidgets('LicensePage returns late if unmounted', (WidgetTester tester) async { |
| final licenseCompleter = Completer<LicenseEntry>(); |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromFuture(licenseCompleter.future); |
| }); |
| |
| await tester.pumpWidget(const MaterialApp(home: LicensePage())); |
| await tester.pump(); |
| final licenseEntry = FakeLicenseEntry(); |
| licenseCompleter.complete(licenseEntry); |
| |
| await tester.pumpWidget(const MaterialApp(home: Placeholder())); |
| |
| await tester.pumpAndSettle(); |
| expect(licenseEntry.packagesCalled, true); |
| }); |
| |
| testWidgets('LicensePage logic defaults to executable name for app name', ( |
| WidgetTester tester, |
| ) async { |
| await tester.pumpWidget( |
| const MaterialApp( |
| title: 'flutter_tester', |
| home: Material(child: LicensePage()), |
| ), |
| ); |
| expect(find.text('flutter_tester'), findsOneWidget); |
| }); |
| |
| testWidgets('AboutListTile dense property is applied', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| const MaterialApp( |
| home: Material(child: Center(child: AboutListTile())), |
| ), |
| ); |
| Rect tileRect = tester.getRect(find.byType(AboutListTile)); |
| expect(tileRect.height, 56.0); |
| |
| await tester.pumpWidget( |
| const MaterialApp( |
| home: Material(child: Center(child: AboutListTile(dense: false))), |
| ), |
| ); |
| tileRect = tester.getRect(find.byType(AboutListTile)); |
| expect(tileRect.height, 56.0); |
| |
| await tester.pumpWidget( |
| const MaterialApp( |
| home: Material(child: Center(child: AboutListTile(dense: true))), |
| ), |
| ); |
| tileRect = tester.getRect(find.byType(AboutListTile)); |
| expect(tileRect.height, 48.0); |
| }); |
| |
| testWidgets('showLicensePage uses nested navigator by default', (WidgetTester tester) async { |
| final rootObserver = LicensePageObserver(); |
| final nestedObserver = LicensePageObserver(); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| navigatorObservers: <NavigatorObserver>[rootObserver], |
| initialRoute: '/', |
| onGenerateRoute: (_) { |
| return PageRouteBuilder<dynamic>( |
| pageBuilder: (_, _, _) => Navigator( |
| observers: <NavigatorObserver>[nestedObserver], |
| onGenerateRoute: (RouteSettings settings) { |
| return PageRouteBuilder<dynamic>( |
| pageBuilder: (BuildContext context, _, _) { |
| return ElevatedButton( |
| onPressed: () { |
| showLicensePage(context: context, applicationName: 'A'); |
| }, |
| child: const Text('Show License Page'), |
| ); |
| }, |
| ); |
| }, |
| ), |
| ); |
| }, |
| ), |
| ); |
| |
| // Open the dialog. |
| await tester.tap(find.byType(ElevatedButton)); |
| |
| expect(rootObserver.licensePageCount, 0); |
| expect(nestedObserver.licensePageCount, 1); |
| }); |
| |
| testWidgets('showLicensePage uses root navigator if useRootNavigator is true', ( |
| WidgetTester tester, |
| ) async { |
| final rootObserver = LicensePageObserver(); |
| final nestedObserver = LicensePageObserver(); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| navigatorObservers: <NavigatorObserver>[rootObserver], |
| initialRoute: '/', |
| onGenerateRoute: (_) { |
| return PageRouteBuilder<dynamic>( |
| pageBuilder: (_, _, _) => Navigator( |
| observers: <NavigatorObserver>[nestedObserver], |
| onGenerateRoute: (RouteSettings settings) { |
| return PageRouteBuilder<dynamic>( |
| pageBuilder: (BuildContext context, _, _) { |
| return ElevatedButton( |
| onPressed: () { |
| showLicensePage( |
| context: context, |
| useRootNavigator: true, |
| applicationName: 'A', |
| ); |
| }, |
| child: const Text('Show License Page'), |
| ); |
| }, |
| ); |
| }, |
| ), |
| ); |
| }, |
| ), |
| ); |
| |
| // Open the dialog. |
| await tester.tap(find.byType(ElevatedButton)); |
| |
| expect(rootObserver.licensePageCount, 1); |
| expect(nestedObserver.licensePageCount, 0); |
| }); |
| |
| group('Barrier dismissible', () { |
| late AboutDialogObserver rootObserver; |
| |
| setUp(() { |
| rootObserver = AboutDialogObserver(); |
| }); |
| |
| testWidgets('Barrier is dismissible with default parameter', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| MaterialApp( |
| navigatorObservers: <NavigatorObserver>[rootObserver], |
| home: Material( |
| child: Center( |
| child: Builder( |
| builder: (BuildContext context) { |
| return ElevatedButton( |
| child: const Text('X'), |
| onPressed: () => showAboutDialog(context: context), |
| ); |
| }, |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| // Open the dialog. |
| await tester.tap(find.byType(ElevatedButton)); |
| await tester.pumpAndSettle(); |
| expect(rootObserver.dialogCount, 1); |
| |
| // Tap on the barrier. |
| await tester.tapAt(const Offset(10.0, 10.0)); |
| await tester.pumpAndSettle(); |
| expect(rootObserver.dialogCount, 0); |
| }); |
| |
| testWidgets('Barrier is not dismissible with barrierDismissible is false', ( |
| WidgetTester tester, |
| ) async { |
| await tester.pumpWidget( |
| MaterialApp( |
| navigatorObservers: <NavigatorObserver>[rootObserver], |
| home: Material( |
| child: Center( |
| child: Builder( |
| builder: (BuildContext context) { |
| return ElevatedButton( |
| child: const Text('X'), |
| onPressed: () => showAboutDialog(context: context, barrierDismissible: false), |
| ); |
| }, |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| // Open the dialog. |
| await tester.tap(find.byType(ElevatedButton)); |
| await tester.pumpAndSettle(); |
| expect(rootObserver.dialogCount, 1); |
| |
| // Tap on the barrier, which shouldn't do anything this time. |
| await tester.tapAt(const Offset(10.0, 10.0)); |
| await tester.pumpAndSettle(); |
| expect(rootObserver.dialogCount, 1); |
| }); |
| }); |
| |
| testWidgets('Barrier color', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Material( |
| child: Center( |
| child: Builder( |
| builder: (BuildContext context) { |
| return ElevatedButton( |
| child: const Text('X'), |
| onPressed: () => showAboutDialog(context: context), |
| ); |
| }, |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| // Open the dialog. |
| await tester.tap(find.byType(ElevatedButton)); |
| await tester.pumpAndSettle(); |
| expect(tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).color, Colors.black54); |
| |
| // Dismiss the dialog. |
| await tester.tapAt(const Offset(10.0, 10.0)); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Material( |
| child: Center( |
| child: Builder( |
| builder: (BuildContext context) { |
| return ElevatedButton( |
| child: const Text('X'), |
| onPressed: () => showAboutDialog(context: context, barrierColor: Colors.pink), |
| ); |
| }, |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| // Open the dialog. |
| await tester.tap(find.byType(ElevatedButton)); |
| await tester.pumpAndSettle(); |
| expect(tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).color, Colors.pink); |
| }); |
| |
| testWidgets('Barrier Label', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Material( |
| child: Center( |
| child: Builder( |
| builder: (BuildContext context) { |
| return ElevatedButton( |
| child: const Text('X'), |
| onPressed: () => showAboutDialog(context: context, barrierLabel: 'Custom Label'), |
| ); |
| }, |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| // Open the dialog. |
| await tester.tap(find.byType(ElevatedButton)); |
| await tester.pumpAndSettle(); |
| expect( |
| tester.widget<ModalBarrier>(find.byType(ModalBarrier).last).semanticsLabel, |
| 'Custom Label', |
| ); |
| }); |
| |
| testWidgets('showAboutDialog uses root navigator by default', (WidgetTester tester) async { |
| final rootObserver = AboutDialogObserver(); |
| final nestedObserver = AboutDialogObserver(); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| navigatorObservers: <NavigatorObserver>[rootObserver], |
| home: Navigator( |
| observers: <NavigatorObserver>[nestedObserver], |
| onGenerateRoute: (RouteSettings settings) { |
| return MaterialPageRoute<dynamic>( |
| builder: (BuildContext context) { |
| return ElevatedButton( |
| onPressed: () { |
| showAboutDialog(context: context, applicationName: 'A'); |
| }, |
| child: const Text('Show About Dialog'), |
| ); |
| }, |
| ); |
| }, |
| ), |
| ), |
| ); |
| |
| // Open the dialog. |
| await tester.tap(find.byType(ElevatedButton)); |
| |
| expect(rootObserver.dialogCount, 1); |
| expect(nestedObserver.dialogCount, 0); |
| }); |
| |
| testWidgets('showAboutDialog uses nested navigator if useRootNavigator is false', ( |
| WidgetTester tester, |
| ) async { |
| final rootObserver = AboutDialogObserver(); |
| final nestedObserver = AboutDialogObserver(); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| navigatorObservers: <NavigatorObserver>[rootObserver], |
| home: Navigator( |
| observers: <NavigatorObserver>[nestedObserver], |
| onGenerateRoute: (RouteSettings settings) { |
| return MaterialPageRoute<dynamic>( |
| builder: (BuildContext context) { |
| return ElevatedButton( |
| onPressed: () { |
| showAboutDialog( |
| context: context, |
| useRootNavigator: false, |
| applicationName: 'A', |
| ); |
| }, |
| child: const Text('Show About Dialog'), |
| ); |
| }, |
| ); |
| }, |
| ), |
| ), |
| ); |
| |
| // Open the dialog. |
| await tester.tap(find.byType(ElevatedButton)); |
| |
| expect(rootObserver.dialogCount, 0); |
| expect(nestedObserver.dialogCount, 1); |
| }); |
| |
| group('showAboutDialog avoids overlapping display features', () { |
| testWidgets('default positioning', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| MaterialApp( |
| builder: (BuildContext context, Widget? child) { |
| return MediaQuery( |
| // Display has a vertical hinge down the middle |
| data: const MediaQueryData( |
| size: Size(800, 600), |
| displayFeatures: <DisplayFeature>[ |
| DisplayFeature( |
| bounds: Rect.fromLTRB(390, 0, 410, 600), |
| type: DisplayFeatureType.hinge, |
| state: DisplayFeatureState.unknown, |
| ), |
| ], |
| ), |
| child: child!, |
| ); |
| }, |
| home: Builder( |
| builder: (BuildContext context) => ElevatedButton( |
| onPressed: () { |
| showAboutDialog(context: context, useRootNavigator: false, applicationName: 'A'); |
| }, |
| child: const Text('Show About Dialog'), |
| ), |
| ), |
| ), |
| ); |
| |
| // Open the dialog. |
| await tester.tap(find.byType(ElevatedButton)); |
| await tester.pumpAndSettle(); |
| |
| // By default it should place the dialog on the left screen |
| expect(tester.getTopLeft(find.byType(AboutDialog)), Offset.zero); |
| expect(tester.getBottomRight(find.byType(AboutDialog)), const Offset(390.0, 600.0)); |
| }); |
| |
| testWidgets('positioning using anchorPoint', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| MaterialApp( |
| builder: (BuildContext context, Widget? child) { |
| return MediaQuery( |
| // Display has a vertical hinge down the middle |
| data: const MediaQueryData( |
| size: Size(800, 600), |
| displayFeatures: <DisplayFeature>[ |
| DisplayFeature( |
| bounds: Rect.fromLTRB(390, 0, 410, 600), |
| type: DisplayFeatureType.hinge, |
| state: DisplayFeatureState.unknown, |
| ), |
| ], |
| ), |
| child: child!, |
| ); |
| }, |
| home: Builder( |
| builder: (BuildContext context) => ElevatedButton( |
| onPressed: () { |
| showAboutDialog( |
| context: context, |
| useRootNavigator: false, |
| applicationName: 'A', |
| anchorPoint: const Offset(1000, 0), |
| ); |
| }, |
| child: const Text('Show About Dialog'), |
| ), |
| ), |
| ), |
| ); |
| |
| // Open the dialog. |
| await tester.tap(find.byType(ElevatedButton)); |
| await tester.pumpAndSettle(); |
| |
| // The anchorPoint hits the right side of the display |
| expect(tester.getTopLeft(find.byType(AboutDialog)), const Offset(410.0, 0.0)); |
| expect(tester.getBottomRight(find.byType(AboutDialog)), const Offset(800.0, 600.0)); |
| }); |
| |
| testWidgets('positioning using Directionality', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| MaterialApp( |
| builder: (BuildContext context, Widget? child) { |
| return MediaQuery( |
| // Display has a vertical hinge down the middle |
| data: const MediaQueryData( |
| size: Size(800, 600), |
| displayFeatures: <DisplayFeature>[ |
| DisplayFeature( |
| bounds: Rect.fromLTRB(390, 0, 410, 600), |
| type: DisplayFeatureType.hinge, |
| state: DisplayFeatureState.unknown, |
| ), |
| ], |
| ), |
| child: Directionality(textDirection: TextDirection.rtl, child: child!), |
| ); |
| }, |
| home: Builder( |
| builder: (BuildContext context) => ElevatedButton( |
| onPressed: () { |
| showAboutDialog(context: context, useRootNavigator: false, applicationName: 'A'); |
| }, |
| child: const Text('Show About Dialog'), |
| ), |
| ), |
| ), |
| ); |
| |
| // Open the dialog. |
| await tester.tap(find.byType(ElevatedButton)); |
| await tester.pumpAndSettle(); |
| |
| // Since this is rtl, the first screen is the on the right |
| expect(tester.getTopLeft(find.byType(AboutDialog)), const Offset(410.0, 0.0)); |
| expect(tester.getBottomRight(find.byType(AboutDialog)), const Offset(800.0, 600.0)); |
| }); |
| }); |
| |
| testWidgets("AboutListTile's child should not be offset when the icon is not specified.", ( |
| WidgetTester tester, |
| ) async { |
| await tester.pumpWidget( |
| const MaterialApp( |
| home: Scaffold(body: AboutListTile(child: Text('About'))), |
| ), |
| ); |
| |
| expect( |
| find.descendant(of: find.byType(AboutListTile), matching: find.byType(Icon)), |
| findsNothing, |
| ); |
| }); |
| |
| testWidgets("AboutDialog's contents are scrollable", (WidgetTester tester) async { |
| final Key contentKey = UniqueKey(); |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Navigator( |
| onGenerateRoute: (RouteSettings settings) { |
| return MaterialPageRoute<dynamic>( |
| builder: (BuildContext context) { |
| return ElevatedButton( |
| onPressed: () { |
| showAboutDialog( |
| context: context, |
| useRootNavigator: false, |
| applicationName: 'A', |
| children: <Widget>[ |
| Container(key: contentKey, color: Colors.orange, height: 500), |
| ], |
| ); |
| }, |
| child: const Text('Show About Dialog'), |
| ); |
| }, |
| ); |
| }, |
| ), |
| ), |
| ); |
| |
| await tester.tap(find.text('Show About Dialog')); |
| await tester.pumpAndSettle(); |
| |
| // Try dragging by the [AboutDialog]'s title. |
| RenderBox box = tester.renderObject(find.text('A')); |
| Offset originalOffset = box.localToGlobal(Offset.zero); |
| await tester.drag(find.byKey(contentKey), const Offset(0.0, -20.0)); |
| |
| expect(box.localToGlobal(Offset.zero), equals(originalOffset.translate(0.0, -20.0))); |
| |
| // Try dragging by the additional children in contents. |
| box = tester.renderObject(find.byKey(contentKey)); |
| originalOffset = box.localToGlobal(Offset.zero); |
| await tester.drag(find.byKey(contentKey), const Offset(0.0, -20.0)); |
| |
| expect(box.localToGlobal(Offset.zero), equals(originalOffset.translate(0.0, -20.0))); |
| }); |
| |
| testWidgets("Material2 - LicensePage's color must be same whether loading or done", ( |
| WidgetTester tester, |
| ) async { |
| const scaffoldColor = Color(0xFF123456); |
| const cardColor = Color(0xFF654321); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData.light( |
| useMaterial3: false, |
| ).copyWith(scaffoldBackgroundColor: scaffoldColor, cardColor: cardColor), |
| home: Scaffold( |
| body: Center( |
| child: Builder( |
| builder: (BuildContext context) => GestureDetector( |
| child: const Text('Show licenses'), |
| onTap: () { |
| showLicensePage( |
| context: context, |
| applicationName: 'MyApp', |
| applicationVersion: '1.0.0', |
| ); |
| }, |
| ), |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| await tester.tap(find.text('Show licenses')); |
| await tester.pump(); |
| await tester.pump(); |
| |
| // Check color when loading. |
| final List<Material> materialLoadings = tester |
| .widgetList<Material>(find.byType(Material)) |
| .toList(); |
| expect(materialLoadings.length, equals(4)); |
| expect(materialLoadings[1].color, scaffoldColor); |
| expect(materialLoadings[2].color, cardColor); |
| |
| await tester.pumpAndSettle(); |
| |
| // Check color when done. |
| expect(find.byKey(const ValueKey<ConnectionState>(ConnectionState.done)), findsOneWidget); |
| final List<Material> materialDones = tester |
| .widgetList<Material>(find.byType(Material)) |
| .toList(); |
| expect(materialDones.length, equals(3)); |
| expect(materialDones[0].color, scaffoldColor); |
| expect(materialDones[1].color, cardColor); |
| }); |
| |
| testWidgets("Material3 - LicensePage's color must be same whether loading or done", ( |
| WidgetTester tester, |
| ) async { |
| const scaffoldColor = Color(0xFF123456); |
| const cardColor = Color(0xFF654321); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(scaffoldBackgroundColor: scaffoldColor, cardColor: cardColor), |
| home: Scaffold( |
| body: Center( |
| child: Builder( |
| builder: (BuildContext context) => GestureDetector( |
| child: const Text('Show licenses'), |
| onTap: () { |
| showLicensePage( |
| context: context, |
| applicationName: 'MyApp', |
| applicationVersion: '1.0.0', |
| ); |
| }, |
| ), |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| await tester.tap(find.text('Show licenses')); |
| await tester.pump(); |
| await tester.pump(); |
| |
| // Check color when loading. |
| final List<Material> materialLoadings = tester |
| .widgetList<Material>(find.byType(Material)) |
| .toList(); |
| expect(materialLoadings.length, equals(5)); |
| expect(materialLoadings[1].color, scaffoldColor); |
| expect(materialLoadings[2].color, cardColor); |
| |
| await tester.pumpAndSettle(); |
| |
| // Check color when done. |
| expect(find.byKey(const ValueKey<ConnectionState>(ConnectionState.done)), findsOneWidget); |
| final List<Material> materialDones = tester |
| .widgetList<Material>(find.byType(Material)) |
| .toList(); |
| expect(materialDones.length, equals(4)); |
| expect(materialDones[0].color, scaffoldColor); |
| expect(materialDones[1].color, cardColor); |
| }); |
| |
| testWidgets( |
| 'Conflicting scrollbars are not applied by ScrollBehavior to _PackageLicensePage', |
| (WidgetTester tester) async { |
| // Regression test for https://github.com/flutter/flutter/issues/83819 |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['AAA'], 'BBB'), |
| ]); |
| }); |
| |
| await tester.pumpWidget(const MaterialApp(home: Center(child: LicensePage()))); |
| await tester.pumpAndSettle(); |
| |
| // Check for packages. |
| expect(find.text('AAA'), findsOneWidget); |
| // Check license is displayed after entering into license page for 'AAA'. |
| await tester.tap(find.text('AAA')); |
| await tester.pumpAndSettle(); |
| |
| // The inherited ScrollBehavior should not apply Scrollbars since they are |
| // already built in to the widget. |
| switch (debugDefaultTargetPlatformOverride) { |
| case TargetPlatform.android: |
| case TargetPlatform.fuchsia: |
| case TargetPlatform.linux: |
| case TargetPlatform.macOS: |
| case TargetPlatform.windows: |
| expect(find.byType(CupertinoScrollbar), findsNothing); |
| case TargetPlatform.iOS: |
| expect(find.byType(CupertinoScrollbar), findsOneWidget); |
| case null: |
| break; |
| } |
| expect(find.byType(Scrollbar), findsOneWidget); |
| expect(find.byType(RawScrollbar), findsNothing); |
| }, |
| variant: TargetPlatformVariant.all(), |
| ); |
| |
| testWidgets('ListView of license entries is primary', (WidgetTester tester) async { |
| // Regression test for https://github.com/flutter/flutter/issues/120710 |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| LicenseEntryWithLineBreaks( |
| <String>['AAA'], |
| // Add enough content to scroll |
| List<String>.generate(500, (int index) => 'BBBB').join('\n'), |
| ), |
| ]); |
| }); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| title: 'Flutter Code Sample', |
| home: Scaffold( |
| body: Builder( |
| builder: (BuildContext context) => TextButton( |
| child: const Text('Show License Page'), |
| onPressed: () { |
| showLicensePage(context: context); |
| }, |
| ), |
| ), |
| ), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('Show License Page'), findsOneWidget); |
| await tester.tap(find.text('Show License Page')); |
| await tester.pumpAndSettle(); |
| |
| // Check for packages. |
| expect(find.text('AAA'), findsOneWidget); |
| // Check license is displayed after entering into license page for 'AAA'. |
| await tester.tap(find.text('AAA')); |
| await tester.pumpAndSettle(); |
| |
| // The inherited ScrollBehavior should not apply Scrollbars since they are |
| // already built in to the widget. |
| switch (debugDefaultTargetPlatformOverride) { |
| case TargetPlatform.android: |
| case TargetPlatform.fuchsia: |
| case TargetPlatform.linux: |
| case TargetPlatform.macOS: |
| case TargetPlatform.windows: |
| expect(find.byType(CupertinoScrollbar), findsNothing); |
| case TargetPlatform.iOS: |
| expect(find.byType(CupertinoScrollbar), findsOneWidget); |
| case null: |
| break; |
| } |
| expect(find.byType(Scrollbar), findsOneWidget); |
| expect(find.byType(RawScrollbar), findsNothing); |
| await tester.drag(find.byType(ListView), const Offset(0.0, 20.0)); |
| await tester.pumpAndSettle(); // No exception triggered. |
| }, variant: TargetPlatformVariant.all()); |
| |
| testWidgets('LicensePage padding', (WidgetTester tester) async { |
| const logo = FlutterLogo(); |
| |
| await tester.pumpWidget( |
| const MaterialApp( |
| title: 'Pirate app', |
| home: Center( |
| child: LicensePage( |
| applicationName: 'LicensePage test app', |
| applicationIcon: logo, |
| applicationVersion: '0.1.2', |
| applicationLegalese: 'I am the very model of a modern major general.', |
| ), |
| ), |
| ), |
| ); |
| |
| final Finder appName = find.text('LicensePage test app'); |
| final Finder appIcon = find.byType(FlutterLogo); |
| final Finder appVersion = find.text('0.1.2'); |
| final Finder appLegalese = find.text('I am the very model of a modern major general.'); |
| final Finder appPowered = find.text('Powered by Flutter'); |
| |
| expect(appName, findsOneWidget); |
| expect(appIcon, findsOneWidget); |
| expect(appVersion, findsOneWidget); |
| expect(appLegalese, findsOneWidget); |
| expect(appPowered, findsOneWidget); |
| |
| // Bottom padding is applied to the app version and app legalese text. |
| final double appNameBottomPadding = |
| tester.getTopLeft(appIcon).dy - tester.getBottomLeft(appName).dy; |
| expect(appNameBottomPadding, 0.0); |
| |
| final double appIconBottomPadding = |
| tester.getTopLeft(appVersion).dy - tester.getBottomLeft(appIcon).dy; |
| expect(appIconBottomPadding, 0.0); |
| |
| final double appVersionBottomPadding = |
| tester.getTopLeft(appLegalese).dy - tester.getBottomLeft(appVersion).dy; |
| expect(appVersionBottomPadding, 18.0); |
| |
| final double appLegaleseBottomPadding = |
| tester.getTopLeft(appPowered).dy - tester.getBottomLeft(appLegalese).dy; |
| expect(appLegaleseBottomPadding, 18.0); |
| }); |
| |
| testWidgets('LicensePage has no extra padding between app icon and app powered text', ( |
| WidgetTester tester, |
| ) async { |
| // This is a regression test for https://github.com/flutter/flutter/issues/99559 |
| |
| const logo = FlutterLogo(); |
| |
| await tester.pumpWidget( |
| const MaterialApp( |
| title: 'Pirate app', |
| home: Center(child: LicensePage(applicationIcon: logo)), |
| ), |
| ); |
| |
| final Finder appName = find.text('LicensePage test app'); |
| final Finder appIcon = find.byType(FlutterLogo); |
| final Finder appVersion = find.text('0.1.2'); |
| final Finder appLegalese = find.text('I am the very model of a modern major general.'); |
| final Finder appPowered = find.text('Powered by Flutter'); |
| |
| expect(appName, findsNothing); |
| expect(appIcon, findsOneWidget); |
| expect(appVersion, findsNothing); |
| expect(appLegalese, findsNothing); |
| expect(appPowered, findsOneWidget); |
| |
| // Padding between app icon and app powered text. |
| final double appIconBottomPadding = |
| tester.getTopLeft(appPowered).dy - tester.getBottomLeft(appIcon).dy; |
| expect(appIconBottomPadding, 18.0); |
| }); |
| |
| testWidgets('Material2 - Error handling test', (WidgetTester tester) async { |
| LicenseRegistry.addLicense(() => Stream<LicenseEntry>.error(Exception('Injected failure'))); |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| home: const Material(child: AboutListTile()), |
| ), |
| ); |
| await tester.tap(find.byType(ListTile)); |
| await tester.pump(); |
| await tester.pump(const Duration(seconds: 2)); |
| await tester.tap(find.text('VIEW LICENSES')); |
| await tester.pump(); |
| await tester.pump(const Duration(seconds: 2)); |
| final Finder finder = find.byWidgetPredicate( |
| (Widget widget) => widget.runtimeType.toString() == '_PackagesView', |
| ); |
| // force the stream to complete (has to be done in a runAsync block since it's areal async process) |
| await tester.runAsync(() => (tester.firstState(finder) as dynamic).licenses as Future<dynamic>); |
| expect(tester.takeException().toString(), 'Exception: Injected failure'); |
| await tester.pumpAndSettle(); |
| expect(tester.takeException().toString(), 'Exception: Injected failure'); |
| expect(find.text('Exception: Injected failure'), findsOneWidget); |
| }); |
| |
| testWidgets('Material3 - Error handling test', (WidgetTester tester) async { |
| LicenseRegistry.addLicense(() => Stream<LicenseEntry>.error(Exception('Injected failure'))); |
| await tester.pumpWidget(const MaterialApp(home: Material(child: AboutListTile()))); |
| await tester.tap(find.byType(ListTile)); |
| await tester.pump(); |
| await tester.pump(const Duration(seconds: 2)); |
| await tester.tap(find.text('View licenses')); |
| await tester.pump(); |
| await tester.pump(const Duration(seconds: 2)); |
| final Finder finder = find.byWidgetPredicate( |
| (Widget widget) => widget.runtimeType.toString() == '_PackagesView', |
| ); |
| // force the stream to complete (has to be done in a runAsync block since it's areal async process) |
| await tester.runAsync(() => (tester.firstState(finder) as dynamic).licenses as Future<dynamic>); |
| expect(tester.takeException().toString(), 'Exception: Injected failure'); |
| await tester.pumpAndSettle(); |
| expect(tester.takeException().toString(), 'Exception: Injected failure'); |
| expect(find.text('Exception: Injected failure'), findsOneWidget); |
| }); |
| |
| testWidgets('Material2 - LicensePage master view layout position - ltr', ( |
| WidgetTester tester, |
| ) async { |
| const TextDirection textDirection = TextDirection.ltr; |
| const defaultSize = Size(800.0, 600.0); |
| const wideSize = Size(1200.0, 600.0); |
| const title = 'License ABC'; |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['ABC'], 'DEF'), |
| ]); |
| }); |
| |
| addTearDown(() async { |
| await tester.binding.setSurfaceSize(null); |
| }); |
| |
| // Configure to show the default layout. |
| await tester.binding.setSurfaceSize(defaultSize); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| title: title, |
| home: const Scaffold( |
| body: Directionality(textDirection: textDirection, child: LicensePage()), |
| ), |
| ), |
| ); |
| |
| await tester.pumpAndSettle(); // Finish rendering the page. |
| |
| // If the layout width is less than 840.0 pixels, nested layout is |
| // used which positions license page title at the top center. |
| Offset titleOffset = tester.getCenter(find.text(title)); |
| expect(titleOffset, Offset(defaultSize.width / 2, 92.0)); |
| expect(tester.getCenter(find.byType(ListView)), Offset(defaultSize.width / 2, 328.0)); |
| |
| // Configure a wide window to show the lateral UI. |
| await tester.binding.setSurfaceSize(wideSize); |
| |
| await tester.pumpWidget( |
| const MaterialApp( |
| title: title, |
| home: Scaffold( |
| body: Directionality(textDirection: textDirection, child: LicensePage()), |
| ), |
| ), |
| ); |
| |
| await tester.pumpAndSettle(); // Finish rendering the page. |
| |
| // If the layout width is greater than 840.0 pixels, lateral UI layout |
| // is used which positions license page title and packageList |
| // at the top left. |
| titleOffset = tester.getTopRight(find.text(title)); |
| expect(titleOffset, const Offset(292.0, 136.0)); |
| expect(titleOffset.dx, lessThan(wideSize.width - 320)); // Default master view width is 320.0. |
| expect(tester.getCenter(find.byType(ListView)), const Offset(160, 356)); |
| }); |
| |
| testWidgets('Material3 - LicensePage master view layout position - ltr', ( |
| WidgetTester tester, |
| ) async { |
| const TextDirection textDirection = TextDirection.ltr; |
| const defaultSize = Size(800.0, 600.0); |
| const wideSize = Size(1200.0, 600.0); |
| const title = 'License ABC'; |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['ABC'], 'DEF'), |
| ]); |
| }); |
| |
| addTearDown(() async { |
| await tester.binding.setSurfaceSize(null); |
| }); |
| |
| // Configure to show the default layout. |
| await tester.binding.setSurfaceSize(defaultSize); |
| |
| await tester.pumpWidget( |
| const MaterialApp( |
| title: title, |
| home: Scaffold( |
| body: Directionality(textDirection: textDirection, child: LicensePage()), |
| ), |
| ), |
| ); |
| |
| await tester.pumpAndSettle(); // Finish rendering the page. |
| |
| // If the layout width is less than 840.0 pixels, nested layout is |
| // used which positions license page title at the top center. |
| Offset titleOffset = tester.getCenter(find.text(title)); |
| expect(titleOffset, Offset(defaultSize.width / 2, 96.0)); |
| expect(tester.getCenter(find.byType(ListView)), Offset(defaultSize.width / 2, 328.0)); |
| |
| // Configure a wide window to show the lateral UI. |
| await tester.binding.setSurfaceSize(wideSize); |
| |
| await tester.pumpWidget( |
| const MaterialApp( |
| title: title, |
| home: Scaffold( |
| body: Directionality(textDirection: textDirection, child: LicensePage()), |
| ), |
| ), |
| ); |
| |
| await tester.pumpAndSettle(); // Finish rendering the page. |
| |
| // If the layout width is greater than 840.0 pixels, lateral UI layout |
| // is used which positions license page title and packageList |
| // at the top left. |
| titleOffset = tester.getTopRight(find.text(title)); |
| expect(titleOffset, const Offset(292.0, 136.0)); |
| expect(titleOffset.dx, lessThan(wideSize.width - 320)); // Default master view width is 320.0. |
| expect(tester.getCenter(find.byType(ListView)), const Offset(160, 356)); |
| }); |
| |
| testWidgets('Material2 - LicensePage master view layout position - rtl', ( |
| WidgetTester tester, |
| ) async { |
| const TextDirection textDirection = TextDirection.rtl; |
| const defaultSize = Size(800.0, 600.0); |
| const wideSize = Size(1200.0, 600.0); |
| const title = 'License ABC'; |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['ABC'], 'DEF'), |
| ]); |
| }); |
| |
| addTearDown(() async { |
| await tester.binding.setSurfaceSize(null); |
| }); |
| |
| // Configure to show the default layout. |
| await tester.binding.setSurfaceSize(defaultSize); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(useMaterial3: false), |
| title: title, |
| home: const Scaffold( |
| body: Directionality(textDirection: textDirection, child: LicensePage()), |
| ), |
| ), |
| ); |
| |
| await tester.pumpAndSettle(); // Finish rendering the page. |
| |
| // If the layout width is less than 840.0 pixels, nested layout is |
| // used which positions license page title at the top center. |
| Offset titleOffset = tester.getCenter(find.text(title)); |
| expect(titleOffset, Offset(defaultSize.width / 2, 92.0)); |
| expect(tester.getCenter(find.byType(ListView)), Offset(defaultSize.width / 2, 328.0)); |
| |
| // Configure a wide window to show the lateral UI. |
| await tester.binding.setSurfaceSize(wideSize); |
| |
| await tester.pumpWidget( |
| const MaterialApp( |
| title: title, |
| home: Scaffold( |
| body: Directionality(textDirection: textDirection, child: LicensePage()), |
| ), |
| ), |
| ); |
| |
| await tester.pumpAndSettle(); // Finish rendering the page. |
| |
| // If the layout width is greater than 840.0 pixels, lateral UI layout |
| // is used which positions license page title and packageList |
| // at the top right. |
| titleOffset = tester.getTopLeft(find.text(title)); |
| expect(titleOffset, const Offset(908.0, 136.0)); |
| expect( |
| titleOffset.dx, |
| greaterThan(wideSize.width - 320), |
| ); // Default master view width is 320.0. |
| expect(tester.getCenter(find.byType(ListView)), const Offset(1040.0, 356.0)); |
| }); |
| |
| testWidgets('Material3 - LicensePage master view layout position - rtl', ( |
| WidgetTester tester, |
| ) async { |
| const TextDirection textDirection = TextDirection.rtl; |
| const defaultSize = Size(800.0, 600.0); |
| const wideSize = Size(1200.0, 600.0); |
| const title = 'License ABC'; |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['ABC'], 'DEF'), |
| ]); |
| }); |
| |
| addTearDown(() async { |
| await tester.binding.setSurfaceSize(null); |
| }); |
| |
| // Configure to show the default layout. |
| await tester.binding.setSurfaceSize(defaultSize); |
| |
| await tester.pumpWidget( |
| const MaterialApp( |
| title: title, |
| home: Scaffold( |
| body: Directionality(textDirection: textDirection, child: LicensePage()), |
| ), |
| ), |
| ); |
| |
| await tester.pumpAndSettle(); // Finish rendering the page. |
| |
| // If the layout width is less than 840.0 pixels, nested layout is |
| // used which positions license page title at the top center. |
| Offset titleOffset = tester.getCenter(find.text(title)); |
| expect(titleOffset, Offset(defaultSize.width / 2, 96.0)); |
| expect(tester.getCenter(find.byType(ListView)), Offset(defaultSize.width / 2, 328.0)); |
| |
| // Configure a wide window to show the lateral UI. |
| await tester.binding.setSurfaceSize(wideSize); |
| |
| await tester.pumpWidget( |
| const MaterialApp( |
| title: title, |
| home: Scaffold( |
| body: Directionality(textDirection: textDirection, child: LicensePage()), |
| ), |
| ), |
| ); |
| |
| await tester.pumpAndSettle(); // Finish rendering the page. |
| |
| // If the layout width is greater than 840.0 pixels, lateral UI layout |
| // is used which positions license page title and packageList |
| // at the top right. |
| titleOffset = tester.getTopLeft(find.text(title)); |
| expect(titleOffset, const Offset(908.0, 136.0)); |
| expect( |
| titleOffset.dx, |
| greaterThan(wideSize.width - 320), |
| ); // Default master view width is 320.0. |
| expect(tester.getCenter(find.byType(ListView)), const Offset(1040.0, 356.0)); |
| }); |
| |
| testWidgets('License page title in lateral UI does not use AppBarTheme.foregroundColor', ( |
| WidgetTester tester, |
| ) async { |
| // This is a regression test for https://github.com/flutter/flutter/issues/108991 |
| final theme = ThemeData(appBarTheme: const AppBarTheme(foregroundColor: Color(0xFFFFFFFF))); |
| const title = 'License ABC'; |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['ABC'], 'DEF'), |
| ]); |
| }); |
| |
| addTearDown(() async { |
| await tester.binding.setSurfaceSize(null); |
| }); |
| |
| // Configure a wide window to show the lateral UI. |
| await tester.binding.setSurfaceSize(const Size(1200.0, 600.0)); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| title: title, |
| theme: theme, |
| home: const Scaffold(body: LicensePage()), |
| ), |
| ); |
| |
| await tester.pumpAndSettle(); // Finish rendering the page. |
| |
| final renderParagraph = tester.renderObject(find.text('ABC').last) as RenderParagraph; |
| |
| // License page title should not use AppBarTheme's foregroundColor. |
| expect(renderParagraph.text.style!.color, isNot(theme.appBarTheme.foregroundColor)); |
| |
| // License page title in the lateral UI uses default text style color. |
| expect(renderParagraph.text.style!.color, theme.textTheme.titleLarge!.color); |
| }); |
| |
| testWidgets('License page default title text color in the nested UI', ( |
| WidgetTester tester, |
| ) async { |
| // This is a regression test for https://github.com/flutter/flutter/issues/108991 |
| final theme = ThemeData(); |
| const title = 'License ABC'; |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['ABC'], 'DEF'), |
| ]); |
| }); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| title: title, |
| theme: theme, |
| home: const Scaffold(body: LicensePage()), |
| ), |
| ); |
| |
| await tester.pumpAndSettle(); // Finish rendering the page. |
| |
| // Currently in the master view. |
| expect(find.text('License ABC'), findsOneWidget); |
| |
| // Navigate to the license page. |
| await tester.tap(find.text('ABC')); |
| await tester.pumpAndSettle(); |
| |
| // Master view is no longer visible. |
| expect(find.text('License ABC'), findsNothing); |
| |
| final renderParagraph = tester.renderObject(find.text('ABC').first) as RenderParagraph; |
| expect(renderParagraph.text.style!.color, theme.textTheme.titleLarge!.color); |
| }); |
| |
| group('Material 2', () { |
| // These tests are only relevant for Material 2. Once Material 2 |
| // support is deprecated and the APIs are removed, these tests |
| // can be deleted. |
| |
| testWidgets('License page default title text color in the nested UI', ( |
| WidgetTester tester, |
| ) async { |
| // This is a regression test for https://github.com/flutter/flutter/issues/108991 |
| final theme = ThemeData(useMaterial3: false); |
| const title = 'License ABC'; |
| LicenseRegistry.addLicense(() { |
| return Stream<LicenseEntry>.fromIterable(<LicenseEntry>[ |
| const LicenseEntryWithLineBreaks(<String>['ABC'], 'DEF'), |
| ]); |
| }); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| title: title, |
| theme: theme, |
| home: const Scaffold(body: LicensePage()), |
| ), |
| ); |
| |
| await tester.pumpAndSettle(); // Finish rendering the page. |
| |
| // Currently in the master view. |
| expect(find.text('License ABC'), findsOneWidget); |
| |
| // Navigate to the license page. |
| await tester.tap(find.text('ABC')); |
| await tester.pumpAndSettle(); |
| |
| // Master view is no longer visible. |
| expect(find.text('License ABC'), findsNothing); |
| |
| final renderParagraph = tester.renderObject(find.text('ABC').first) as RenderParagraph; |
| expect(renderParagraph.text.style!.color, theme.primaryTextTheme.titleLarge!.color); |
| }); |
| }); |
| |
| testWidgets('Adaptive AboutDialog shows correct widget on each platform', ( |
| WidgetTester tester, |
| ) async { |
| for (final platform in <TargetPlatform>[TargetPlatform.iOS, TargetPlatform.macOS]) { |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(platform: platform), |
| home: const Material( |
| child: Center(child: ElevatedButton(onPressed: null, child: Text('Go'))), |
| ), |
| ), |
| ); |
| |
| final BuildContext context = tester.element(find.text('Go')); |
| |
| showAdaptiveAboutDialog( |
| context: context, |
| applicationIcon: const Icon(Icons.abc), |
| applicationName: 'Test', |
| applicationVersion: '1.0.0', |
| applicationLegalese: 'Application Legalese', |
| children: <Widget>[const Text('Test1')], |
| ); |
| |
| await tester.pumpAndSettle(const Duration(seconds: 1)); |
| expect(find.byType(CupertinoDialogAction), findsWidgets); |
| } |
| |
| for (final platform in <TargetPlatform>[ |
| TargetPlatform.android, |
| TargetPlatform.fuchsia, |
| TargetPlatform.linux, |
| TargetPlatform.windows, |
| ]) { |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(platform: platform), |
| home: const Material( |
| child: Center(child: ElevatedButton(onPressed: null, child: Text('Go'))), |
| ), |
| ), |
| ); |
| |
| final BuildContext context = tester.element(find.text('Go')); |
| |
| showAboutDialog( |
| context: context, |
| applicationIcon: const Icon(Icons.abc), |
| applicationName: 'Test', |
| applicationVersion: '1.0.0', |
| ); |
| |
| await tester.pumpAndSettle(const Duration(seconds: 1)); |
| expect(find.byType(CupertinoDialogAction), findsNothing); |
| } |
| }); |
| |
| testWidgets('Adaptive AboutDialog closes correctly on each platform', ( |
| WidgetTester tester, |
| ) async { |
| for (final platform in <TargetPlatform>[TargetPlatform.iOS, TargetPlatform.macOS]) { |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(platform: platform), |
| home: const Material( |
| child: Center(child: ElevatedButton(onPressed: null, child: Text('Go'))), |
| ), |
| ), |
| ); |
| |
| final BuildContext context = tester.element(find.text('Go')); |
| |
| showAdaptiveAboutDialog( |
| context: context, |
| applicationName: 'Test', |
| applicationVersion: '1.0.0', |
| ); |
| |
| await tester.pumpAndSettle(const Duration(seconds: 1)); |
| expect(find.byType(CupertinoDialogAction), findsWidgets); |
| |
| await tester.tap(find.text('Close')); |
| await tester.pumpAndSettle(); |
| expect(find.byType(CupertinoAlertDialog), findsNothing); |
| } |
| |
| for (final platform in <TargetPlatform>[ |
| TargetPlatform.android, |
| TargetPlatform.fuchsia, |
| TargetPlatform.linux, |
| TargetPlatform.windows, |
| ]) { |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(platform: platform), |
| home: const Material( |
| child: Center(child: ElevatedButton(onPressed: null, child: Text('Go'))), |
| ), |
| ), |
| ); |
| |
| final BuildContext context = tester.element(find.text('Go')); |
| |
| showAdaptiveAboutDialog( |
| context: context, |
| applicationName: 'Test', |
| applicationVersion: '1.0.0', |
| ); |
| |
| await tester.pumpAndSettle(const Duration(seconds: 1)); |
| expect(find.byType(TextButton), findsWidgets); |
| |
| await tester.tap(find.text('Close')); |
| await tester.pumpAndSettle(); |
| expect(find.byType(AlertDialog), findsNothing); |
| } |
| }); |
| |
| testWidgets('showLicensePage inherits ambient Theme', (WidgetTester tester) async { |
| final theme = ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: const Color(0XFFFF0000))); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Theme( |
| data: theme, |
| child: Builder( |
| builder: (BuildContext context) => ElevatedButton( |
| onPressed: () { |
| showAboutDialog( |
| context: context, |
| applicationName: 'Sample Test', |
| applicationVersion: 'v1.0.0', // Version of the app |
| ); |
| }, |
| child: const Text('Show About Dialog'), |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| // Open the dialog. |
| await tester.tap(find.byType(ElevatedButton)); |
| await tester.pumpAndSettle(); |
| |
| await tester.tap(find.text('View licenses')); |
| await tester.pumpAndSettle(); |
| |
| final ThemeData licensePageTheme = Theme.of(tester.element(find.text('Powered by Flutter'))); |
| expect(theme.colorScheme.primary, licensePageTheme.colorScheme.primary); |
| }); |
| |
| testWidgets('AboutDialog renders at zero area', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| const MaterialApp( |
| home: Center( |
| child: SizedBox.shrink( |
| child: Scaffold(body: AboutDialog(children: <Widget>[Text('X')])), |
| ), |
| ), |
| ), |
| ); |
| final Finder xText = find.text('X'); |
| expect(tester.getSize(xText).isEmpty, isTrue); |
| }); |
| |
| testWidgets('AboutListTile renders at zero area', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| const MaterialApp( |
| home: Center( |
| child: SizedBox.shrink( |
| child: Scaffold(body: AboutListTile(child: Text('X'))), |
| ), |
| ), |
| ), |
| ); |
| final Finder xText = find.text('X'); |
| expect(tester.getSize(xText).isEmpty, isTrue); |
| }); |
| |
| testWidgets('LicensePage renders at zero area', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| const MaterialApp( |
| home: Center( |
| child: SizedBox.shrink( |
| child: Scaffold(body: LicensePage(applicationName: 'X')), |
| ), |
| ), |
| ), |
| ); |
| final Finder xText = find.text('X'); |
| expect(tester.getSize(xText).isEmpty, isTrue); |
| }); |
| } |
| |
| class FakeLicenseEntry extends LicenseEntry { |
| FakeLicenseEntry(); |
| |
| bool get packagesCalled => _packagesCalled; |
| bool _packagesCalled = false; |
| |
| @override |
| Iterable<LicenseParagraph> paragraphs = <LicenseParagraph>[]; |
| |
| @override |
| Iterable<String> get packages { |
| _packagesCalled = true; |
| return <String>[]; |
| } |
| } |
| |
| class LicensePageObserver extends NavigatorObserver { |
| int licensePageCount = 0; |
| |
| @override |
| void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) { |
| if (route is MaterialPageRoute<dynamic>) { |
| licensePageCount++; |
| } |
| super.didPush(route, previousRoute); |
| } |
| } |
| |
| class AboutDialogObserver extends NavigatorObserver { |
| int dialogCount = 0; |
| |
| @override |
| void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) { |
| if (route is DialogRoute) { |
| dialogCount++; |
| } |
| super.didPush(route, previousRoute); |
| } |
| |
| @override |
| void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) { |
| if (route is DialogRoute) { |
| dialogCount--; |
| } |
| super.didPop(route, previousRoute); |
| } |
| } |