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 {
...
@@ -47,51 +47,58 @@ class CupertinoNavigationDemo extends StatelessWidget {
return
new
WillPopScope
(
return
new
WillPopScope
(
// Prevent swipe popping of this page. Use explicit exit buttons only.
// Prevent swipe popping of this page. Use explicit exit buttons only.
onWillPop:
()
=>
new
Future
<
bool
>.
value
(
true
),
onWillPop:
()
=>
new
Future
<
bool
>.
value
(
true
),
child:
new
CupertinoTabScaffold
(
child:
new
DefaultTextStyle
(
tabBar:
new
CupertinoTabBar
(
style:
const
TextStyle
(
items:
const
<
BottomNavigationBarItem
>[
fontFamily:
'.SF UI Text'
,
BottomNavigationBarItem
(
fontSize:
17.0
,
icon:
Icon
(
CupertinoIcons
.
home
),
color:
CupertinoColors
.
black
,
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
)
{
child:
new
CupertinoTabScaffold
(
return
new
DefaultTextStyle
(
tabBar:
new
CupertinoTabBar
(
style:
const
TextStyle
(
items:
const
<
BottomNavigationBarItem
>[
fontFamily:
'.SF UI Text'
,
BottomNavigationBarItem
(
fontSize:
17.0
,
icon:
Icon
(
CupertinoIcons
.
home
),
color:
CupertinoColors
.
black
,
title:
Text
(
'Home'
),
),
),
child:
new
CupertinoTabView
(
BottomNavigationBarItem
(
builder:
(
BuildContext
context
)
{
icon:
Icon
(
CupertinoIcons
.
conversation_bubble
),
switch
(
index
)
{
title:
Text
(
'Support'
),
case
0
:
),
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
(
return
new
CupertinoDemoTab1
(
colorItems:
colorItems
,
colorItems:
colorItems
,
colorNameItems:
colorNameItems
colorNameItems:
colorNameItems
);
);
break
;
},
case
1
:
defaultTitle:
'Colors'
,
return
new
CupertinoDemoTab2
();
);
break
;
break
;
case
2
:
case
1
:
return
new
CupertinoDemoTab3
();
return
new
CupertinoTabView
(
break
;
builder:
(
BuildContext
context
)
=>
CupertinoDemoTab2
(),
default
:
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 {
...
@@ -129,7 +136,6 @@ class CupertinoDemoTab1 extends StatelessWidget {
child:
new
CustomScrollView
(
child:
new
CustomScrollView
(
slivers:
<
Widget
>[
slivers:
<
Widget
>[
const
CupertinoSliverNavigationBar
(
const
CupertinoSliverNavigationBar
(
largeTitle:
Text
(
'Colors'
),
trailing:
ExitButton
(),
trailing:
ExitButton
(),
),
),
new
SliverPadding
(
new
SliverPadding
(
...
@@ -174,6 +180,7 @@ class Tab1RowItem extends StatelessWidget {
...
@@ -174,6 +180,7 @@ class Tab1RowItem extends StatelessWidget {
behavior:
HitTestBehavior
.
opaque
,
behavior:
HitTestBehavior
.
opaque
,
onTap:
()
{
onTap:
()
{
Navigator
.
of
(
context
).
push
(
new
CupertinoPageRoute
<
void
>(
Navigator
.
of
(
context
).
push
(
new
CupertinoPageRoute
<
void
>(
title:
colorName
,
builder:
(
BuildContext
context
)
=>
new
Tab1ItemPage
(
builder:
(
BuildContext
context
)
=>
new
Tab1ItemPage
(
color:
color
,
color:
color
,
colorName:
colorName
,
colorName:
colorName
,
...
@@ -285,9 +292,8 @@ class Tab1ItemPageState extends State<Tab1ItemPage> {
...
@@ -285,9 +292,8 @@ class Tab1ItemPageState extends State<Tab1ItemPage> {
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
return
new
CupertinoPageScaffold
(
return
new
CupertinoPageScaffold
(
navigationBar:
new
CupertinoNavigationBar
(
navigationBar:
const
CupertinoNavigationBar
(
middle:
new
Text
(
widget
.
colorName
),
trailing:
ExitButton
(),
trailing:
const
ExitButton
(),
),
),
child:
new
SafeArea
(
child:
new
SafeArea
(
top:
false
,
top:
false
,
...
@@ -415,7 +421,6 @@ class CupertinoDemoTab2 extends StatelessWidget {
...
@@ -415,7 +421,6 @@ class CupertinoDemoTab2 extends StatelessWidget {
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
return
new
CupertinoPageScaffold
(
return
new
CupertinoPageScaffold
(
navigationBar:
const
CupertinoNavigationBar
(
navigationBar:
const
CupertinoNavigationBar
(
middle:
Text
(
'Support Chat'
),
trailing:
ExitButton
(),
trailing:
ExitButton
(),
),
),
child:
new
ListView
(
child:
new
ListView
(
...
@@ -699,7 +704,6 @@ class CupertinoDemoTab3 extends StatelessWidget {
...
@@ -699,7 +704,6 @@ class CupertinoDemoTab3 extends StatelessWidget {
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
return
new
CupertinoPageScaffold
(
return
new
CupertinoPageScaffold
(
navigationBar:
const
CupertinoNavigationBar
(
navigationBar:
const
CupertinoNavigationBar
(
middle:
Text
(
'Account'
),
trailing:
ExitButton
(),
trailing:
ExitButton
(),
),
),
child:
new
DecoratedBox
(
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
...
@@ -50,6 +50,7 @@ class _CupertinoRefreshControlDemoState extends State<CupertinoRefreshControlDem
slivers:
<
Widget
>[
slivers:
<
Widget
>[
const
CupertinoSliverNavigationBar
(
const
CupertinoSliverNavigationBar
(
largeTitle:
Text
(
'Cupertino Refresh'
),
largeTitle:
Text
(
'Cupertino Refresh'
),
previousPageTitle:
'Cupertino'
,
),
),
new
CupertinoSliverRefreshControl
(
new
CupertinoSliverRefreshControl
(
onRefresh:
()
{
onRefresh:
()
{
...
...
packages/flutter/lib/src/cupertino/nav_bar.dart
View file @
c418b2f3
...
@@ -12,6 +12,7 @@ import 'button.dart';
...
@@ -12,6 +12,7 @@ import 'button.dart';
import
'colors.dart'
;
import
'colors.dart'
;
import
'icons.dart'
;
import
'icons.dart'
;
import
'page_scaffold.dart'
;
import
'page_scaffold.dart'
;
import
'route.dart'
;
/// Standard iOS navigation bar height without the status bar.
/// Standard iOS navigation bar height without the status bar.
const
double
_kNavBarPersistentHeight
=
44.0
;
const
double
_kNavBarPersistentHeight
=
44.0
;
...
@@ -62,6 +63,10 @@ const TextStyle _kLargeTitleTextStyle = TextStyle(
...
@@ -62,6 +63,10 @@ const TextStyle _kLargeTitleTextStyle = TextStyle(
/// close button in case of a fullscreen dialog) to pop the current route if none
/// close button in case of a fullscreen dialog) to pop the current route if none
/// is provided and [automaticallyImplyLeading] is true (true by default).
/// 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
/// It should be placed at top of the screen and automatically accounts for
/// the OS's status bar.
/// the OS's status bar.
///
///
...
@@ -80,6 +85,8 @@ class CupertinoNavigationBar extends StatelessWidget implements ObstructingPrefe
...
@@ -80,6 +85,8 @@ class CupertinoNavigationBar extends StatelessWidget implements ObstructingPrefe
Key
key
,
Key
key
,
this
.
leading
,
this
.
leading
,
this
.
automaticallyImplyLeading
=
true
,
this
.
automaticallyImplyLeading
=
true
,
this
.
automaticallyImplyMiddle
=
true
,
this
.
previousPageTitle
,
this
.
middle
,
this
.
middle
,
this
.
trailing
,
this
.
trailing
,
this
.
border
=
_kDefaultNavBarBorder
,
this
.
border
=
_kDefaultNavBarBorder
,
...
@@ -87,35 +94,83 @@ class CupertinoNavigationBar extends StatelessWidget implements ObstructingPrefe
...
@@ -87,35 +94,83 @@ class CupertinoNavigationBar extends StatelessWidget implements ObstructingPrefe
this
.
padding
,
this
.
padding
,
this
.
actionsForegroundColor
=
CupertinoColors
.
activeBlue
,
this
.
actionsForegroundColor
=
CupertinoColors
.
activeBlue
,
})
:
assert
(
automaticallyImplyLeading
!=
null
),
})
:
assert
(
automaticallyImplyLeading
!=
null
),
assert
(
automaticallyImplyMiddle
!=
null
),
super
(
key:
key
);
super
(
key:
key
);
/// {@template flutter.cupertino.navBar.leading}
/// Widget to place at the start of the navigation bar. Normally a back button
/// 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.
/// 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
;
final
Widget
leading
;
/// {@template flutter.cupertino.navBar.automaticallyImplyLeading}
/// Controls whether we should try to imply the leading widget if null.
/// 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]
/// 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.
/// 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.
/// This value cannot be null.
/// {@endtemplate}
final
bool
automaticallyImplyLeading
;
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
/// Widget to place in the middle of the navigation bar. Normally a title or
/// a segmented control.
/// 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
;
final
Widget
middle
;
/// {@template flutter.cupertino.navBar.trailing}
/// Widget to place at the end of the navigation bar. Normally additional actions
/// Widget to place at the end of the navigation bar. Normally additional actions
/// taken on the page such as a search or edit function.
/// taken on the page such as a search or edit function.
/// {@endtemplate}
final
Widget
trailing
;
final
Widget
trailing
;
// TODO(xster): implement support for double row navigation bars.
// 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
/// The background color of the navigation bar. If it contains transparency, the
/// tab bar will automatically produce a blurring effect to the content
/// tab bar will automatically produce a blurring effect to the content
/// behind it.
/// behind it.
/// {@endtemplate}
final
Color
backgroundColor
;
final
Color
backgroundColor
;
/// {@template flutter.cupertino.navBar.padding}
/// Padding for the contents of the navigation bar.
/// Padding for the contents of the navigation bar.
///
///
/// If null, the navigation bar will adopt the following defaults:
/// If null, the navigation bar will adopt the following defaults:
...
@@ -127,11 +182,14 @@ class CupertinoNavigationBar extends StatelessWidget implements ObstructingPrefe
...
@@ -127,11 +182,14 @@ class CupertinoNavigationBar extends StatelessWidget implements ObstructingPrefe
/// which case the padding will be 0.
/// which case the padding will be 0.
///
///
/// Vertical padding won't change the height of the nav bar.
/// Vertical padding won't change the height of the nav bar.
/// {@endtemplate}
final
EdgeInsetsDirectional
padding
;
final
EdgeInsetsDirectional
padding
;
/// {@template flutter.cupertino.navBar.border}
/// The border of the navigation bar. By default renders a single pixel bottom border side.
/// 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.
/// If a border is null, the navigation bar will not display a border.
/// {@endtemplate}
final
Border
border
;
final
Border
border
;
/// Default color used for text and icons of the [leading] and [trailing]
/// Default color used for text and icons of the [leading] and [trailing]
...
@@ -152,13 +210,20 @@ class CupertinoNavigationBar extends StatelessWidget implements ObstructingPrefe
...
@@ -152,13 +210,20 @@ class CupertinoNavigationBar extends StatelessWidget implements ObstructingPrefe
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
final
Widget
effectiveMiddle
=
_effectiveTitle
(
title:
middle
,
automaticallyImplyTitle:
automaticallyImplyMiddle
,
currentRoute:
ModalRoute
.
of
(
context
),
);
return
_wrapWithBackground
(
return
_wrapWithBackground
(
border:
border
,
border:
border
,
backgroundColor:
backgroundColor
,
backgroundColor:
backgroundColor
,
child:
new
_CupertinoPersistentNavigationBar
(
child:
new
_CupertinoPersistentNavigationBar
(
leading:
leading
,
leading:
leading
,
automaticallyImplyLeading:
automaticallyImplyLeading
,
automaticallyImplyLeading:
automaticallyImplyLeading
,
middle:
new
Semantics
(
child:
middle
,
header:
true
),
previousPageTitle:
previousPageTitle
,
middle:
effectiveMiddle
,
trailing:
trailing
,
trailing:
trailing
,
padding:
padding
,
padding:
padding
,
actionsForegroundColor:
actionsForegroundColor
,
actionsForegroundColor:
actionsForegroundColor
,
...
@@ -192,6 +257,10 @@ class CupertinoNavigationBar extends StatelessWidget implements ObstructingPrefe
...
@@ -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
/// close button in case of a fullscreen dialog) to pop the current route if none
/// is provided and [automaticallyImplyLeading] is true (true by default).
/// 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:
/// See also:
///
///
/// * [CupertinoNavigationBar], an iOS navigation bar for use on non-scrolling
/// * [CupertinoNavigationBar], an iOS navigation bar for use on non-scrolling
...
@@ -202,17 +271,19 @@ class CupertinoSliverNavigationBar extends StatelessWidget {
...
@@ -202,17 +271,19 @@ class CupertinoSliverNavigationBar extends StatelessWidget {
/// The [largeTitle] argument is required and must not be null.
/// The [largeTitle] argument is required and must not be null.
const
CupertinoSliverNavigationBar
({
const
CupertinoSliverNavigationBar
({
Key
key
,
Key
key
,
@required
this
.
largeTitle
,
this
.
largeTitle
,
this
.
leading
,
this
.
leading
,
this
.
automaticallyImplyLeading
=
true
,
this
.
automaticallyImplyLeading
=
true
,
this
.
automaticallyImplyTitle
=
true
,
this
.
previousPageTitle
,
this
.
middle
,
this
.
middle
,
this
.
trailing
,
this
.
trailing
,
this
.
border
=
_kDefaultNavBarBorder
,
this
.
border
=
_kDefaultNavBarBorder
,
this
.
backgroundColor
=
_kDefaultNavBarBackgroundColor
,
this
.
backgroundColor
=
_kDefaultNavBarBackgroundColor
,
this
.
padding
,
this
.
padding
,
this
.
actionsForegroundColor
=
CupertinoColors
.
activeBlue
,
this
.
actionsForegroundColor
=
CupertinoColors
.
activeBlue
,
})
:
assert
(
largeTitle
!=
null
),
})
:
assert
(
automaticallyImplyLeading
!=
null
),
assert
(
automaticallyImply
Leading
!=
null
),
assert
(
automaticallyImply
Title
!=
null
),
super
(
key:
key
);
super
(
key:
key
);
/// The navigation bar's title.
/// The navigation bar's title.
...
@@ -229,21 +300,31 @@ class CupertinoSliverNavigationBar extends StatelessWidget {
...
@@ -229,21 +300,31 @@ class CupertinoSliverNavigationBar extends StatelessWidget {
/// any [GlobalKey]s, and that it not rely on maintaining state (for example,
/// 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,
/// 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).
/// 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
;
final
Widget
largeTitle
;
/// Widget to place at the start of the static navigation bar. Normally a back button
/// {@macro flutter.cupertino.navBar.leading}
/// for a normal page or a cancel button for full page dialogs.
///
///
/// This widget is visible in both collapsed and expanded states.
/// This widget is visible in both collapsed and expanded states.
final
Widget
leading
;
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]
/// If true and [largeTitle] is null, automatically fill in a [Text] widget
/// widget should be. If [leading] widget is not null, this parameter has no effect.
/// 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.
/// 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
/// A widget to place in the middle of the static navigation bar instead of
/// the [largeTitle].
/// the [largeTitle].
...
@@ -253,39 +334,24 @@ class CupertinoSliverNavigationBar extends StatelessWidget {
...
@@ -253,39 +334,24 @@ class CupertinoSliverNavigationBar extends StatelessWidget {
/// [middle] widget is provided.
/// [middle] widget is provided.
final
Widget
middle
;
final
Widget
middle
;
/// Widget to place at the end of the static navigation bar. Normally
/// {@macro flutter.cupertino.navBar.trailing}
/// additional actions taken on the page such as a search or edit function.
///
///
/// This widget is visible in both collapsed and expanded states.
/// This widget is visible in both collapsed and expanded states.
final
Widget
trailing
;
final
Widget
trailing
;
/// Padding for the contents of the navigation bar.
/// {@macro flutter.cupertino.navBar.backgroundColor}
///
final
Color
backgroundColor
;
/// If null, the navigation bar will adopt the following defaults:
///
/// {@macro flutter.cupertino.navBar.padding}
/// * 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.
final
EdgeInsetsDirectional
padding
;
final
EdgeInsetsDirectional
padding
;
/// The border of the navigation bar. By default renders a single pixel bottom border side.
/// {@macro flutter.cupertino.navBar.border}
///
/// If a border is null, the navigation bar will not display a border.
final
Border
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]
/// Default color used for text and icons of the [leading] and [trailing]
/// widgets in the navigation bar.
/// 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.
/// iOS standard design.
final
Color
actionsForegroundColor
;
final
Color
actionsForegroundColor
;
...
@@ -294,13 +360,20 @@ class CupertinoSliverNavigationBar extends StatelessWidget {
...
@@ -294,13 +360,20 @@ class CupertinoSliverNavigationBar extends StatelessWidget {
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
final
Widget
effectiveTitle
=
_effectiveTitle
(
title:
largeTitle
,
automaticallyImplyTitle:
automaticallyImplyTitle
,
currentRoute:
ModalRoute
.
of
(
context
),
);
return
new
SliverPersistentHeader
(
return
new
SliverPersistentHeader
(
pinned:
true
,
// iOS navigation bars are always pinned.
pinned:
true
,
// iOS navigation bars are always pinned.
delegate:
new
_CupertinoLargeTitleNavigationBarSliverDelegate
(
delegate:
new
_CupertinoLargeTitleNavigationBarSliverDelegate
(
persistentHeight:
_kNavBarPersistentHeight
+
MediaQuery
.
of
(
context
).
padding
.
top
,
persistentHeight:
_kNavBarPersistentHeight
+
MediaQuery
.
of
(
context
).
padding
.
top
,
title:
larg
eTitle
,
largeTitle:
effectiv
eTitle
,
leading:
leading
,
leading:
leading
,
automaticallyImplyLeading:
automaticallyImplyLeading
,
automaticallyImplyLeading:
automaticallyImplyLeading
,
previousPageTitle:
previousPageTitle
,
middle:
middle
,
middle:
middle
,
trailing:
trailing
,
trailing:
trailing
,
padding:
padding
,
padding:
padding
,
...
@@ -312,6 +385,137 @@ class CupertinoSliverNavigationBar extends StatelessWidget {
...
@@ -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
/// Returns `child` wrapped with background and a bottom border if background color
/// is opaque. Otherwise, also blur with [BackdropFilter].
/// is opaque. Otherwise, also blur with [BackdropFilter].
Widget
_wrapWithBackground
(
{
Widget
_wrapWithBackground
(
{
...
@@ -347,6 +551,22 @@ 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.
/// The top part of the navigation bar that's never scrolled away.
///
///
/// Consists of the entire navigation bar without background and border when used
/// Consists of the entire navigation bar without background and border when used
...
@@ -357,6 +577,7 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
...
@@ -357,6 +577,7 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
Key
key
,
Key
key
,
this
.
leading
,
this
.
leading
,
this
.
automaticallyImplyLeading
,
this
.
automaticallyImplyLeading
,
this
.
previousPageTitle
,
this
.
middle
,
this
.
middle
,
this
.
trailing
,
this
.
trailing
,
this
.
padding
,
this
.
padding
,
...
@@ -368,6 +589,8 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
...
@@ -368,6 +589,8 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
final
bool
automaticallyImplyLeading
;
final
bool
automaticallyImplyLeading
;
final
String
previousPageTitle
;
final
Widget
middle
;
final
Widget
middle
;
final
Widget
trailing
;
final
Widget
trailing
;
...
@@ -418,14 +641,16 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
...
@@ -418,14 +641,16 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
// Let the middle be black rather than `actionsForegroundColor` in case
// Let the middle be black rather than `actionsForegroundColor` in case
// it's a plain text title.
// it's a plain text title.
final
Widget
styledMiddle
=
middle
==
null
?
null
:
new
DefaultTextStyle
(
final
Widget
styledMiddle
=
middle
==
null
style:
actionsStyle
.
copyWith
(
?
null
fontWeight:
FontWeight
.
w600
,
:
new
DefaultTextStyle
(
letterSpacing:
-
0.08
,
style:
actionsStyle
.
copyWith
(
color:
CupertinoColors
.
black
,
fontWeight:
FontWeight
.
w600
,
),
letterSpacing:
-
0.08
,
child:
middle
,
color:
CupertinoColors
.
black
,
);
),
child:
new
Semantics
(
child:
middle
,
header:
true
),
);
final
Widget
animatedStyledMiddle
=
middleVisible
==
null
final
Widget
animatedStyledMiddle
=
middleVisible
==
null
?
styledMiddle
?
styledMiddle
...
@@ -437,23 +662,26 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
...
@@ -437,23 +662,26 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
// Auto add back button if leading not provided.
// Auto add back button if leading not provided.
Widget
backOrCloseButton
;
Widget
backOrCloseButton
;
bool
useBackButton
=
false
;
if
(
styledLeading
==
null
&&
automaticallyImplyLeading
)
{
if
(
styledLeading
==
null
&&
automaticallyImplyLeading
)
{
final
ModalRoute
<
dynamic
>
currentRoute
=
ModalRoute
.
of
(
context
);
final
ModalRoute
<
dynamic
>
currentRoute
=
ModalRoute
.
of
(
context
);
if
(
currentRoute
?.
canPop
==
true
)
{
if
(
currentRoute
?.
canPop
==
true
)
{
useBackButton
=
!(
currentRoute
is
PageRoute
&&
currentRoute
?.
fullscreenDialog
==
true
);
if
(
currentRoute
is
PageRoute
&&
currentRoute
?.
fullscreenDialog
==
true
)
{
backOrCloseButton
=
new
CupertinoButton
(
backOrCloseButton
=
new
CupertinoButton
(
child:
useBackButton
child:
const
Padding
(
?
new
Container
(
padding:
EdgeInsetsDirectional
.
only
(
height:
_kNavBarPersistentHeight
,
start:
_kNavBarEdgePadding
,
width:
_kNavBarBackButtonTapWidth
,
),
alignment:
AlignmentDirectional
.
centerStart
,
child:
Text
(
'Close'
),
child:
const
Icon
(
CupertinoIcons
.
back
,
size:
34.0
,)
),
)
padding:
EdgeInsets
.
zero
,
:
const
Text
(
'Close'
),
onPressed:
()
{
Navigator
.
maybePop
(
context
);
},
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
...
@@ -462,6 +690,7 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
middle:
animatedStyledMiddle
,
middle:
animatedStyledMiddle
,
trailing:
styledTrailing
,
trailing:
styledTrailing
,
centerMiddle:
true
,
centerMiddle:
true
,
middleSpacing:
6.0
,
);
);
if
(
padding
!=
null
)
{
if
(
padding
!=
null
)
{
...
@@ -476,143 +705,164 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
...
@@ -476,143 +705,164 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
return
new
SizedBox
(
return
new
SizedBox
(
height:
_kNavBarPersistentHeight
+
MediaQuery
.
of
(
context
).
padding
.
top
,
height:
_kNavBarPersistentHeight
+
MediaQuery
.
of
(
context
).
padding
.
top
,
child:
IconTheme
.
merge
(
child:
new
SafeArea
(
data:
new
IconThemeData
(
bottom:
false
,
color:
actionsForegroundColor
,
child:
paddedToolbar
,
size:
22.0
,
),
child:
new
SafeArea
(
bottom:
false
,
child:
paddedToolbar
,
),
),
),
);
);
}
}
}
}
class
_CupertinoLargeTitleNavigationBarSliverDelegate
/// A nav bar back button typically used in [CupertinoNavigationBar].
extends
SliverPersistentHeaderDelegate
with
DiagnosticableTreeMixin
{
///
_CupertinoLargeTitleNavigationBarSliverDelegate
({
/// This is automatically inserted into [CupertinoNavigationBar] and
@required
this
.
persistentHeight
,
/// [CupertinoSliverNavigationBar]'s `leading` slot when
@required
this
.
title
,
/// `automaticallyImplyLeading` is true.
this
.
leading
,
///
this
.
automaticallyImplyLeading
,
/// Shows a back chevron and the previous route's title when available from
this
.
middle
,
/// the previous [CupertinoPageRoute.title]. If [previousPageTitle] is specified,
this
.
trailing
,
/// it will be shown instead.
this
.
padding
,
class
CupertinoNavigationBarBackButton
extends
StatelessWidget
{
this
.
border
,
/// Construct a [CupertinoNavigationBarBackButton] that can be used to pop
this
.
backgroundColor
,
/// the current route.
this
.
actionsForegroundColor
,
///
})
:
assert
(
persistentHeight
!=
null
);
/// The [color] parameter must not be null.
const
CupertinoNavigationBarBackButton
({
final
double
persistentHeight
;
@required
this
.
color
,
this
.
previousPageTitle
,
final
Widget
title
;
})
:
assert
(
color
!=
null
);
final
Widget
leading
;
final
bool
automaticallyImplyLeading
;
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
@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
return
iconWidget
;
double
get
maxExtent
=>
persistentHeight
+
_kNavBarLargeTitleHeightExtension
;
}
}
@override
/// A widget that shows next to the back chevron when `automaticallyImplyLeading`
Widget
build
(
BuildContext
context
,
double
shrinkOffset
,
bool
overlapsContent
)
{
/// is true.
final
bool
showLargeTitle
=
shrinkOffset
<
maxExtent
-
minExtent
-
_kNavBarShowLargeTitleThreshold
;
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
=
if
(
previousTitle
.
length
>
10
)
{
new
_CupertinoPersistentNavigationBar
(
return
const
Text
(
'Back'
);
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
,
);
return
_wrapWithBackground
(
return
new
Text
(
previousTitle
,
maxLines:
1
);
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
,
),
],
),
);
}
}
@override
@override
bool
shouldRebuild
(
_CupertinoLargeTitleNavigationBarSliverDelegate
oldDelegate
)
{
Widget
build
(
BuildContext
context
)
{
return
persistentHeight
!=
oldDelegate
.
persistentHeight
if
(
specifiedPreviousTitle
!=
null
)
{
||
title
!=
oldDelegate
.
title
return
_buildPreviousTitleWidget
(
context
,
specifiedPreviousTitle
,
null
);
||
leading
!=
oldDelegate
.
leading
}
else
if
(
route
is
CupertinoPageRoute
<
dynamic
>)
{
||
middle
!=
oldDelegate
.
middle
final
CupertinoPageRoute
<
dynamic
>
cupertinoRoute
=
route
;
||
trailing
!=
oldDelegate
.
trailing
// There is no timing issue because the previousTitle Listenable changes
||
border
!=
oldDelegate
.
border
// happen during route modifications before the ValueListenableBuilder
||
backgroundColor
!=
oldDelegate
.
backgroundColor
// is built.
||
actionsForegroundColor
!=
oldDelegate
.
actionsForegroundColor
;
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 @@
...
@@ -4,6 +4,7 @@
import
'dart:async'
;
import
'dart:async'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter/widgets.dart'
;
...
@@ -87,6 +88,7 @@ class CupertinoPageRoute<T> extends PageRoute<T> {
...
@@ -87,6 +88,7 @@ class CupertinoPageRoute<T> extends PageRoute<T> {
/// be null.
/// be null.
CupertinoPageRoute
({
CupertinoPageRoute
({
@required
this
.
builder
,
@required
this
.
builder
,
this
.
title
,
RouteSettings
settings
,
RouteSettings
settings
,
this
.
maintainState
=
true
,
this
.
maintainState
=
true
,
bool
fullscreenDialog
=
false
,
bool
fullscreenDialog
=
false
,
...
@@ -102,6 +104,50 @@ class CupertinoPageRoute<T> extends PageRoute<T> {
...
@@ -102,6 +104,50 @@ class CupertinoPageRoute<T> extends PageRoute<T> {
/// Builds the primary contents of the route.
/// Builds the primary contents of the route.
final
WidgetBuilder
builder
;
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
@override
final
bool
maintainState
;
final
bool
maintainState
;
...
@@ -511,7 +557,6 @@ class _CupertinoBackGestureDetectorState<T> extends State<_CupertinoBackGestureD
...
@@ -511,7 +557,6 @@ class _CupertinoBackGestureDetectorState<T> extends State<_CupertinoBackGestureD
}
}
}
}
/// A controller for an iOS-style back gesture.
/// A controller for an iOS-style back gesture.
///
///
/// This is created by a [CupertinoPageRoute] in response from a gesture caught
/// 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 {
...
@@ -42,6 +42,7 @@ class CupertinoTabView extends StatelessWidget {
const
CupertinoTabView
({
const
CupertinoTabView
({
Key
key
,
Key
key
,
this
.
builder
,
this
.
builder
,
this
.
defaultTitle
,
this
.
routes
,
this
.
routes
,
this
.
onGenerateRoute
,
this
.
onGenerateRoute
,
this
.
onUnknownRoute
,
this
.
onUnknownRoute
,
...
@@ -56,6 +57,9 @@ class CupertinoTabView extends StatelessWidget {
...
@@ -56,6 +57,9 @@ class CupertinoTabView extends StatelessWidget {
/// as [builder] takes its place.
/// as [builder] takes its place.
final
WidgetBuilder
builder
;
final
WidgetBuilder
builder
;
/// The title of the default route.
final
String
defaultTitle
;
/// This tab view's routing table.
/// This tab view's routing table.
///
///
/// When a named route is pushed with [Navigator.pushNamed] inside this tab view,
/// When a named route is pushed with [Navigator.pushNamed] inside this tab view,
...
@@ -109,13 +113,17 @@ class CupertinoTabView extends StatelessWidget {
...
@@ -109,13 +113,17 @@ class CupertinoTabView extends StatelessWidget {
Route
<
dynamic
>
_onGenerateRoute
(
RouteSettings
settings
)
{
Route
<
dynamic
>
_onGenerateRoute
(
RouteSettings
settings
)
{
final
String
name
=
settings
.
name
;
final
String
name
=
settings
.
name
;
WidgetBuilder
routeBuilder
;
WidgetBuilder
routeBuilder
;
if
(
name
==
Navigator
.
defaultRouteName
&&
builder
!=
null
)
String
title
;
if
(
name
==
Navigator
.
defaultRouteName
&&
builder
!=
null
)
{
routeBuilder
=
builder
;
routeBuilder
=
builder
;
title
=
defaultTitle
;
}
else
if
(
routes
!=
null
)
else
if
(
routes
!=
null
)
routeBuilder
=
routes
[
name
];
routeBuilder
=
routes
[
name
];
if
(
routeBuilder
!=
null
)
{
if
(
routeBuilder
!=
null
)
{
return
new
CupertinoPageRoute
<
dynamic
>(
return
new
CupertinoPageRoute
<
dynamic
>(
builder:
routeBuilder
,
builder:
routeBuilder
,
title:
title
,
settings:
settings
,
settings:
settings
,
);
);
}
}
...
...
packages/flutter/lib/src/widgets/navigator.dart
View file @
c418b2f3
...
@@ -111,15 +111,15 @@ abstract class Route<T> {
...
@@ -111,15 +111,15 @@ abstract class Route<T> {
///
///
/// The returned value resolves when the push transition is complete.
/// The returned value resolves when the push transition is complete.
///
///
/// The [didChangeNext]
method is typically called immediately after this
/// The [didChangeNext]
and [didChangePrevious] methods are typically called
/// method is called.
///
immediately after this
method is called.
@protected
@protected
TickerFuture
didPush
()
=>
new
TickerFuture
.
complete
();
TickerFuture
didPush
()
=>
new
TickerFuture
.
complete
();
/// Called after [install] when the route replaced another in the navigator.
/// Called after [install] when the route replaced another in the navigator.
///
///
/// The [didChangeNext]
method is typically called immediately after this
/// The [didChangeNext]
and [didChangePrevious] methods are typically called
/// method is called.
///
immediately after this
method is called.
@protected
@protected
@mustCallSuper
@mustCallSuper
void
didReplace
(
Route
<
dynamic
>
oldRoute
)
{
}
void
didReplace
(
Route
<
dynamic
>
oldRoute
)
{
}
...
@@ -201,9 +201,8 @@ abstract class Route<T> {
...
@@ -201,9 +201,8 @@ abstract class Route<T> {
/// This route's previous route has changed to the given new route. This is
/// 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
/// 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
/// long as it is in the history. `previousRoute` will be null if there's no
/// been pushed (in which case [didPush] or [didReplace] will be called
/// previous route.
/// instead). `previousRoute` will be null if there's no previous route.
@protected
@protected
@mustCallSuper
@mustCallSuper
void
didChangePrevious
(
Route
<
dynamic
>
previousRoute
)
{
}
void
didChangePrevious
(
Route
<
dynamic
>
previousRoute
)
{
}
...
@@ -1539,8 +1538,10 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
...
@@ -1539,8 +1538,10 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
_history
.
add
(
route
);
_history
.
add
(
route
);
route
.
didPush
();
route
.
didPush
();
route
.
didChangeNext
(
null
);
route
.
didChangeNext
(
null
);
if
(
oldRoute
!=
null
)
if
(
oldRoute
!=
null
)
{
oldRoute
.
didChangeNext
(
route
);
oldRoute
.
didChangeNext
(
route
);
route
.
didChangePrevious
(
oldRoute
);
}
for
(
NavigatorObserver
observer
in
widget
.
observers
)
for
(
NavigatorObserver
observer
in
widget
.
observers
)
observer
.
didPush
(
route
,
oldRoute
);
observer
.
didPush
(
route
,
oldRoute
);
assert
(()
{
_debugLocked
=
false
;
return
true
;
}());
assert
(()
{
_debugLocked
=
false
;
return
true
;
}());
...
@@ -1589,8 +1590,10 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
...
@@ -1589,8 +1590,10 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
}
}
});
});
newRoute
.
didChangeNext
(
null
);
newRoute
.
didChangeNext
(
null
);
if
(
index
>
0
)
if
(
index
>
0
)
{
_history
[
index
-
1
].
didChangeNext
(
newRoute
);
_history
[
index
-
1
].
didChangeNext
(
newRoute
);
newRoute
.
didChangePrevious
(
_history
[
index
-
1
]);
}
for
(
NavigatorObserver
observer
in
widget
.
observers
)
for
(
NavigatorObserver
observer
in
widget
.
observers
)
observer
.
didReplace
(
newRoute:
newRoute
,
oldRoute:
oldRoute
);
observer
.
didReplace
(
newRoute:
newRoute
,
oldRoute:
oldRoute
);
assert
(()
{
_debugLocked
=
false
;
return
true
;
}());
assert
(()
{
_debugLocked
=
false
;
return
true
;
}());
...
@@ -1684,8 +1687,10 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
...
@@ -1684,8 +1687,10 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin {
}
else
{
}
else
{
newRoute
.
didChangeNext
(
null
);
newRoute
.
didChangeNext
(
null
);
}
}
if
(
index
>
0
)
if
(
index
>
0
)
{
_history
[
index
-
1
].
didChangeNext
(
newRoute
);
_history
[
index
-
1
].
didChangeNext
(
newRoute
);
newRoute
.
didChangePrevious
(
_history
[
index
-
1
]);
}
for
(
NavigatorObserver
observer
in
widget
.
observers
)
for
(
NavigatorObserver
observer
in
widget
.
observers
)
observer
.
didReplace
(
newRoute:
newRoute
,
oldRoute:
oldRoute
);
observer
.
didReplace
(
newRoute:
newRoute
,
oldRoute:
oldRoute
);
oldRoute
.
dispose
();
oldRoute
.
dispose
();
...
...
packages/flutter/test/cupertino/nav_bar_test.dart
View file @
c418b2f3
...
@@ -393,7 +393,7 @@ void main() {
...
@@ -393,7 +393,7 @@ void main() {
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
expect
(
find
.
byType
(
CupertinoButton
),
findsOneWidget
);
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
>(
tester
.
state
<
NavigatorState
>(
find
.
byType
(
Navigator
)).
push
(
new
CupertinoPageRoute
<
void
>(
fullscreenDialog:
true
,
fullscreenDialog:
true
,
...
@@ -418,7 +418,7 @@ void main() {
...
@@ -418,7 +418,7 @@ void main() {
expect
(
find
.
text
(
'Page 2'
),
findsOneWidget
);
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
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
200
));
...
@@ -426,6 +426,49 @@ void main() {
...
@@ -426,6 +426,49 @@ void main() {
expect
(
find
.
text
(
'Home page'
),
findsOneWidget
);
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
{
testWidgets
(
'Border should be displayed by default'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
new
CupertinoApp
(
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
...
@@ -608,7 +608,7 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
return
TestAsyncUtils
.
guard
(()
async
{
return
TestAsyncUtils
.
guard
(()
async
{
Finder
backButton
=
find
.
byTooltip
(
'Back'
);
Finder
backButton
=
find
.
byTooltip
(
'Back'
);
if
(
backButton
.
evaluate
().
isEmpty
)
{
if
(
backButton
.
evaluate
().
isEmpty
)
{
backButton
=
find
.
widgetWithIcon
(
CupertinoButton
,
CupertinoIcons
.
back
);
backButton
=
find
.
byType
(
CupertinoNavigationBarBackButton
);
}
}
expectSync
(
backButton
,
findsOneWidget
,
reason:
'One back button expected on screen'
);
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