| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:flutter/cupertino.dart'; |
| import 'package:flutter/rendering.dart'; |
| import 'package:flutter_test/flutter_test.dart' hide TypeMatcher; |
| |
| int count = 0; |
| |
| void main() { |
| testWidgets('Middle still in center with asymmetrical actions', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| new WidgetsApp( |
| color: const Color(0xFFFFFFFF), |
| onGenerateRoute: (RouteSettings settings) { |
| return new CupertinoPageRoute<void>( |
| settings: settings, |
| builder: (BuildContext context) { |
| return const CupertinoNavigationBar( |
| leading: const CupertinoButton(child: const Text('Something'), onPressed: null,), |
| middle: const Text('Title'), |
| ); |
| }, |
| ); |
| }, |
| ), |
| ); |
| |
| // Expect the middle of the title to be exactly in the middle of the screen. |
| expect(tester.getCenter(find.text('Title')).dx, 400.0); |
| }); |
| |
| testWidgets('Opaque background does not add blur effects', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| new WidgetsApp( |
| color: const Color(0xFFFFFFFF), |
| onGenerateRoute: (RouteSettings settings) { |
| return new CupertinoPageRoute<void>( |
| settings: settings, |
| builder: (BuildContext context) { |
| return const CupertinoNavigationBar( |
| middle: const Text('Title'), |
| backgroundColor: const Color(0xFFE5E5E5), |
| ); |
| }, |
| ); |
| }, |
| ), |
| ); |
| expect(find.byType(BackdropFilter), findsNothing); |
| }); |
| |
| testWidgets('Non-opaque background adds blur effects', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| new WidgetsApp( |
| color: const Color(0xFFFFFFFF), |
| onGenerateRoute: (RouteSettings settings) { |
| return new CupertinoPageRoute<void>( |
| settings: settings, |
| builder: (BuildContext context) { |
| return const CupertinoNavigationBar( |
| middle: const Text('Title'), |
| ); |
| }, |
| ); |
| }, |
| ), |
| ); |
| expect(find.byType(BackdropFilter), findsOneWidget); |
| }); |
| |
| testWidgets('Verify styles of each slot', (WidgetTester tester) async { |
| count = 0x000000; |
| await tester.pumpWidget( |
| new WidgetsApp( |
| color: const Color(0xFFFFFFFF), |
| onGenerateRoute: (RouteSettings settings) { |
| return new CupertinoPageRoute<void>( |
| settings: settings, |
| builder: (BuildContext context) { |
| return const CupertinoNavigationBar( |
| leading: const _ExpectStyles(color: const Color(0xFF001122), index: 0x000001), |
| middle: const _ExpectStyles(color: const Color(0xFF000000), letterSpacing: -0.72, index: 0x000100), |
| trailing: const _ExpectStyles(color: const Color(0xFF001122), index: 0x010000), |
| actionsForegroundColor: const Color(0xFF001122), |
| ); |
| }, |
| ); |
| }, |
| ), |
| ); |
| expect(count, 0x010101); |
| }); |
| |
| testWidgets('No slivers with no large titles', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| new WidgetsApp( |
| color: const Color(0xFFFFFFFF), |
| onGenerateRoute: (RouteSettings settings) { |
| return new CupertinoPageRoute<void>( |
| settings: settings, |
| builder: (BuildContext context) { |
| return const CupertinoPageScaffold( |
| navigationBar: const CupertinoNavigationBar( |
| middle: const Text('Title'), |
| ), |
| child: const Center(), |
| ); |
| }, |
| ); |
| }, |
| ), |
| ); |
| |
| expect(find.byType(SliverPersistentHeader), findsNothing); |
| }); |
| |
| testWidgets('Media padding is applied to CupertinoSliverNavigationBar', (WidgetTester tester) async { |
| final ScrollController scrollController = new ScrollController(); |
| final Key leadingKey = new GlobalKey(); |
| final Key middleKey = new GlobalKey(); |
| final Key trailingKey = new GlobalKey(); |
| final Key titleKey = new GlobalKey(); |
| await tester.pumpWidget( |
| new WidgetsApp( |
| color: const Color(0xFFFFFFFF), |
| onGenerateRoute: (RouteSettings settings) { |
| return new CupertinoPageRoute<void>( |
| settings: settings, |
| builder: (BuildContext context) { |
| return new MediaQuery( |
| data: const MediaQueryData( |
| padding: const EdgeInsets.only( |
| top: 10.0, |
| left: 20.0, |
| bottom: 30.0, |
| right: 40.0, |
| ), |
| ), |
| child: new CupertinoPageScaffold( |
| child: new CustomScrollView( |
| controller: scrollController, |
| slivers: <Widget>[ |
| new CupertinoSliverNavigationBar( |
| leading: new Placeholder(key: leadingKey), |
| middle: new Placeholder(key: middleKey), |
| largeTitle: new Text('Large Title', key: titleKey), |
| trailing: new Placeholder(key: trailingKey), |
| ), |
| new SliverToBoxAdapter( |
| child: new Container( |
| height: 1200.0, |
| ), |
| ), |
| ], |
| ), |
| ), |
| ); |
| }, |
| ); |
| }, |
| ), |
| ); |
| |
| // Media padding applied to leading (T,L), middle (T), trailing (T, R). |
| expect(tester.getTopLeft(find.byKey(leadingKey)), const Offset(16.0 + 20.0, 10.0)); |
| expect(tester.getRect(find.byKey(middleKey)).top, 10.0); |
| expect(tester.getTopRight(find.byKey(trailingKey)), const Offset(800.0 - 16.0 - 40.0, 10.0)); |
| |
| // Top and left padding is applied to large title. |
| expect(tester.getTopLeft(find.byKey(titleKey)), const Offset(16.0 + 20.0, 58.0 + 10.0)); |
| }); |
| |
| testWidgets('Large title nav bar scrolls', (WidgetTester tester) async { |
| final ScrollController scrollController = new ScrollController(); |
| await tester.pumpWidget( |
| new WidgetsApp( |
| color: const Color(0xFFFFFFFF), |
| onGenerateRoute: (RouteSettings settings) { |
| return new CupertinoPageRoute<void>( |
| settings: settings, |
| builder: (BuildContext context) { |
| return new CupertinoPageScaffold( |
| child: new CustomScrollView( |
| controller: scrollController, |
| slivers: <Widget>[ |
| const CupertinoSliverNavigationBar( |
| largeTitle: const Text('Title'), |
| ), |
| new SliverToBoxAdapter( |
| child: new Container( |
| height: 1200.0, |
| ), |
| ), |
| ], |
| ), |
| ); |
| }, |
| ); |
| }, |
| ), |
| ); |
| |
| expect(scrollController.offset, 0.0); |
| expect(tester.getTopLeft(find.byType(NavigationToolbar)).dy, 0.0); |
| expect(tester.getSize(find.byType(NavigationToolbar)).height, 44.0); |
| |
| expect(find.text('Title'), findsNWidgets(2)); // Though only one is visible. |
| |
| List<Element> titles = tester.elementList(find.text('Title')) |
| .toList() |
| ..sort((Element a, Element b) { |
| final RenderParagraph aParagraph = a.renderObject; |
| final RenderParagraph bParagraph = b.renderObject; |
| return aParagraph.text.style.fontSize.compareTo(bParagraph.text.style.fontSize); |
| }); |
| |
| Iterable<double> opacities = titles.map((Element element) { |
| final RenderOpacity renderOpacity = element.ancestorRenderObjectOfType(const TypeMatcher<RenderOpacity>()); |
| return renderOpacity.opacity; |
| }); |
| |
| expect(opacities, <double> [ |
| 0.0, // Initially the smaller font title is invisible. |
| 1.0, // The larger font title is visible. |
| ]); |
| |
| expect(tester.getTopLeft(find.widgetWithText(OverflowBox, 'Title')).dy, 44.0); |
| expect(tester.getSize(find.widgetWithText(OverflowBox, 'Title')).height, 56.0); |
| |
| scrollController.jumpTo(600.0); |
| await tester.pump(); // Once to trigger the opacity animation. |
| await tester.pump(const Duration(milliseconds: 300)); |
| |
| titles = tester.elementList(find.text('Title')) |
| .toList() |
| ..sort((Element a, Element b) { |
| final RenderParagraph aParagraph = a.renderObject; |
| final RenderParagraph bParagraph = b.renderObject; |
| return aParagraph.text.style.fontSize.compareTo(bParagraph.text.style.fontSize); |
| }); |
| |
| opacities = titles.map((Element element) { |
| final RenderOpacity renderOpacity = element.ancestorRenderObjectOfType(const TypeMatcher<RenderOpacity>()); |
| return renderOpacity.opacity; |
| }); |
| |
| expect(opacities, <double> [ |
| 1.0, // Smaller font title now visible |
| 0.0, // Larger font title invisible. |
| ]); |
| |
| // The persistent toolbar doesn't move or change size. |
| expect(tester.getTopLeft(find.byType(NavigationToolbar)).dy, 0.0); |
| expect(tester.getSize(find.byType(NavigationToolbar)).height, 44.0); |
| |
| expect(tester.getTopLeft(find.widgetWithText(OverflowBox, 'Title')).dy, 44.0); |
| // The OverflowBox is squished with the text in it. |
| expect(tester.getSize(find.widgetWithText(OverflowBox, 'Title')).height, 0.0); |
| }); |
| |
| testWidgets('Small title can be overridden', (WidgetTester tester) async { |
| final ScrollController scrollController = new ScrollController(); |
| await tester.pumpWidget( |
| new WidgetsApp( |
| color: const Color(0xFFFFFFFF), |
| onGenerateRoute: (RouteSettings settings) { |
| return new CupertinoPageRoute<void>( |
| settings: settings, |
| builder: (BuildContext context) { |
| return new CupertinoPageScaffold( |
| child: new CustomScrollView( |
| controller: scrollController, |
| slivers: <Widget>[ |
| const CupertinoSliverNavigationBar( |
| middle: const Text('Different title'), |
| largeTitle: const Text('Title'), |
| ), |
| new SliverToBoxAdapter( |
| child: new Container( |
| height: 1200.0, |
| ), |
| ), |
| ], |
| ), |
| ); |
| }, |
| ); |
| }, |
| ), |
| ); |
| |
| expect(scrollController.offset, 0.0); |
| expect(tester.getTopLeft(find.byType(NavigationToolbar)).dy, 0.0); |
| expect(tester.getSize(find.byType(NavigationToolbar)).height, 44.0); |
| |
| expect(find.text('Title'), findsOneWidget); |
| expect(find.text('Different title'), findsOneWidget); |
| |
| RenderOpacity largeTitleOpacity = |
| tester.element(find.text('Title')).ancestorRenderObjectOfType(const TypeMatcher<RenderOpacity>()); |
| // Large title initially visible. |
| expect( |
| largeTitleOpacity.opacity, |
| 1.0 |
| ); |
| // Middle widget not even wrapped with RenderOpacity, i.e. is always visible. |
| expect( |
| tester.element(find.text('Different title')).ancestorRenderObjectOfType(const TypeMatcher<RenderOpacity>()), |
| isNull, |
| ); |
| |
| expect(tester.getBottomLeft(find.text('Title')).dy, 44.0 + 56.0 - 8.0); // Static part + extension - padding. |
| |
| scrollController.jumpTo(600.0); |
| await tester.pump(); // Once to trigger the opacity animation. |
| await tester.pump(const Duration(milliseconds: 300)); |
| |
| largeTitleOpacity = |
| tester.element(find.text('Title')).ancestorRenderObjectOfType(const TypeMatcher<RenderOpacity>()); |
| // Large title no longer visible. |
| expect( |
| largeTitleOpacity.opacity, |
| 0.0 |
| ); |
| |
| // The persistent toolbar doesn't move or change size. |
| expect(tester.getTopLeft(find.byType(NavigationToolbar)).dy, 0.0); |
| expect(tester.getSize(find.byType(NavigationToolbar)).height, 44.0); |
| |
| expect(tester.getBottomLeft(find.text('Title')).dy, 44.0 - 8.0); // Extension gone, (static part - padding) left. |
| }); |
| |
| testWidgets('Auto back/close button', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| new WidgetsApp( |
| color: const Color(0xFFFFFFFF), |
| onGenerateRoute: (RouteSettings settings) { |
| return new CupertinoPageRoute<void>( |
| settings: settings, |
| builder: (BuildContext context) { |
| return const CupertinoNavigationBar( |
| middle: const Text('Home page'), |
| ); |
| }, |
| ); |
| }, |
| ), |
| ); |
| |
| expect(find.byType(CupertinoButton), findsNothing); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).push(new CupertinoPageRoute<void>( |
| builder: (BuildContext context) { |
| return const CupertinoNavigationBar( |
| middle: const Text('Page 2'), |
| ); |
| }, |
| )); |
| |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 200)); |
| |
| expect(find.byType(CupertinoButton), findsOneWidget); |
| expect(find.byType(Icon), findsOneWidget); |
| |
| tester.state<NavigatorState>(find.byType(Navigator)).push(new CupertinoPageRoute<void>( |
| fullscreenDialog: true, |
| builder: (BuildContext context) { |
| return const CupertinoNavigationBar( |
| middle: const Text('Dialog page'), |
| ); |
| }, |
| )); |
| |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 200)); |
| |
| expect(find.byType(CupertinoButton), findsNWidgets(2)); |
| expect(find.text('Close'), findsOneWidget); |
| |
| // Test popping goes back correctly. |
| await tester.tap(find.text('Close')); |
| |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 200)); |
| |
| expect(find.text('Page 2'), findsOneWidget); |
| |
| await tester.tap(find.byType(Icon)); |
| |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 200)); |
| |
| expect(find.text('Home page'), findsOneWidget); |
| }); |
| |
| testWidgets('Border should be displayed by default', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| new WidgetsApp( |
| color: const Color(0xFFFFFFFF), |
| onGenerateRoute: (RouteSettings settings) { |
| return new CupertinoPageRoute<void>( |
| settings: settings, |
| builder: (BuildContext context) { |
| return const CupertinoNavigationBar( |
| middle: const Text('Title'), |
| ); |
| }, |
| ); |
| }, |
| ), |
| ); |
| |
| final DecoratedBox decoratedBox = tester.widgetList(find.byType(DecoratedBox)).elementAt(1); |
| expect(decoratedBox.decoration.runtimeType, BoxDecoration); |
| |
| final BoxDecoration decoration = decoratedBox.decoration; |
| expect(decoration.border, isNotNull); |
| |
| final BorderSide side = decoration.border.bottom; |
| expect(side, isNotNull); |
| }); |
| |
| testWidgets('Overrides border color', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| new WidgetsApp( |
| color: const Color(0xFFFFFFFF), |
| onGenerateRoute: (RouteSettings settings) { |
| return new CupertinoPageRoute<void>( |
| settings: settings, |
| builder: (BuildContext context) { |
| return const CupertinoNavigationBar( |
| middle: const Text('Title'), |
| border: const Border( |
| bottom: const BorderSide( |
| color: const Color(0xFFAABBCC), |
| width: 0.0, |
| ), |
| ), |
| ); |
| }, |
| ); |
| }, |
| ), |
| ); |
| |
| final DecoratedBox decoratedBox = tester.widgetList(find.byType(DecoratedBox)).elementAt(1); |
| expect(decoratedBox.decoration.runtimeType, BoxDecoration); |
| |
| final BoxDecoration decoration = decoratedBox.decoration; |
| expect(decoration.border, isNotNull); |
| |
| final BorderSide side = decoration.border.bottom; |
| expect(side, isNotNull); |
| expect(side.color, const Color(0xFFAABBCC)); |
| }); |
| |
| testWidgets('Border should not be displayed when null', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| new WidgetsApp( |
| color: const Color(0xFFFFFFFF), |
| onGenerateRoute: (RouteSettings settings) { |
| return new CupertinoPageRoute<void>( |
| settings: settings, |
| builder: (BuildContext context) { |
| return const CupertinoNavigationBar( |
| middle: const Text('Title'), |
| border: null, |
| ); |
| }, |
| ); |
| }, |
| ), |
| ); |
| |
| final DecoratedBox decoratedBox = tester.widgetList(find.byType(DecoratedBox)).elementAt(1); |
| expect(decoratedBox.decoration.runtimeType, BoxDecoration); |
| |
| final BoxDecoration decoration = decoratedBox.decoration; |
| expect(decoration.border, isNull); |
| }); |
| } |
| |
| class _ExpectStyles extends StatelessWidget { |
| const _ExpectStyles({ this.color, this.letterSpacing, this.index }); |
| |
| final Color color; |
| final double letterSpacing; |
| final int index; |
| |
| @override |
| Widget build(BuildContext context) { |
| final TextStyle style = DefaultTextStyle.of(context).style; |
| expect(style.color, color); |
| expect(style.fontSize, 17.0); |
| expect(style.letterSpacing, letterSpacing ?? -0.24); |
| count += index; |
| return new Container(); |
| } |
| } |