Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
c418b2f3
Unverified
Commit
c418b2f3
authored
Aug 24, 2018
by
xster
Committed by
GitHub
Aug 24, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Auto populate nav bar title and previous from page route (#19637)
parent
ce8ba6e8
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
780 additions
and
234 deletions
+780
-234
cupertino_navigation_demo.dart
...gallery/lib/demo/cupertino/cupertino_navigation_demo.dart
+50
-46
cupertino_refresh_demo.dart
...er_gallery/lib/demo/cupertino/cupertino_refresh_demo.dart
+1
-0
nav_bar.dart
packages/flutter/lib/src/cupertino/nav_bar.dart
+423
-173
route.dart
packages/flutter/lib/src/cupertino/route.dart
+46
-1
tab_view.dart
packages/flutter/lib/src/cupertino/tab_view.dart
+9
-1
navigator.dart
packages/flutter/lib/src/widgets/navigator.dart
+15
-10
nav_bar_test.dart
packages/flutter/test/cupertino/nav_bar_test.dart
+45
-2
route_test.dart
packages/flutter/test/cupertino/route_test.dart
+190
-0
widget_tester.dart
packages/flutter_test/lib/src/widget_tester.dart
+1
-1
No files found.
examples/flutter_gallery/lib/demo/cupertino/cupertino_navigation_demo.dart
View file @
c418b2f3
...
...
@@ -47,51 +47,58 @@ class CupertinoNavigationDemo extends StatelessWidget {
return
new
WillPopScope
(
// Prevent swipe popping of this page. Use explicit exit buttons only.
onWillPop:
()
=>
new
Future
<
bool
>.
value
(
true
),
child:
new
CupertinoTabScaffold
(
tabBar:
new
CupertinoTabBar
(
items:
const
<
BottomNavigationBarItem
>[
BottomNavigationBarItem
(
icon:
Icon
(
CupertinoIcons
.
home
),
title:
Text
(
'Home'
),
),
BottomNavigationBarItem
(
icon:
Icon
(
CupertinoIcons
.
conversation_bubble
),
title:
Text
(
'Support'
),
),
BottomNavigationBarItem
(
icon:
Icon
(
CupertinoIcons
.
profile_circled
),
title:
Text
(
'Profile'
),
),
],
child:
new
DefaultTextStyle
(
style:
const
TextStyle
(
fontFamily:
'.SF UI Text'
,
fontSize:
17.0
,
color:
CupertinoColors
.
black
,
),
tabBuilder:
(
BuildContext
context
,
int
index
)
{
return
new
DefaultTextStyle
(
style:
const
TextStyle
(
fontFamily:
'.SF UI Text'
,
fontSize:
17.0
,
color:
CupertinoColors
.
black
,
),
child:
new
CupertinoTabView
(
builder:
(
BuildContext
context
)
{
switch
(
index
)
{
case
0
:
child:
new
CupertinoTabScaffold
(
tabBar:
new
CupertinoTabBar
(
items:
const
<
BottomNavigationBarItem
>[
BottomNavigationBarItem
(
icon:
Icon
(
CupertinoIcons
.
home
),
title:
Text
(
'Home'
),
),
BottomNavigationBarItem
(
icon:
Icon
(
CupertinoIcons
.
conversation_bubble
),
title:
Text
(
'Support'
),
),
BottomNavigationBarItem
(
icon:
Icon
(
CupertinoIcons
.
profile_circled
),
title:
Text
(
'Profile'
),
),
],
),
tabBuilder:
(
BuildContext
context
,
int
index
)
{
switch
(
index
)
{
case
0
:
return
new
CupertinoTabView
(
builder:
(
BuildContext
context
)
{
return
new
CupertinoDemoTab1
(
colorItems:
colorItems
,
colorNameItems:
colorNameItems
);
break
;
case
1
:
return
new
CupertinoDemoTab2
();
break
;
case
2
:
return
new
CupertinoDemoTab3
();
break
;
default
:
}
},
),
);
},
},
defaultTitle:
'Colors'
,
);
break
;
case
1
:
return
new
CupertinoTabView
(
builder:
(
BuildContext
context
)
=>
CupertinoDemoTab2
(),
defaultTitle:
'Support Chat'
,
);
break
;
case
2
:
return
new
CupertinoTabView
(
builder:
(
BuildContext
context
)
=>
CupertinoDemoTab3
(),
defaultTitle:
'Account'
,
);
break
;
default
:
}
},
),
),
);
}
...
...
@@ -129,7 +136,6 @@ class CupertinoDemoTab1 extends StatelessWidget {
child:
new
CustomScrollView
(
slivers:
<
Widget
>[
const
CupertinoSliverNavigationBar
(
largeTitle:
Text
(
'Colors'
),
trailing:
ExitButton
(),
),
new
SliverPadding
(
...
...
@@ -174,6 +180,7 @@ class Tab1RowItem extends StatelessWidget {
behavior:
HitTestBehavior
.
opaque
,
onTap:
()
{
Navigator
.
of
(
context
).
push
(
new
CupertinoPageRoute
<
void
>(
title:
colorName
,
builder:
(
BuildContext
context
)
=>
new
Tab1ItemPage
(
color:
color
,
colorName:
colorName
,
...
...
@@ -285,9 +292,8 @@ class Tab1ItemPageState extends State<Tab1ItemPage> {
@override
Widget
build
(
BuildContext
context
)
{
return
new
CupertinoPageScaffold
(
navigationBar:
new
CupertinoNavigationBar
(
middle:
new
Text
(
widget
.
colorName
),
trailing:
const
ExitButton
(),
navigationBar:
const
CupertinoNavigationBar
(
trailing:
ExitButton
(),
),
child:
new
SafeArea
(
top:
false
,
...
...
@@ -415,7 +421,6 @@ class CupertinoDemoTab2 extends StatelessWidget {
Widget
build
(
BuildContext
context
)
{
return
new
CupertinoPageScaffold
(
navigationBar:
const
CupertinoNavigationBar
(
middle:
Text
(
'Support Chat'
),
trailing:
ExitButton
(),
),
child:
new
ListView
(
...
...
@@ -699,7 +704,6 @@ class CupertinoDemoTab3 extends StatelessWidget {
Widget
build
(
BuildContext
context
)
{
return
new
CupertinoPageScaffold
(
navigationBar:
const
CupertinoNavigationBar
(
middle:
Text
(
'Account'
),
trailing:
ExitButton
(),
),
child:
new
DecoratedBox
(
...
...
examples/flutter_gallery/lib/demo/cupertino/cupertino_refresh_demo.dart
View file @
c418b2f3
...
...
@@ -50,6 +50,7 @@ class _CupertinoRefreshControlDemoState extends State<CupertinoRefreshControlDem
slivers:
<
Widget
>[
const
CupertinoSliverNavigationBar
(
largeTitle:
Text
(
'Cupertino Refresh'
),
previousPageTitle:
'Cupertino'
,
),
new
CupertinoSliverRefreshControl
(
onRefresh:
()
{
...
...
packages/flutter/lib/src/cupertino/nav_bar.dart
View file @
c418b2f3
...
...
@@ -12,6 +12,7 @@ import 'button.dart';
import
'colors.dart'
;
import
'icons.dart'
;
import
'page_scaffold.dart'
;
import
'route.dart'
;
/// Standard iOS navigation bar height without the status bar.
const
double
_kNavBarPersistentHeight
=
44.0
;
...
...
@@ -62,6 +63,10 @@ const TextStyle _kLargeTitleTextStyle = TextStyle(
/// close button in case of a fullscreen dialog) to pop the current route if none
/// is provided and [automaticallyImplyLeading] is true (true by default).
///
/// The [middle] widget will automatically be a title text from the current
/// route if none is provided and [automaticallyImplyMiddle] is true (true by
/// default).
///
/// It should be placed at top of the screen and automatically accounts for
/// the OS's status bar.
///
...
...
@@ -80,6 +85,8 @@ class CupertinoNavigationBar extends StatelessWidget implements ObstructingPrefe
Key
key
,
this
.
leading
,
this
.
automaticallyImplyLeading
=
true
,
this
.
automaticallyImplyMiddle
=
true
,
this
.
previousPageTitle
,
this
.
middle
,
this
.
trailing
,
this
.
border
=
_kDefaultNavBarBorder
,
...
...
@@ -87,35 +94,83 @@ class CupertinoNavigationBar extends StatelessWidget implements ObstructingPrefe
this
.
padding
,
this
.
actionsForegroundColor
=
CupertinoColors
.
activeBlue
,
})
:
assert
(
automaticallyImplyLeading
!=
null
),
assert
(
automaticallyImplyMiddle
!=
null
),
super
(
key:
key
);
/// {@template flutter.cupertino.navBar.leading}
/// Widget to place at the start of the navigation bar. Normally a back button
/// for a normal page or a cancel button for full page dialogs.
///
/// If null and [automaticallyImplyLeading] is true, an appropriate button
/// will be automatically created.
/// {@endtemplate}
final
Widget
leading
;
/// {@template flutter.cupertino.navBar.automaticallyImplyLeading}
/// Controls whether we should try to imply the leading widget if null.
///
/// If true and [leading] is null, automatically try to deduce what the [leading]
/// widget should be. If [leading] widget is not null, this parameter has no effect.
///
/// Specifically this navigation bar will:
///
/// 1. Show a 'Close' button if the current route is a `fullscreenDialog`.
/// 2. Show a back chevron with [previousPageTitle] if [previousPageTitle] is
/// not null.
/// 3. Show a back chevron with the previous route's `title` if the current
/// route is a [CupertinoPageRoute] and the previous route is also a
/// [CupertinoPageRoute].
///
/// This value cannot be null.
/// {@endtemplate}
final
bool
automaticallyImplyLeading
;
/// Controls whether we should try to imply the middle widget if null.
///
/// If true and [middle] is null, automatically fill in a [Text] widget with
/// the current route's `title` if the route is a [CupertinoPageRoute].
/// If [middle] widget is not null, this parameter has no effect.
///
/// This value cannot be null.
final
bool
automaticallyImplyMiddle
;
/// {@template flutter.cupertino.navBar.previousPageTitle}
/// Manually specify the previous route's title when automatically implying
/// the leading back button.
///
/// Overrides the text shown with the back chevron instead of automatically
/// showing the previous [CupertinoPageRoute]'s `title` when
/// [automaticallyImplyLeading] is true.
///
/// Has no effect when [leading] is not null or if [automaticallyImplyLeading]
/// is false.
/// {@endtemplate}
final
String
previousPageTitle
;
/// Widget to place in the middle of the navigation bar. Normally a title or
/// a segmented control.
///
/// If null and [automaticallyImplyMiddle] is true, an appropriate [Text]
/// title will be created if the current route is a [CupertinoPageRoute] and
/// has a `title`.
final
Widget
middle
;
/// {@template flutter.cupertino.navBar.trailing}
/// Widget to place at the end of the navigation bar. Normally additional actions
/// taken on the page such as a search or edit function.
/// {@endtemplate}
final
Widget
trailing
;
// TODO(xster): implement support for double row navigation bars.
/// {@template flutter.cupertino.navBar.backgroundColor}
/// The background color of the navigation bar. If it contains transparency, the
/// tab bar will automatically produce a blurring effect to the content
/// behind it.
/// {@endtemplate}
final
Color
backgroundColor
;
/// {@template flutter.cupertino.navBar.padding}
/// Padding for the contents of the navigation bar.
///
/// If null, the navigation bar will adopt the following defaults:
...
...
@@ -127,11 +182,14 @@ class CupertinoNavigationBar extends StatelessWidget implements ObstructingPrefe
/// which case the padding will be 0.
///
/// Vertical padding won't change the height of the nav bar.
/// {@endtemplate}
final
EdgeInsetsDirectional
padding
;
/// {@template flutter.cupertino.navBar.border}
/// The border of the navigation bar. By default renders a single pixel bottom border side.
///
/// If a border is null, the navigation bar will not display a border.
/// {@endtemplate}
final
Border
border
;
/// Default color used for text and icons of the [leading] and [trailing]
...
...
@@ -152,13 +210,20 @@ class CupertinoNavigationBar extends StatelessWidget implements ObstructingPrefe
@override
Widget
build
(
BuildContext
context
)
{
final
Widget
effectiveMiddle
=
_effectiveTitle
(
title:
middle
,
automaticallyImplyTitle:
automaticallyImplyMiddle
,
currentRoute:
ModalRoute
.
of
(
context
),
);
return
_wrapWithBackground
(
border:
border
,
backgroundColor:
backgroundColor
,
child:
new
_CupertinoPersistentNavigationBar
(
leading:
leading
,
automaticallyImplyLeading:
automaticallyImplyLeading
,
middle:
new
Semantics
(
child:
middle
,
header:
true
),
previousPageTitle:
previousPageTitle
,
middle:
effectiveMiddle
,
trailing:
trailing
,
padding:
padding
,
actionsForegroundColor:
actionsForegroundColor
,
...
...
@@ -192,6 +257,10 @@ class CupertinoNavigationBar extends StatelessWidget implements ObstructingPrefe
/// close button in case of a fullscreen dialog) to pop the current route if none
/// is provided and [automaticallyImplyLeading] is true (true by default).
///
/// The [largeTitle] widget will automatically be a title text from the current
/// route if none is provided and [automaticallyImplyTitle] is true (true by
/// default).
///
/// See also:
///
/// * [CupertinoNavigationBar], an iOS navigation bar for use on non-scrolling
...
...
@@ -202,17 +271,19 @@ class CupertinoSliverNavigationBar extends StatelessWidget {
/// The [largeTitle] argument is required and must not be null.
const
CupertinoSliverNavigationBar
({
Key
key
,
@required
this
.
largeTitle
,
this
.
largeTitle
,
this
.
leading
,
this
.
automaticallyImplyLeading
=
true
,
this
.
automaticallyImplyTitle
=
true
,
this
.
previousPageTitle
,
this
.
middle
,
this
.
trailing
,
this
.
border
=
_kDefaultNavBarBorder
,
this
.
backgroundColor
=
_kDefaultNavBarBackgroundColor
,
this
.
padding
,
this
.
actionsForegroundColor
=
CupertinoColors
.
activeBlue
,
})
:
assert
(
largeTitle
!=
null
),
assert
(
automaticallyImply
Leading
!=
null
),
})
:
assert
(
automaticallyImplyLeading
!=
null
),
assert
(
automaticallyImply
Title
!=
null
),
super
(
key:
key
);
/// The navigation bar's title.
...
...
@@ -229,21 +300,31 @@ class CupertinoSliverNavigationBar extends StatelessWidget {
/// any [GlobalKey]s, and that it not rely on maintaining state (for example,
/// animations will not survive the transition from one location to the other,
/// and may in fact be visible in two places at once during the transition).
///
/// If null and [automaticallyImplyTitle] is true, an appropriate [Text]
/// title will be created if the current route is a [CupertinoPageRoute] and
/// has a `title`.
final
Widget
largeTitle
;
/// Widget to place at the start of the static navigation bar. Normally a back button
/// for a normal page or a cancel button for full page dialogs.
/// {@macro flutter.cupertino.navBar.leading}
///
/// This widget is visible in both collapsed and expanded states.
final
Widget
leading
;
/// Controls whether we should try to imply the leading widget if null.
/// {@macro flutter.cupertino.navBar.automaticallyImplyLeading}
final
bool
automaticallyImplyLeading
;
/// Controls whether we should try to imply the [largeTitle] widget if null.
///
/// If true and [leading] is null, automatically try to deduce what the [leading]
/// widget should be. If [leading] widget is not null, this parameter has no effect.
/// If true and [largeTitle] is null, automatically fill in a [Text] widget
/// with the current route's `title` if the route is a [CupertinoPageRoute].
/// If [largeTitle] widget is not null, this parameter has no effect.
///
/// This value cannot be null.
final
bool
automaticallyImplyLeading
;
final
bool
automaticallyImplyTitle
;
/// {@macro flutter.cupertino.navBar.previousPageTitle}
final
String
previousPageTitle
;
/// A widget to place in the middle of the static navigation bar instead of
/// the [largeTitle].
...
...
@@ -253,39 +334,24 @@ class CupertinoSliverNavigationBar extends StatelessWidget {
/// [middle] widget is provided.
final
Widget
middle
;
/// Widget to place at the end of the static navigation bar. Normally
/// additional actions taken on the page such as a search or edit function.
/// {@macro flutter.cupertino.navBar.trailing}
///
/// This widget is visible in both collapsed and expanded states.
final
Widget
trailing
;
/// Padding for the contents of the navigation bar.
///
/// If null, the navigation bar will adopt the following defaults:
///
/// * Vertically, contents will be sized to the same height as the navigation
/// bar itself minus the status bar.
/// * Horizontally, padding will be 16 pixels according to iOS specifications
/// unless the leading widget is an automatically inserted back button, in
/// which case the padding will be 0.
///
/// Vertical padding won't change the height of the nav bar.
/// {@macro flutter.cupertino.navBar.backgroundColor}
final
Color
backgroundColor
;
/// {@macro flutter.cupertino.navBar.padding}
final
EdgeInsetsDirectional
padding
;
/// The border of the navigation bar. By default renders a single pixel bottom border side.
///
/// If a border is null, the navigation bar will not display a border.
/// {@macro flutter.cupertino.navBar.border}
final
Border
border
;
/// The background color of the navigation bar. If it contains transparency, the
/// tab bar will automatically produce a blurring effect to the content
/// behind it.
final
Color
backgroundColor
;
/// Default color used for text and icons of the [leading] and [trailing]
/// widgets in the navigation bar.
///
/// The default color for text in the [
midd
le] slot is always black, as per
/// The default color for text in the [
largeTit
le] slot is always black, as per
/// iOS standard design.
final
Color
actionsForegroundColor
;
...
...
@@ -294,13 +360,20 @@ class CupertinoSliverNavigationBar extends StatelessWidget {
@override
Widget
build
(
BuildContext
context
)
{
final
Widget
effectiveTitle
=
_effectiveTitle
(
title:
largeTitle
,
automaticallyImplyTitle:
automaticallyImplyTitle
,
currentRoute:
ModalRoute
.
of
(
context
),
);
return
new
SliverPersistentHeader
(
pinned:
true
,
// iOS navigation bars are always pinned.
delegate:
new
_CupertinoLargeTitleNavigationBarSliverDelegate
(
persistentHeight:
_kNavBarPersistentHeight
+
MediaQuery
.
of
(
context
).
padding
.
top
,
title:
larg
eTitle
,
largeTitle:
effectiv
eTitle
,
leading:
leading
,
automaticallyImplyLeading:
automaticallyImplyLeading
,
previousPageTitle:
previousPageTitle
,
middle:
middle
,
trailing:
trailing
,
padding:
padding
,
...
...
@@ -312,6 +385,137 @@ class CupertinoSliverNavigationBar extends StatelessWidget {
}
}
class
_CupertinoLargeTitleNavigationBarSliverDelegate
extends
SliverPersistentHeaderDelegate
with
DiagnosticableTreeMixin
{
_CupertinoLargeTitleNavigationBarSliverDelegate
({
@required
this
.
persistentHeight
,
@required
this
.
largeTitle
,
this
.
leading
,
this
.
automaticallyImplyLeading
,
this
.
previousPageTitle
,
this
.
middle
,
this
.
trailing
,
this
.
padding
,
this
.
border
,
this
.
backgroundColor
,
this
.
actionsForegroundColor
,
})
:
assert
(
persistentHeight
!=
null
);
final
double
persistentHeight
;
final
Widget
largeTitle
;
final
Widget
leading
;
final
bool
automaticallyImplyLeading
;
final
String
previousPageTitle
;
final
Widget
middle
;
final
Widget
trailing
;
final
EdgeInsetsDirectional
padding
;
final
Color
backgroundColor
;
final
Border
border
;
final
Color
actionsForegroundColor
;
@override
double
get
minExtent
=>
persistentHeight
;
@override
double
get
maxExtent
=>
persistentHeight
+
_kNavBarLargeTitleHeightExtension
;
@override
Widget
build
(
BuildContext
context
,
double
shrinkOffset
,
bool
overlapsContent
)
{
final
bool
showLargeTitle
=
shrinkOffset
<
maxExtent
-
minExtent
-
_kNavBarShowLargeTitleThreshold
;
final
_CupertinoPersistentNavigationBar
persistentNavigationBar
=
new
_CupertinoPersistentNavigationBar
(
leading:
leading
,
automaticallyImplyLeading:
automaticallyImplyLeading
,
previousPageTitle:
previousPageTitle
,
middle:
middle
??
largeTitle
,
trailing:
trailing
,
// If middle widget exists, always show it. Otherwise, show title
// when collapsed.
middleVisible:
middle
!=
null
?
null
:
!
showLargeTitle
,
padding:
padding
,
actionsForegroundColor:
actionsForegroundColor
,
);
return
_wrapWithBackground
(
border:
border
,
backgroundColor:
backgroundColor
,
child:
new
Stack
(
fit:
StackFit
.
expand
,
children:
<
Widget
>[
new
Positioned
(
top:
persistentHeight
,
left:
0.0
,
right:
0.0
,
bottom:
0.0
,
child:
new
ClipRect
(
// The large title starts at the persistent bar.
// It's aligned with the bottom of the sliver and expands clipped
// and behind the persistent bar.
child:
new
OverflowBox
(
minHeight:
0.0
,
maxHeight:
double
.
infinity
,
alignment:
AlignmentDirectional
.
bottomStart
,
child:
new
Padding
(
padding:
const
EdgeInsetsDirectional
.
only
(
start:
_kNavBarEdgePadding
,
bottom:
8.0
,
// Bottom has a different padding.
),
child:
new
DefaultTextStyle
(
style:
_kLargeTitleTextStyle
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
child:
new
AnimatedOpacity
(
opacity:
showLargeTitle
?
1.0
:
0.0
,
duration:
_kNavBarTitleFadeDuration
,
child:
new
SafeArea
(
top:
false
,
bottom:
false
,
child:
new
Semantics
(
header:
true
,
child:
largeTitle
,
),
),
),
),
),
),
),
),
new
Positioned
(
left:
0.0
,
right:
0.0
,
top:
0.0
,
child:
persistentNavigationBar
,
),
],
),
);
}
@override
bool
shouldRebuild
(
_CupertinoLargeTitleNavigationBarSliverDelegate
oldDelegate
)
{
return
persistentHeight
!=
oldDelegate
.
persistentHeight
||
largeTitle
!=
oldDelegate
.
largeTitle
||
leading
!=
oldDelegate
.
leading
||
middle
!=
oldDelegate
.
middle
||
trailing
!=
oldDelegate
.
trailing
||
border
!=
oldDelegate
.
border
||
backgroundColor
!=
oldDelegate
.
backgroundColor
||
actionsForegroundColor
!=
oldDelegate
.
actionsForegroundColor
;
}
}
/// Returns `child` wrapped with background and a bottom border if background color
/// is opaque. Otherwise, also blur with [BackdropFilter].
Widget
_wrapWithBackground
(
{
...
...
@@ -347,6 +551,22 @@ Widget _wrapWithBackground({
);
}
Widget
_effectiveTitle
(
{
Widget
title
,
bool
automaticallyImplyTitle
,
ModalRoute
<
dynamic
>
currentRoute
,
})
{
// Auto use the CupertinoPageRoute's title if middle not provided.
if
(
title
==
null
&&
automaticallyImplyTitle
&&
currentRoute
is
CupertinoPageRoute
&&
currentRoute
.
title
!=
null
)
{
return
new
Text
(
currentRoute
.
title
);
}
return
title
;
}
/// The top part of the navigation bar that's never scrolled away.
///
/// Consists of the entire navigation bar without background and border when used
...
...
@@ -357,6 +577,7 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
Key
key
,
this
.
leading
,
this
.
automaticallyImplyLeading
,
this
.
previousPageTitle
,
this
.
middle
,
this
.
trailing
,
this
.
padding
,
...
...
@@ -368,6 +589,8 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
final
bool
automaticallyImplyLeading
;
final
String
previousPageTitle
;
final
Widget
middle
;
final
Widget
trailing
;
...
...
@@ -418,14 +641,16 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
// Let the middle be black rather than `actionsForegroundColor` in case
// it's a plain text title.
final
Widget
styledMiddle
=
middle
==
null
?
null
:
new
DefaultTextStyle
(
style:
actionsStyle
.
copyWith
(
fontWeight:
FontWeight
.
w600
,
letterSpacing:
-
0.08
,
color:
CupertinoColors
.
black
,
),
child:
middle
,
);
final
Widget
styledMiddle
=
middle
==
null
?
null
:
new
DefaultTextStyle
(
style:
actionsStyle
.
copyWith
(
fontWeight:
FontWeight
.
w600
,
letterSpacing:
-
0.08
,
color:
CupertinoColors
.
black
,
),
child:
new
Semantics
(
child:
middle
,
header:
true
),
);
final
Widget
animatedStyledMiddle
=
middleVisible
==
null
?
styledMiddle
...
...
@@ -437,23 +662,26 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
// Auto add back button if leading not provided.
Widget
backOrCloseButton
;
bool
useBackButton
=
false
;
if
(
styledLeading
==
null
&&
automaticallyImplyLeading
)
{
final
ModalRoute
<
dynamic
>
currentRoute
=
ModalRoute
.
of
(
context
);
if
(
currentRoute
?.
canPop
==
true
)
{
useBackButton
=
!(
currentRoute
is
PageRoute
&&
currentRoute
?.
fullscreenDialog
==
true
);
backOrCloseButton
=
new
CupertinoButton
(
child:
useBackButton
?
new
Container
(
height:
_kNavBarPersistentHeight
,
width:
_kNavBarBackButtonTapWidth
,
alignment:
AlignmentDirectional
.
centerStart
,
child:
const
Icon
(
CupertinoIcons
.
back
,
size:
34.0
,)
)
:
const
Text
(
'Close'
),
padding:
EdgeInsets
.
zero
,
onPressed:
()
{
Navigator
.
maybePop
(
context
);
},
);
if
(
currentRoute
is
PageRoute
&&
currentRoute
?.
fullscreenDialog
==
true
)
{
backOrCloseButton
=
new
CupertinoButton
(
child:
const
Padding
(
padding:
EdgeInsetsDirectional
.
only
(
start:
_kNavBarEdgePadding
,
),
child:
Text
(
'Close'
),
),
padding:
EdgeInsets
.
zero
,
onPressed:
()
{
Navigator
.
maybePop
(
context
);
},
);
}
else
{
backOrCloseButton
=
new
CupertinoNavigationBarBackButton
(
color:
actionsForegroundColor
,
previousPageTitle:
previousPageTitle
,
);
}
}
}
...
...
@@ -462,6 +690,7 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
middle:
animatedStyledMiddle
,
trailing:
styledTrailing
,
centerMiddle:
true
,
middleSpacing:
6.0
,
);
if
(
padding
!=
null
)
{
...
...
@@ -476,143 +705,164 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
return
new
SizedBox
(
height:
_kNavBarPersistentHeight
+
MediaQuery
.
of
(
context
).
padding
.
top
,
child:
IconTheme
.
merge
(
data:
new
IconThemeData
(
color:
actionsForegroundColor
,
size:
22.0
,
),
child:
new
SafeArea
(
bottom:
false
,
child:
paddedToolbar
,
),
child:
new
SafeArea
(
bottom:
false
,
child:
paddedToolbar
,
),
);
}
}
class
_CupertinoLargeTitleNavigationBarSliverDelegate
extends
SliverPersistentHeaderDelegate
with
DiagnosticableTreeMixin
{
_CupertinoLargeTitleNavigationBarSliverDelegate
({
@required
this
.
persistentHeight
,
@required
this
.
title
,
this
.
leading
,
this
.
automaticallyImplyLeading
,
this
.
middle
,
this
.
trailing
,
this
.
padding
,
this
.
border
,
this
.
backgroundColor
,
this
.
actionsForegroundColor
,
})
:
assert
(
persistentHeight
!=
null
);
final
double
persistentHeight
;
final
Widget
title
;
final
Widget
leading
;
final
bool
automaticallyImplyLeading
;
/// A nav bar back button typically used in [CupertinoNavigationBar].
///
/// This is automatically inserted into [CupertinoNavigationBar] and
/// [CupertinoSliverNavigationBar]'s `leading` slot when
/// `automaticallyImplyLeading` is true.
///
/// Shows a back chevron and the previous route's title when available from
/// the previous [CupertinoPageRoute.title]. If [previousPageTitle] is specified,
/// it will be shown instead.
class
CupertinoNavigationBarBackButton
extends
StatelessWidget
{
/// Construct a [CupertinoNavigationBarBackButton] that can be used to pop
/// the current route.
///
/// The [color] parameter must not be null.
const
CupertinoNavigationBarBackButton
({
@required
this
.
color
,
this
.
previousPageTitle
,
})
:
assert
(
color
!=
null
);
final
Widget
middle
;
/// The [Color] of the back chevron.
///
/// Must not be null.
final
Color
color
;
final
Widget
trailing
;
/// An override for showing the previous route's title. If null, it will be
/// automatically derived from [CupertinoPageRoute.title] if the current and
/// previous routes are both [CupertinoPageRoute]s.
final
String
previousPageTitle
;
final
EdgeInsetsDirectional
padding
;
@override
Widget
build
(
BuildContext
context
)
{
final
ModalRoute
<
dynamic
>
currentRoute
=
ModalRoute
.
of
(
context
);
assert
(
currentRoute
.
canPop
,
'CupertinoNavigationBarBackButton should only be used in routes that can be popped'
,
);
final
Color
backgroundColor
;
return
new
CupertinoButton
(
child:
new
Semantics
(
container:
true
,
excludeSemantics:
true
,
label:
'Back'
,
button:
true
,
child:
ConstrainedBox
(
constraints:
const
BoxConstraints
(
minWidth:
_kNavBarBackButtonTapWidth
),
child:
new
Row
(
mainAxisSize:
MainAxisSize
.
min
,
mainAxisAlignment:
MainAxisAlignment
.
start
,
children:
<
Widget
>[
const
Padding
(
padding:
EdgeInsetsDirectional
.
only
(
start:
8.0
)),
new
_BackChevron
(
color:
color
),
const
Padding
(
padding:
EdgeInsetsDirectional
.
only
(
start:
6.0
)),
new
Flexible
(
child:
new
_BackLabel
(
specifiedPreviousTitle:
previousPageTitle
,
route:
currentRoute
,
),
),
],
),
),
),
padding:
EdgeInsets
.
zero
,
onPressed:
()
{
Navigator
.
maybePop
(
context
);
},
);
}
}
final
Border
border
;
class
_BackChevron
extends
StatelessWidget
{
const
_BackChevron
({
@required
this
.
color
,
})
:
assert
(
color
!=
null
);
final
Color
actionsForegroundC
olor
;
final
Color
c
olor
;
@override
double
get
minExtent
=>
persistentHeight
;
Widget
build
(
BuildContext
context
)
{
final
TextDirection
textDirection
=
Directionality
.
of
(
context
);
// Replicate the Icon logic here to get a tightly sized icon and add
// custom non-square padding.
Widget
iconWidget
=
new
Text
.
rich
(
new
TextSpan
(
text:
new
String
.
fromCharCode
(
CupertinoIcons
.
back
.
codePoint
),
style:
new
TextStyle
(
inherit:
false
,
color:
color
,
fontSize:
34.0
,
fontFamily:
CupertinoIcons
.
back
.
fontFamily
,
package:
CupertinoIcons
.
back
.
fontPackage
,
),
),
);
switch
(
textDirection
)
{
case
TextDirection
.
rtl
:
iconWidget
=
new
Transform
(
transform:
new
Matrix4
.
identity
()..
scale
(-
1.0
,
1.0
,
1.0
),
alignment:
Alignment
.
center
,
transformHitTests:
false
,
child:
iconWidget
,
);
break
;
case
TextDirection
.
ltr
:
break
;
}
@override
double
get
maxExtent
=>
persistentHeight
+
_kNavBarLargeTitleHeightExtension
;
return
iconWidget
;
}
}
@override
Widget
build
(
BuildContext
context
,
double
shrinkOffset
,
bool
overlapsContent
)
{
final
bool
showLargeTitle
=
shrinkOffset
<
maxExtent
-
minExtent
-
_kNavBarShowLargeTitleThreshold
;
/// A widget that shows next to the back chevron when `automaticallyImplyLeading`
/// is true.
class
_BackLabel
extends
StatelessWidget
{
const
_BackLabel
({
@required
this
.
specifiedPreviousTitle
,
@required
this
.
route
,
})
:
assert
(
route
!=
null
);
final
String
specifiedPreviousTitle
;
final
ModalRoute
<
dynamic
>
route
;
// `child` is never passed in into ValueListenableBuilder so it's always
// null here and unused.
Widget
_buildPreviousTitleWidget
(
BuildContext
context
,
String
previousTitle
,
Widget
child
)
{
if
(
previousTitle
==
null
)
{
return
const
SizedBox
(
height:
0.0
,
width:
0.0
);
}
final
_CupertinoPersistentNavigationBar
persistentNavigationBar
=
new
_CupertinoPersistentNavigationBar
(
leading:
leading
,
automaticallyImplyLeading:
automaticallyImplyLeading
,
middle:
new
Semantics
(
child:
middle
??
title
,
header:
true
),
trailing:
trailing
,
// If middle widget exists, always show it. Otherwise, show title
// when collapsed.
middleVisible:
middle
!=
null
?
null
:
!
showLargeTitle
,
padding:
padding
,
actionsForegroundColor:
actionsForegroundColor
,
);
if
(
previousTitle
.
length
>
10
)
{
return
const
Text
(
'Back'
);
}
return
_wrapWithBackground
(
border:
border
,
backgroundColor:
backgroundColor
,
child:
new
Stack
(
fit:
StackFit
.
expand
,
children:
<
Widget
>[
new
Positioned
(
top:
persistentHeight
,
left:
0.0
,
right:
0.0
,
bottom:
0.0
,
child:
new
ClipRect
(
// The large title starts at the persistent bar.
// It's aligned with the bottom of the sliver and expands clipped
// and behind the persistent bar.
child:
new
OverflowBox
(
minHeight:
0.0
,
maxHeight:
double
.
infinity
,
alignment:
AlignmentDirectional
.
bottomStart
,
child:
new
Padding
(
padding:
const
EdgeInsetsDirectional
.
only
(
start:
_kNavBarEdgePadding
,
bottom:
8.0
,
// Bottom has a different padding.
),
child:
new
DefaultTextStyle
(
style:
_kLargeTitleTextStyle
,
maxLines:
1
,
overflow:
TextOverflow
.
ellipsis
,
child:
new
AnimatedOpacity
(
opacity:
showLargeTitle
?
1.0
:
0.0
,
duration:
_kNavBarTitleFadeDuration
,
child:
new
SafeArea
(
top:
false
,
bottom:
false
,
child:
new
Semantics
(
header:
true
,
child:
title
,
),
),
),
),
),
),
),
),
new
Positioned
(
left:
0.0
,
right:
0.0
,
top:
0.0
,
child:
persistentNavigationBar
,
),
],
),
);
return
new
Text
(
previousTitle
,
maxLines:
1
);
}
@override
bool
shouldRebuild
(
_CupertinoLargeTitleNavigationBarSliverDelegate
oldDelegate
)
{
return
persistentHeight
!=
oldDelegate
.
persistentHeight
||
title
!=
oldDelegate
.
title
||
leading
!=
oldDelegate
.
leading
||
middle
!=
oldDelegate
.
middle
||
trailing
!=
oldDelegate
.
trailing
||
border
!=
oldDelegate
.
border
||
backgroundColor
!=
oldDelegate
.
backgroundColor
||
actionsForegroundColor
!=
oldDelegate
.
actionsForegroundColor
;
Widget
build
(
BuildContext
context
)
{
if
(
specifiedPreviousTitle
!=
null
)
{
return
_buildPreviousTitleWidget
(
context
,
specifiedPreviousTitle
,
null
);
}
else
if
(
route
is
CupertinoPageRoute
<
dynamic
>)
{
final
CupertinoPageRoute
<
dynamic
>
cupertinoRoute
=
route
;
// There is no timing issue because the previousTitle Listenable changes
// happen during route modifications before the ValueListenableBuilder
// is built.
return
new
ValueListenableBuilder
<
String
>(
valueListenable:
cupertinoRoute
.
previousTitle
,
builder:
_buildPreviousTitleWidget
,
);
}
else
{
return
const
SizedBox
(
height:
0.0
,
width:
0.0
);
}
}
}
packages/flutter/lib/src/cupertino/route.dart
View file @
c418b2f3
...
...
@@ -4,6 +4,7 @@
import
'dart:async'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
...
...
@@ -87,6 +88,7 @@ class CupertinoPageRoute<T> extends PageRoute<T> {
/// be null.
CupertinoPageRoute
({
@required
this
.
builder
,
this
.
title
,
RouteSettings
settings
,
this
.
maintainState
=
true
,
bool
fullscreenDialog
=
false
,
...
...
@@ -102,6 +104,50 @@ class CupertinoPageRoute<T> extends PageRoute<T> {
/// Builds the primary contents of the route.
final
WidgetBuilder
builder
;
/// A title string for this route.
///
/// Used to autopopulate [CupertinoNavigationBar] and
/// [CupertinoSliverNavigationBar]'s `middle`/`largeTitle` widgets when
/// one is not manually supplied.
final
String
title
;
ValueNotifier
<
String
>
_previousTitle
;
/// The title string of the previous [CupertinoPageRoute].
///
/// The [ValueListenable]'s value is readable after the route is installed
/// onto a [Navigator]. The [ValueListenable] will also notify its listeners
/// if the value changes (such as by replacing the previous route).
///
/// The [ValueListenable] itself will be null before the route is installed.
/// Its content value will be null if the previous route has no title or
/// is not a [CupertinoPageRoute].
///
/// See also:
///
/// * [ValueListenableBuilder], which can be used to listen and rebuild
/// widgets based on a ValueListenable.
ValueListenable
<
String
>
get
previousTitle
{
assert
(
_previousTitle
!=
null
,
'Cannot read the previousTitle for a route that has not yet been installed'
,
);
return
_previousTitle
;
}
@override
void
didChangePrevious
(
Route
<
dynamic
>
previousRoute
)
{
final
String
previousTitleString
=
previousRoute
is
CupertinoPageRoute
?
previousRoute
.
title
:
null
;
if
(
_previousTitle
==
null
)
{
_previousTitle
=
new
ValueNotifier
<
String
>(
previousTitleString
);
}
else
{
_previousTitle
.
value
=
previousTitleString
;
}
super
.
didChangePrevious
(
previousRoute
);
}
@override
final
bool
maintainState
;
...
...
@@ -511,7 +557,6 @@ class _CupertinoBackGestureDetectorState<T> extends State<_CupertinoBackGestureD
}
}
/// A controller for an iOS-style back gesture.
///
/// This is created by a [CupertinoPageRoute] in response from a gesture caught
...
...
packages/flutter/lib/src/cupertino/tab_view.dart
View file @
c418b2f3
...
...
@@ -42,6 +42,7 @@ class CupertinoTabView extends StatelessWidget {
const
CupertinoTabView
({
Key
key
,
this
.
builder
,
this
.
defaultTitle
,
this
.
routes
,
this
.
onGenerateRoute
,
this
.
onUnknownRoute
,
...
...
@@ -56,6 +57,9 @@ class CupertinoTabView extends StatelessWidget {
/// as [builder] takes its place.
final
WidgetBuilder
builder
;
/// The title of the default route.
final
String
defaultTitle
;
/// This tab view's routing table.
///
/// When a named route is pushed with [Navigator.pushNamed] inside this tab view,
...
...
@@ -109,13 +113,17 @@ class CupertinoTabView extends StatelessWidget {
Route
<
dynamic
>
_onGenerateRoute
(
RouteSettings
settings
)
{
final
String
name
=
settings
.
name
;
WidgetBuilder
routeBuilder
;
if
(
name
==
Navigator
.
defaultRouteName
&&
builder
!=
null
)
String
title
;
if
(
name
==
Navigator
.
defaultRouteName
&&
builder
!=
null
)
{
routeBuilder
=
builder
;
title
=
defaultTitle
;
}
else
if
(
routes
!=
null
)
routeBuilder
=
routes
[
name
];
if
(
routeBuilder
!=
null
)
{
return
new
CupertinoPageRoute
<
dynamic
>(
builder:
routeBuilder
,
title:
title
,
settings:
settings
,
);
}
...
...
packages/flutter/lib/src/widgets/navigator.dart
View file @
c418b2f3
...
...
@@ -111,15 +111,15 @@ abstract class Route<T> {
///
/// The returned value resolves when the push transition is complete.
///
/// The [didChangeNext]
method is typically called immediately after this
/// method is called.
/// The [didChangeNext]
and [didChangePrevious] methods are typically called
///
immediately after this
method is called.
@protected
TickerFuture
didPush
()
=>
new
TickerFuture
.
complete
();
/// Called after [install] when the route replaced another in the navigator.
///
/// The [didChangeNext]
method is typically called immediately after this
/// method is called.
/// The [didChangeNext]
and [didChangePrevious] methods are typically called
///
immediately after this
method is called.
@protected
@mustCallSuper
void
didReplace
(
Route
<
dynamic
>
oldRoute
)
{
}
...
...
@@ -201,9 +201,8 @@ abstract class Route<T> {
/// This route's previous route has changed to the given new route. This is
/// called on a route whenever the previous route changes for any reason, so
/// long as it is in the history, except for immediately after the route has
/// been pushed (in which case [didPush] or [didReplace] will be called
/// instead). `previousRoute` will be null if there's no previous route.
/// long as it is in the history. `previousRoute` will be null if there's no
/// previous route.
@protected
@mustCallSuper
void
didChangePrevious
(
Route
<
dynamic
>
previousRoute
)
{
}
...
...
@@ -1539,8 +1538,10 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
_history
.
add
(
route
);
route
.
didPush
();
route
.
didChangeNext
(
null
);
if
(
oldRoute
!=
null
)
if
(
oldRoute
!=
null
)
{
oldRoute
.
didChangeNext
(
route
);
route
.
didChangePrevious
(
oldRoute
);
}
for
(
NavigatorObserver
observer
in
widget
.
observers
)
observer
.
didPush
(
route
,
oldRoute
);
assert
(()
{
_debugLocked
=
false
;
return
true
;
}());
...
...
@@ -1589,8 +1590,10 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
}
});
newRoute
.
didChangeNext
(
null
);
if
(
index
>
0
)
if
(
index
>
0
)
{
_history
[
index
-
1
].
didChangeNext
(
newRoute
);
newRoute
.
didChangePrevious
(
_history
[
index
-
1
]);
}
for
(
NavigatorObserver
observer
in
widget
.
observers
)
observer
.
didReplace
(
newRoute:
newRoute
,
oldRoute:
oldRoute
);
assert
(()
{
_debugLocked
=
false
;
return
true
;
}());
...
...
@@ -1684,8 +1687,10 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
}
else
{
newRoute
.
didChangeNext
(
null
);
}
if
(
index
>
0
)
if
(
index
>
0
)
{
_history
[
index
-
1
].
didChangeNext
(
newRoute
);
newRoute
.
didChangePrevious
(
_history
[
index
-
1
]);
}
for
(
NavigatorObserver
observer
in
widget
.
observers
)
observer
.
didReplace
(
newRoute:
newRoute
,
oldRoute:
oldRoute
);
oldRoute
.
dispose
();
...
...
packages/flutter/test/cupertino/nav_bar_test.dart
View file @
c418b2f3
...
...
@@ -393,7 +393,7 @@ void main() {
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
expect
(
find
.
byType
(
CupertinoButton
),
findsOneWidget
);
expect
(
find
.
byType
(
Icon
),
findsOneWidget
);
expect
(
find
.
text
(
new
String
.
fromCharCode
(
CupertinoIcons
.
back
.
codePoint
)
),
findsOneWidget
);
tester
.
state
<
NavigatorState
>(
find
.
byType
(
Navigator
)).
push
(
new
CupertinoPageRoute
<
void
>(
fullscreenDialog:
true
,
...
...
@@ -418,7 +418,7 @@ void main() {
expect
(
find
.
text
(
'Page 2'
),
findsOneWidget
);
await
tester
.
tap
(
find
.
byType
(
Icon
));
await
tester
.
tap
(
find
.
text
(
new
String
.
fromCharCode
(
CupertinoIcons
.
back
.
codePoint
)
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
...
...
@@ -426,6 +426,49 @@ void main() {
expect
(
find
.
text
(
'Home page'
),
findsOneWidget
);
});
testWidgets
(
'Long back label turns into "back"'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
new
CupertinoApp
(
home:
const
Placeholder
(),
),
);
tester
.
state
<
NavigatorState
>(
find
.
byType
(
Navigator
)).
push
(
new
CupertinoPageRoute
<
void
>(
builder:
(
BuildContext
context
)
{
return
const
CupertinoPageScaffold
(
navigationBar:
CupertinoNavigationBar
(
previousPageTitle:
'0123456789'
,
),
child:
Placeholder
(),
);
}
)
);
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
find
.
widgetWithText
(
CupertinoButton
,
'0123456789'
),
findsOneWidget
);
tester
.
state
<
NavigatorState
>(
find
.
byType
(
Navigator
)).
push
(
new
CupertinoPageRoute
<
void
>(
builder:
(
BuildContext
context
)
{
return
const
CupertinoPageScaffold
(
navigationBar:
CupertinoNavigationBar
(
previousPageTitle:
'01234567890'
,
),
child:
Placeholder
(),
);
}
)
);
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
find
.
widgetWithText
(
CupertinoButton
,
'Back'
),
findsOneWidget
);
});
testWidgets
(
'Border should be displayed by default'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
new
CupertinoApp
(
...
...
packages/flutter/test/cupertino/route_test.dart
0 → 100644
View file @
c418b2f3
// Copyright 2018 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_test/flutter_test.dart'
;
void
main
(
)
{
testWidgets
(
'Middle auto-populates with title'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
new
CupertinoApp
(
home:
const
Placeholder
(),
),
);
tester
.
state
<
NavigatorState
>(
find
.
byType
(
Navigator
)).
push
(
new
CupertinoPageRoute
<
void
>(
title:
'An iPod'
,
builder:
(
BuildContext
context
)
{
return
const
CupertinoPageScaffold
(
navigationBar:
CupertinoNavigationBar
(),
child:
Placeholder
(),
);
}
)
);
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
// There should be a Text widget with the title in the nav bar even though
// we didn't specify anything in the nav bar constructor.
expect
(
find
.
widgetWithText
(
CupertinoNavigationBar
,
'An iPod'
),
findsOneWidget
);
// As a title, it should also be centered.
expect
(
tester
.
getCenter
(
find
.
text
(
'An iPod'
)).
dx
,
400.0
);
});
testWidgets
(
'Leading auto-populates with back button with previous title'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
new
CupertinoApp
(
home:
const
Placeholder
(),
),
);
tester
.
state
<
NavigatorState
>(
find
.
byType
(
Navigator
)).
push
(
new
CupertinoPageRoute
<
void
>(
title:
'An iPod'
,
builder:
(
BuildContext
context
)
{
return
const
CupertinoPageScaffold
(
navigationBar:
CupertinoNavigationBar
(),
child:
Placeholder
(),
);
}
)
);
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
tester
.
state
<
NavigatorState
>(
find
.
byType
(
Navigator
)).
push
(
new
CupertinoPageRoute
<
void
>(
title:
'A Phone'
,
builder:
(
BuildContext
context
)
{
return
const
CupertinoPageScaffold
(
navigationBar:
CupertinoNavigationBar
(),
child:
Placeholder
(),
);
}
)
);
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
find
.
widgetWithText
(
CupertinoNavigationBar
,
'A Phone'
),
findsOneWidget
);
expect
(
tester
.
getCenter
(
find
.
text
(
'A Phone'
)).
dx
,
400.0
);
// Also shows the previous page's title next to the back button.
expect
(
find
.
widgetWithText
(
CupertinoButton
,
'An iPod'
),
findsOneWidget
);
// 2 paddings + 1 ahem character at font size 34.0.
expect
(
tester
.
getTopLeft
(
find
.
text
(
'An iPod'
)).
dx
,
8.0
+
34.0
+
6.0
);
});
testWidgets
(
'Previous title is correct on first transition frame'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
new
CupertinoApp
(
home:
const
Placeholder
(),
),
);
tester
.
state
<
NavigatorState
>(
find
.
byType
(
Navigator
)).
push
(
new
CupertinoPageRoute
<
void
>(
title:
'An iPod'
,
builder:
(
BuildContext
context
)
{
return
const
CupertinoPageScaffold
(
navigationBar:
CupertinoNavigationBar
(),
child:
Placeholder
(),
);
}
)
);
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
tester
.
state
<
NavigatorState
>(
find
.
byType
(
Navigator
)).
push
(
new
CupertinoPageRoute
<
void
>(
title:
'A Phone'
,
builder:
(
BuildContext
context
)
{
return
const
CupertinoPageScaffold
(
navigationBar:
CupertinoNavigationBar
(),
child:
Placeholder
(),
);
}
)
);
// Trigger the route push
await
tester
.
pump
();
// Draw the first frame.
await
tester
.
pump
();
// Also shows the previous page's title next to the back button.
expect
(
find
.
widgetWithText
(
CupertinoButton
,
'An iPod'
),
findsOneWidget
);
});
testWidgets
(
'Previous title stays up to date with changing routes'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
new
CupertinoApp
(
home:
const
Placeholder
(),
),
);
final
CupertinoPageRoute
<
void
>
route2
=
new
CupertinoPageRoute
<
void
>(
title:
'An iPod'
,
builder:
(
BuildContext
context
)
{
return
const
CupertinoPageScaffold
(
navigationBar:
CupertinoNavigationBar
(),
child:
Placeholder
(),
);
}
);
final
CupertinoPageRoute
<
void
>
route3
=
new
CupertinoPageRoute
<
void
>(
title:
'A Phone'
,
builder:
(
BuildContext
context
)
{
return
const
CupertinoPageScaffold
(
navigationBar:
CupertinoNavigationBar
(),
child:
Placeholder
(),
);
}
);
tester
.
state
<
NavigatorState
>(
find
.
byType
(
Navigator
)).
push
(
route2
);
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
tester
.
state
<
NavigatorState
>(
find
.
byType
(
Navigator
)).
push
(
route3
);
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
tester
.
state
<
NavigatorState
>(
find
.
byType
(
Navigator
)).
replace
(
oldRoute:
route2
,
newRoute:
new
CupertinoPageRoute
<
void
>(
title:
'An Internet communicator'
,
builder:
(
BuildContext
context
)
{
return
const
CupertinoPageScaffold
(
navigationBar:
CupertinoNavigationBar
(),
child:
Placeholder
(),
);
}
)
);
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
find
.
widgetWithText
(
CupertinoNavigationBar
,
'A Phone'
),
findsOneWidget
);
expect
(
tester
.
getCenter
(
find
.
text
(
'A Phone'
)).
dx
,
400.0
);
// After swapping the route behind the top one, the previous label changes
// from An iPod to Back (since An Internet communicator is too long to
// fit in the back button).
expect
(
find
.
widgetWithText
(
CupertinoButton
,
'Back'
),
findsOneWidget
);
expect
(
tester
.
getTopLeft
(
find
.
text
(
'Back'
)).
dx
,
8.0
+
34.0
+
6.0
);
});
}
packages/flutter_test/lib/src/widget_tester.dart
View file @
c418b2f3
...
...
@@ -608,7 +608,7 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
return
TestAsyncUtils
.
guard
(()
async
{
Finder
backButton
=
find
.
byTooltip
(
'Back'
);
if
(
backButton
.
evaluate
().
isEmpty
)
{
backButton
=
find
.
widgetWithIcon
(
CupertinoButton
,
CupertinoIcons
.
back
);
backButton
=
find
.
byType
(
CupertinoNavigationBarBackButton
);
}
expectSync
(
backButton
,
findsOneWidget
,
reason:
'One back button expected on screen'
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment