blob: 54f82201be89d502ff309b09da0b484ee85308c0 [file] [log] [blame] [edit]
// 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);
}
}