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
0d111bc9
Unverified
Commit
0d111bc9
authored
Mar 26, 2020
by
Anthony
Committed by
GitHub
Mar 26, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[Material] Create a Navigation Rail component and theme (#49574)
parent
c9f99927
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
3580 additions
and
0 deletions
+3580
-0
material.dart
packages/flutter/lib/material.dart
+2
-0
navigation_rail.dart
packages/flutter/lib/src/material/navigation_rail.dart
+873
-0
navigation_rail_theme.dart
packages/flutter/lib/src/material/navigation_rail_theme.dart
+215
-0
theme_data.dart
packages/flutter/lib/src/material/theme_data.dart
+16
-0
navigation_rail_test.dart
packages/flutter/test/material/navigation_rail_test.dart
+2151
-0
navigation_rail_theme_test.dart
...ges/flutter/test/material/navigation_rail_theme_test.dart
+319
-0
theme_data_test.dart
packages/flutter/test/material/theme_data_test.dart
+4
-0
No files found.
packages/flutter/lib/material.dart
View file @
0d111bc9
...
...
@@ -81,6 +81,8 @@ export 'src/material/material_button.dart';
export
'src/material/material_localizations.dart'
;
export
'src/material/material_state.dart'
;
export
'src/material/mergeable_material.dart'
;
export
'src/material/navigation_rail.dart'
;
export
'src/material/navigation_rail_theme.dart'
;
export
'src/material/outline_button.dart'
;
export
'src/material/page.dart'
;
export
'src/material/page_transitions_theme.dart'
;
...
...
packages/flutter/lib/src/material/navigation_rail.dart
0 → 100644
View file @
0d111bc9
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:ui'
;
import
'package:flutter/widgets.dart'
;
import
'../../scheduler.dart'
;
import
'color_scheme.dart'
;
import
'ink_well.dart'
;
import
'material.dart'
;
import
'material_localizations.dart'
;
import
'navigation_rail_theme.dart'
;
import
'theme.dart'
;
import
'theme_data.dart'
;
/// A material widget that is meant to be displayed at the left or right of an
/// app to navigate between a small number of views, typically between three and
/// five.
///
/// A navigation rail is usually used as the first or last element of a [Row]
/// which defines the app's [Scaffold] body.
///
/// The appearance of all of the [NavigationRail]s within an app can be
/// specified with [NavigationRailTheme]. The default values for null theme
/// properties are based on the [Theme]'s [ThemeData.textTheme],
/// [ThemeData.iconTheme], and [ThemeData.colorScheme].
//
/// The navigation rail is meant for layouts with wide viewports, such as a
/// desktop web or tablet landscape layout. For smaller layouts, like mobile
/// portrait, a [BottomNavigationBar] should be used instead.
///
/// Adaptive layouts can build different instances of the [Scaffold] in order to
/// have a navigation rail for more horizontal layouts and a bottom navigation
/// bar for more vertical layouts. See
/// [https://github.com/flutter/samples/blob/master/experimental/web_dashboard/lib/src/widgets/third_party/adaptive_scaffold.dart]
/// for an example.
///
/// {@tool dartpad --template=stateful_widget_material}
///
/// This example shows a [NavigationRail] used within a Scaffold with 3
/// [NavigationRailDestination]s. The main content is separated by a divider
/// (although elevation on the navigation rail can be used instead). The
/// `_selectedIndex` is updated by the `onDestinationSelected` callback.
///
/// ```dart
/// int _selectedIndex = 0;
///
/// @override
/// Widget build(BuildContext context) {
/// return Scaffold(
/// body: Row(
/// children: <Widget>[
/// NavigationRail(
/// selectedIndex: _selectedIndex,
/// onDestinationSelected: (int index) {
/// setState(() {
/// _selectedIndex = index;
/// });
/// },
/// labelType: NavigationRailLabelType.selected,
/// destinations: [
/// NavigationRailDestination(
/// icon: Icon(Icons.favorite_border),
/// selectedIcon: Icon(Icons.favorite),
/// label: Text('First'),
/// ),
/// NavigationRailDestination(
/// icon: Icon(Icons.bookmark_border),
/// selectedIcon: Icon(Icons.book),
/// label: Text('Second'),
/// ),
/// NavigationRailDestination(
/// icon: Icon(Icons.star_border),
/// selectedIcon: Icon(Icons.star),
/// label: Text('Third'),
/// ),
/// ],
/// ),
/// VerticalDivider(thickness: 1, width: 1),
/// // This is the main content.
/// Expanded(
/// child: Center(
/// child: Text('selectedIndex: $_selectedIndex'),
/// ),
/// )
/// ],
/// ),
/// );
/// }
/// ```
/// {@end-tool}
///
/// See also:
///
/// * [Scaffold], which can display the navigation rail within a [Row] of the
/// [Scaffold.body] slot.
/// * [NavigationRailDestination], which is used as a model to create tappable
/// destinations in the navigation rail.
/// * [BottomNavigationBar], which is a similar navigation widget that's laid
/// out horizontally.
/// * [https://material.io/components/navigation-rail/]
class
NavigationRail
extends
StatefulWidget
{
/// Creates a material design navigation rail.
///
/// The value of [destinations] must be a list of one or more
/// [NavigationRailDestination] values.
///
/// If [elevation] is specified, it must be non-negative.
///
/// If [minWidth] is specified, it must be non-negative, and if
/// [minExtendedWidth] is specified, it must be non-negative and greater than
/// [minWidth].
///
/// The argument [extended] must not be null. [extended] can only be set to
/// true when when the [labelType] is null or [NavigationRailLabelType.none].
///
/// If [backgroundColor], [elevation], [groupAlignment], [labelType],
/// [unselectedLabelTextStyle], [unselectedLabelTextStyle],
/// [unselectedIconTheme], or [selectedIconTheme] are null, then their
/// [NavigationRailThemeData] values will be used. If the corresponding
/// [NavigationRailThemeData] property is null, then the navigation rail
/// defaults are used. See the individual properties for more information.
///
/// Typically used within a [Row] that defines the [Scaffold.body] property.
const
NavigationRail
({
this
.
backgroundColor
,
this
.
extended
=
false
,
this
.
leading
,
this
.
trailing
,
@required
this
.
destinations
,
@required
this
.
selectedIndex
,
this
.
onDestinationSelected
,
this
.
elevation
,
this
.
groupAlignment
,
this
.
labelType
,
this
.
unselectedLabelTextStyle
,
this
.
selectedLabelTextStyle
,
this
.
unselectedIconTheme
,
this
.
selectedIconTheme
,
this
.
minWidth
,
this
.
minExtendedWidth
,
})
:
assert
(
destinations
!=
null
&&
destinations
.
length
>=
2
),
assert
(
selectedIndex
!=
null
),
assert
(
0
<=
selectedIndex
&&
selectedIndex
<
destinations
.
length
),
assert
(
elevation
==
null
||
elevation
>
0
),
assert
(
minWidth
==
null
||
minWidth
>
0
),
assert
(
minExtendedWidth
==
null
||
minExtendedWidth
>
0
),
assert
((
minWidth
==
null
||
minExtendedWidth
==
null
)
||
minExtendedWidth
>=
minWidth
),
assert
(
extended
!=
null
),
assert
(!
extended
||
(
labelType
==
null
||
labelType
==
NavigationRailLabelType
.
none
));
/// Sets the color of the Container that holds all of the [NavigationRail]'s
/// contents.
///
/// The default value is [NavigationRailThemeData.backgroundColor]. If
/// [NavigationRailThemeData.backgroundColor] is null, then the default value
/// is based on [ThemeData.colorScheme.surface].
final
Color
backgroundColor
;
/// Indicates that the [NavigationRail] should be in the extended state.
///
/// The extended state has a wider rail container, and the labels are
/// positioned next to the icons. [minExtendedWidth] can be used to set the
/// the minimum width of the rail when it is in this state.
///
/// The rail will implicitly animate between the extended and normal state.
///
/// If the rail is going to be in the extended state, then the [labelType]
/// must be set to [NavigationRailLabelType.none].
///
/// The default value is false.
final
bool
extended
;
/// The leading widget in the rail that is placed above the destinations.
///
/// It is placed at the top of the rail, above the [destinations]. Its
/// location is not affected by [groupAlignment].
///
/// This is commonly a [FloatingActionButton], but may also be a non-button,
/// such as a logo.
///
/// The default value is null.
final
Widget
leading
;
/// The trailing widget in the rail that is placed below the destinations.
///
/// The trailing widget is placed below the last [NavigationRailDestination].
/// It's location is affected by [groupAlignment].
///
/// This is commonly a list of additional options or destinations that is
/// usually only rendered when [extended] is true.
///
/// The default value is null.
final
Widget
trailing
;
/// Defines the appearance of the button items that are arrayed within the
/// navigation rail.
///
/// The value must be a list of two or more [NavigationRailDestination]
/// values.
final
List
<
NavigationRailDestination
>
destinations
;
/// The index into [destinations] for the current selected
/// [NavigationRailDestination].
final
int
selectedIndex
;
/// Called when one of the [destinations] is selected.
///
/// The stateful widget that creates the navigation rail needs to keep
/// track of the index of the selected [NavigationRailDestination] and call
/// `setState` to rebuild the navigation rail with the new [selectedIndex].
final
ValueChanged
<
int
>
onDestinationSelected
;
/// The rail's elevation or z-coordinate.
///
/// If [Directionality] is [TextDirection.LTR], the inner side is the right
/// side, and if [Directionality] is [TextDirection.RTL], it is the left side.
///
/// The default value is 0.
final
double
elevation
;
/// The vertical alignment for the group of [destinations] within the rail.
///
/// The [NavigationRailDestination]s are grouped together with the [trailing]
/// widget, between the [leading] widget and the bottom of the rail.
///
/// The value must be between -1.0 and 1.0.
///
/// If [groupAlignment] is -1.0, then the items are aligned to the top. If
/// [groupAlignment] is 0.0, then the items are aligned to the center. If
/// [groupAlignment] is 1.0, then the items are aligned to the bottom.
///
/// The default is -1.0.
///
/// See also:
/// * [Alignment.y]
///
final
double
groupAlignment
;
/// Defines the layout and behavior of the labels for the default, unextended
/// [NavigationRail].
///
/// When a navigation rail is [extended], the labels are always shown.
///
/// The default value is [NavigationRailThemeData.labelType]. If
/// [NavigationRailThemeData.labelType] is null, then the default value is
/// [NavigationRailLabelType.none].
///
/// See also:
///
/// * [NavigationRailLabelType] for information on the meaning of different
/// types.
final
NavigationRailLabelType
labelType
;
/// The [TextStyle] of a destination's label when it is unselected.
///
/// When one of the [destinations] is selected the [selectedLabelTextStyle]
/// will be used instead.
///
/// The default value is based on the [Theme]'s
/// [ThemeData.textTheme.bodyText]. The default color is based on the
/// [Theme]'s [ColorScheme.onSurface].
///
/// Properties from this text style, or
/// [NavigationRailThemeData.unselectedLabelTextStyle] if this is null, are
/// merged into the defaults.
final
TextStyle
unselectedLabelTextStyle
;
/// The [TextStyle] of a destination's label when it is selected.
///
/// When a [NavigationRailDestination] is not selected,
/// [unselectedLabelTextStyle] will be used.
///
/// The default value is based on the [Theme]'s
/// [ThemeData.textTheme.bodyText]. The default color is based on the
/// [Theme]'s [ColorScheme.primary].
///
/// Properties from this text style,
/// or [NavigationRailThemeData.selectedLabelTextStyle] if this is null, are
/// merged into the defaults.
final
TextStyle
selectedLabelTextStyle
;
/// The visual properties of the icon in the unselected destination.
///
/// If this field is not provided, or provided with any null properties, then
/// a copy of the [IconThemeData.fallback] with a custom [NavigationRail]
/// specific color will be used.
///
/// The default value is Is the [Theme]'s [ThemeData.iconTheme] with a color
/// of the [Theme]'s [ColorScheme.onSurface] with an opacity of 0.64.
/// Properties from this icon theme, or
/// [NavigationRailThemeData.unselectedIconTheme] if this is null, are
/// merged into the defaults.
final
IconThemeData
unselectedIconTheme
;
/// The visual properties of the icon in the selected destination.
///
/// When a [NavigationRailDestination] is not selected,
/// [unselectedIconTheme] will be used.
///
/// The default value is Is the [Theme]'s [ThemeData.iconTheme] with a color
/// of the [Theme]'s [ColorScheme.primary]. Properties from this icon theme,
/// or [NavigationRailThemeData.selectedIconTheme] if this is null, are
/// merged into the defaults.
final
IconThemeData
selectedIconTheme
;
/// The smallest possible width for the rail regardless of the destination's
/// icon or label size.
///
/// The default is 72.
///
/// This value also defines the min width and min height of the destinations.
///
/// To make a compact rail, set this to 56 and use
/// [NavigationRailLabelType.none].
final
double
minWidth
;
/// The final width when the animation is complete for setting [extended] to
/// true.
///
/// This is only used when [extended] is set to true.
///
/// The default value is 256.
final
double
minExtendedWidth
;
/// Returns the animation that controls the [NavigationRail.extended] state.
///
/// This can be used to synchronize animations in the [leading] or [trailing]
/// widget, such as an animated menu or a [FloatingActionButton] animation.
///
/// {@tool snippet}
///
/// This example shows how to use this animation to create a
/// [FloatingActionButton] that animates itself between the normal and
/// extended states of the [NavigationRail].
///
/// An instance of `ExtendableFab` would be created for
/// [NavigationRail.leading].
///
/// ```dart
/// import 'dart:ui';
///
/// @override
/// Widget build(BuildContext context) {
/// final Animation<double> animation = NavigationRail.extendedAnimation(context);
/// return AnimatedBuilder(
/// animation: animation,
/// builder: (BuildContext context, Widget child) {
/// // The extended fab has a shorter height than the regular fab.
/// return Container(
/// height: 56,
/// padding: EdgeInsets.symmetric(
/// vertical: lerpDouble(0, 6, animation.value),
/// ),
/// child: animation.value == 0
/// ? FloatingActionButton(
/// child: Icon(Icons.add),
/// onPressed: () {},
/// )
/// : Align(
/// alignment: AlignmentDirectional.centerStart,
/// widthFactor: animation.value,
/// child: Padding(
/// padding: const EdgeInsetsDirectional.only(start: 8),
/// child: FloatingActionButton.extended(
/// icon: Icon(Icons.add),
/// label: Text('CREATE'),
/// onPressed: () {},
/// ),
/// ),
/// ),
/// );
/// },
/// );
/// }
/// ```
///
/// {@end-tool}
static
Animation
<
double
>
extendedAnimation
(
BuildContext
context
)
{
return
context
.
dependOnInheritedWidgetOfExactType
<
_ExtendedNavigationRailAnimation
>().
animation
;
}
@override
_NavigationRailState
createState
()
=>
_NavigationRailState
();
}
class
_NavigationRailState
extends
State
<
NavigationRail
>
with
TickerProviderStateMixin
{
List
<
AnimationController
>
_destinationControllers
=
<
AnimationController
>[];
List
<
Animation
<
double
>>
_destinationAnimations
;
AnimationController
_extendedController
;
Animation
<
double
>
_extendedAnimation
;
@override
void
initState
()
{
super
.
initState
();
_initControllers
();
}
@override
void
dispose
()
{
_disposeControllers
();
super
.
dispose
();
}
@override
void
didUpdateWidget
(
NavigationRail
oldWidget
)
{
super
.
didUpdateWidget
(
oldWidget
);
if
(
widget
.
extended
!=
oldWidget
.
extended
)
{
if
(
widget
.
extended
)
{
_extendedController
.
forward
();
}
else
{
_extendedController
.
reverse
();
}
}
// No animated segue if the length of the items list changes.
if
(
widget
.
destinations
.
length
!=
oldWidget
.
destinations
.
length
)
{
_resetState
();
return
;
}
if
(
widget
.
selectedIndex
!=
oldWidget
.
selectedIndex
)
{
_destinationControllers
[
oldWidget
.
selectedIndex
].
reverse
();
_destinationControllers
[
widget
.
selectedIndex
].
forward
();
return
;
}
}
@override
Widget
build
(
BuildContext
context
)
{
final
ThemeData
theme
=
Theme
.
of
(
context
);
final
NavigationRailThemeData
navigationRailTheme
=
NavigationRailTheme
.
of
(
context
);
final
MaterialLocalizations
localizations
=
MaterialLocalizations
.
of
(
context
);
final
Color
backgroundColor
=
widget
.
backgroundColor
??
navigationRailTheme
.
backgroundColor
??
theme
.
colorScheme
.
surface
;
final
double
elevation
=
widget
.
elevation
??
navigationRailTheme
.
elevation
??
0
;
final
double
minWidth
=
widget
.
minWidth
??
_minRailWidth
;
final
double
minExtendedWidth
=
widget
.
minExtendedWidth
??
_minExtendedRailWidth
;
final
Color
baseSelectedColor
=
theme
.
colorScheme
.
primary
;
final
Color
baseColor
=
theme
.
colorScheme
.
onSurface
.
withOpacity
(
0.64
);
final
IconThemeData
defaultUnselectedIconTheme
=
widget
.
unselectedIconTheme
??
navigationRailTheme
.
unselectedIconTheme
;
final
IconThemeData
unselectedIconTheme
=
IconThemeData
(
size:
defaultUnselectedIconTheme
?.
size
??
24.0
,
color:
defaultUnselectedIconTheme
?.
color
??
theme
.
colorScheme
.
onSurface
,
opacity:
defaultUnselectedIconTheme
?.
opacity
??
1.0
,
);
final
IconThemeData
defaultSelectedIconTheme
=
widget
.
selectedIconTheme
??
navigationRailTheme
.
selectedIconTheme
;
final
IconThemeData
selectedIconTheme
=
IconThemeData
(
size:
defaultSelectedIconTheme
?.
size
??
24.0
,
color:
defaultSelectedIconTheme
?.
color
??
theme
.
colorScheme
.
primary
,
opacity:
defaultSelectedIconTheme
?.
opacity
??
0.64
,
);
final
TextStyle
unselectedLabelTextStyle
=
theme
.
textTheme
.
bodyText1
.
copyWith
(
color:
baseColor
).
merge
(
widget
.
unselectedLabelTextStyle
??
navigationRailTheme
.
unselectedLabelTextStyle
);
final
TextStyle
selectedLabelTextStyle
=
theme
.
textTheme
.
bodyText1
.
copyWith
(
color:
baseSelectedColor
).
merge
(
widget
.
selectedLabelTextStyle
??
navigationRailTheme
.
selectedLabelTextStyle
);
final
double
groupAlignment
=
widget
.
groupAlignment
??
navigationRailTheme
.
groupAlignment
??
-
1.0
;
final
NavigationRailLabelType
labelType
=
widget
.
labelType
??
navigationRailTheme
.
labelType
??
NavigationRailLabelType
.
none
;
return
_ExtendedNavigationRailAnimation
(
animation:
_extendedAnimation
,
child:
Semantics
(
explicitChildNodes:
true
,
child:
Material
(
elevation:
elevation
,
color:
backgroundColor
,
child:
Column
(
children:
<
Widget
>[
_verticalSpacer
,
if
(
widget
.
leading
!=
null
)
...<
Widget
>[
ConstrainedBox
(
constraints:
BoxConstraints
(
minWidth:
lerpDouble
(
minWidth
,
minExtendedWidth
,
_extendedAnimation
.
value
),
),
child:
widget
.
leading
,
),
_verticalSpacer
,
],
Expanded
(
child:
Align
(
alignment:
Alignment
(
0
,
groupAlignment
),
child:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
children:
<
Widget
>[
for
(
int
i
=
0
;
i
<
widget
.
destinations
.
length
;
i
+=
1
)
_RailDestination
(
minWidth:
minWidth
,
minExtendedWidth:
minExtendedWidth
,
extendedTransitionAnimation:
_extendedAnimation
,
selected:
widget
.
selectedIndex
==
i
,
icon:
widget
.
selectedIndex
==
i
?
widget
.
destinations
[
i
].
selectedIcon
:
widget
.
destinations
[
i
].
icon
,
label:
widget
.
destinations
[
i
].
label
,
destinationAnimation:
_destinationAnimations
[
i
],
labelType:
labelType
,
iconTheme:
widget
.
selectedIndex
==
i
?
selectedIconTheme
:
unselectedIconTheme
,
labelTextStyle:
widget
.
selectedIndex
==
i
?
selectedLabelTextStyle
:
unselectedLabelTextStyle
,
onTap:
()
{
widget
.
onDestinationSelected
(
i
);
},
indexLabel:
localizations
.
tabLabel
(
tabIndex:
i
+
1
,
tabCount:
widget
.
destinations
.
length
,
),
),
if
(
widget
.
trailing
!=
null
)
ConstrainedBox
(
constraints:
BoxConstraints
(
minWidth:
lerpDouble
(
minWidth
,
minExtendedWidth
,
_extendedAnimation
.
value
),
),
child:
widget
.
trailing
,
),
],
),
),
),
],
),
),
),
);
}
void
_disposeControllers
()
{
for
(
final
AnimationController
controller
in
_destinationControllers
)
{
controller
.
dispose
();
}
_extendedController
.
dispose
();
}
void
_initControllers
()
{
_destinationControllers
=
List
<
AnimationController
>.
generate
(
widget
.
destinations
.
length
,
(
int
index
)
{
return
AnimationController
(
duration:
kThemeAnimationDuration
,
vsync:
this
,
)..
addListener
(
_rebuild
);
});
_destinationAnimations
=
_destinationControllers
.
map
((
AnimationController
controller
)
=>
controller
.
view
).
toList
();
_destinationControllers
[
widget
.
selectedIndex
].
value
=
1.0
;
_extendedController
=
AnimationController
(
duration:
kThemeAnimationDuration
,
vsync:
this
,
value:
widget
.
extended
?
1.0
:
0.0
,
);
_extendedAnimation
=
CurvedAnimation
(
parent:
_extendedController
,
curve:
Curves
.
easeInOut
,
);
_extendedController
.
addListener
(()
{
_rebuild
();
});
}
void
_resetState
()
{
_disposeControllers
();
_initControllers
();
}
void
_rebuild
()
{
setState
(()
{
// Rebuilding when any of the controllers tick, i.e. when the items are
// animating.
});
}
}
class
_RailDestination
extends
StatelessWidget
{
_RailDestination
({
@required
this
.
minWidth
,
@required
this
.
minExtendedWidth
,
@required
this
.
icon
,
@required
this
.
label
,
@required
this
.
destinationAnimation
,
@required
this
.
extendedTransitionAnimation
,
@required
this
.
labelType
,
@required
this
.
selected
,
@required
this
.
iconTheme
,
@required
this
.
labelTextStyle
,
@required
this
.
onTap
,
@required
this
.
indexLabel
,
})
:
assert
(
minWidth
!=
null
),
assert
(
minExtendedWidth
!=
null
),
assert
(
icon
!=
null
),
assert
(
label
!=
null
),
assert
(
destinationAnimation
!=
null
),
assert
(
extendedTransitionAnimation
!=
null
),
assert
(
labelType
!=
null
),
assert
(
selected
!=
null
),
assert
(
iconTheme
!=
null
),
assert
(
labelTextStyle
!=
null
),
assert
(
onTap
!=
null
),
assert
(
indexLabel
!=
null
),
_positionAnimation
=
CurvedAnimation
(
parent:
ReverseAnimation
(
destinationAnimation
),
curve:
Curves
.
easeInOut
,
reverseCurve:
Curves
.
easeInOut
.
flipped
,
);
final
double
minWidth
;
final
double
minExtendedWidth
;
final
Widget
icon
;
final
Widget
label
;
final
Animation
<
double
>
destinationAnimation
;
final
NavigationRailLabelType
labelType
;
final
bool
selected
;
final
Animation
<
double
>
extendedTransitionAnimation
;
final
IconThemeData
iconTheme
;
final
TextStyle
labelTextStyle
;
final
VoidCallback
onTap
;
final
String
indexLabel
;
final
Animation
<
double
>
_positionAnimation
;
@override
Widget
build
(
BuildContext
context
)
{
final
Widget
themedIcon
=
IconTheme
(
data:
iconTheme
,
child:
icon
,
);
final
Widget
styledLabel
=
DefaultTextStyle
(
style:
labelTextStyle
,
child:
label
,
);
Widget
content
;
switch
(
labelType
)
{
case
NavigationRailLabelType
.
none
:
final
Widget
iconPart
=
SizedBox
(
width:
minWidth
,
height:
minWidth
,
child:
Align
(
alignment:
Alignment
.
center
,
child:
themedIcon
,
),
);
if
(
extendedTransitionAnimation
.
value
==
0
)
{
content
=
Stack
(
children:
<
Widget
>[
iconPart
,
// For semantics when label is not showing,
SizedBox
(
width:
0
,
height:
0
,
child:
Opacity
(
alwaysIncludeSemantics:
true
,
opacity:
0.0
,
child:
label
,
),
),
]
);
}
else
{
content
=
ConstrainedBox
(
constraints:
BoxConstraints
(
minWidth:
lerpDouble
(
minWidth
,
minExtendedWidth
,
extendedTransitionAnimation
.
value
),
),
child:
ClipRect
(
child:
Row
(
children:
<
Widget
>[
iconPart
,
Align
(
heightFactor:
1.0
,
widthFactor:
extendedTransitionAnimation
.
value
,
alignment:
AlignmentDirectional
.
centerStart
,
child:
Opacity
(
alwaysIncludeSemantics:
true
,
opacity:
_extendedLabelFadeValue
(),
child:
styledLabel
,
),
),
const
SizedBox
(
width:
_horizontalDestinationPadding
),
],
),
),
);
}
break
;
case
NavigationRailLabelType
.
selected
:
final
double
appearingAnimationValue
=
1
-
_positionAnimation
.
value
;
final
double
verticalPadding
=
lerpDouble
(
_verticalDestinationPaddingNoLabel
,
_verticalDestinationPaddingWithLabel
,
appearingAnimationValue
);
content
=
Container
(
constraints:
BoxConstraints
(
minWidth:
minWidth
,
minHeight:
minWidth
,
),
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
_horizontalDestinationPadding
),
child:
ClipRect
(
child:
Column
(
mainAxisSize:
MainAxisSize
.
min
,
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
<
Widget
>[
SizedBox
(
height:
verticalPadding
),
themedIcon
,
Align
(
alignment:
Alignment
.
topCenter
,
heightFactor:
appearingAnimationValue
,
widthFactor:
1.0
,
child:
Opacity
(
alwaysIncludeSemantics:
true
,
opacity:
selected
?
_normalLabelFadeInValue
()
:
_normalLabelFadeOutValue
(),
child:
styledLabel
,
),
),
SizedBox
(
height:
verticalPadding
),
],
),
),
);
break
;
case
NavigationRailLabelType
.
all
:
content
=
Container
(
constraints:
BoxConstraints
(
minWidth:
minWidth
,
minHeight:
minWidth
,
),
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
_horizontalDestinationPadding
),
child:
Column
(
children:
<
Widget
>[
const
SizedBox
(
height:
_verticalDestinationPaddingWithLabel
),
themedIcon
,
styledLabel
,
const
SizedBox
(
height:
_verticalDestinationPaddingWithLabel
),
],
),
);
break
;
}
final
ColorScheme
colors
=
Theme
.
of
(
context
).
colorScheme
;
return
Semantics
(
container:
true
,
selected:
selected
,
child:
Stack
(
children:
<
Widget
>[
Material
(
type:
MaterialType
.
transparency
,
clipBehavior:
Clip
.
none
,
child:
InkResponse
(
onTap:
onTap
,
onHover:
(
_
)
{},
highlightShape:
BoxShape
.
rectangle
,
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
minWidth
/
2.0
)),
containedInkWell:
true
,
splashColor:
colors
.
primary
.
withOpacity
(
0.12
),
hoverColor:
colors
.
primary
.
withOpacity
(
0.04
),
child:
content
,
),
),
Semantics
(
label:
indexLabel
,
),
]
),
);
}
double
_normalLabelFadeInValue
()
{
if
(
destinationAnimation
.
value
<
0.25
)
{
return
0
;
}
else
if
(
destinationAnimation
.
value
<
0.75
)
{
return
(
destinationAnimation
.
value
-
0.25
)
*
2
;
}
else
{
return
1
;
}
}
double
_normalLabelFadeOutValue
()
{
if
(
destinationAnimation
.
value
>
0.75
)
{
return
(
destinationAnimation
.
value
-
0.75
)
*
4.0
;
}
else
{
return
0
;
}
}
double
_extendedLabelFadeValue
()
{
return
extendedTransitionAnimation
.
value
<
0.25
?
extendedTransitionAnimation
.
value
*
4.0
:
1.0
;
}
}
/// Defines the behavior of the labels of a [NavigationRail].
///
/// See also:
///
/// * [NavigationRail]
enum
NavigationRailLabelType
{
/// Only the [NavigationRailDestination]s are shown.
none
,
/// Only the selected [NavigationRailDestination] will show its label.
///
/// The label will animate in and out as new [NavigationRailDestination]s are
/// selected.
selected
,
/// All [NavigationRailDestination]s will show their label.
all
,
}
/// Defines a [NavigationRail] button that represents one "destination" view.
///
/// See also:
///
/// * [NavigationRail]
class
NavigationRailDestination
{
/// Creates a destination that is used with [NavigationRail.destinations].
///
/// [icon] and [label] must be non-null. When the [NavigationRail.labelType]
/// is [NavigationRailLabelType.none], the label is still used for semantics,
/// and may still be used if [NavigationRail.extended] is true.
const
NavigationRailDestination
({
@required
this
.
icon
,
Widget
selectedIcon
,
this
.
label
,
})
:
selectedIcon
=
selectedIcon
??
icon
,
assert
(
icon
!=
null
);
/// The icon of the destination.
///
/// Typically the icon is an [Icon] or an [ImageIcon] widget. If another type
/// of widget is provided then it should configure itself to match the current
/// [IconTheme] size and color.
///
/// If [selectedIcon] is provided, this will only be displayed when the
/// destination is not selected.
///
/// To make the [NavigationRail] more accessible, consider choosing an
/// icon with a stroked and filled version, such as [Icons.cloud] and
/// [Icons.cloud_queue]. The [icon] should be set to the stroked version and
/// [selectedIcon] to the filled version.
final
Widget
icon
;
/// An alternative icon displayed when this destination is selected.
///
/// If this icon is not provided, the [NavigationRail] will display [icon] in
/// either state. The size, color, and opacity of the
/// [NavigationRail.selectedIconTheme] will still apply.
///
/// See also:
///
/// * [NavigationRailDestination.icon], for a description of how to pair
/// icons.
final
Widget
selectedIcon
;
/// The label for the destination.
///
/// The label must be provided when used with the [NavigationRail]. When the
/// [NavigationRail.labelType] is [NavigationRailLabelType.none], the label is
/// still used for semantics, and may still be used if
/// [NavigationRail.extended] is true.
final
Widget
label
;
}
class
_ExtendedNavigationRailAnimation
extends
InheritedWidget
{
const
_ExtendedNavigationRailAnimation
({
Key
key
,
@required
this
.
animation
,
@required
Widget
child
,
})
:
assert
(
child
!=
null
),
super
(
key:
key
,
child:
child
);
final
Animation
<
double
>
animation
;
@override
bool
updateShouldNotify
(
_ExtendedNavigationRailAnimation
old
)
=>
animation
!=
old
.
animation
;
}
const
double
_minRailWidth
=
72.0
;
const
double
_minExtendedRailWidth
=
256.0
;
const
double
_horizontalDestinationPadding
=
8.0
;
const
double
_verticalDestinationPaddingNoLabel
=
24.0
;
const
double
_verticalDestinationPaddingWithLabel
=
16.0
;
const
Widget
_verticalSpacer
=
SizedBox
(
height:
8.0
);
packages/flutter/lib/src/material/navigation_rail_theme.dart
0 → 100644
View file @
0d111bc9
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:ui'
show
lerpDouble
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
import
'navigation_rail.dart'
;
import
'theme.dart'
;
import
'theme_data.dart'
;
/// Defines default property values for descendant [NavigationRail]
/// widgets.
///
/// Descendant widgets obtain the current [NavigationRailThemeData] object
/// using `NavigationRailTheme.of(context)`. Instances of
/// [NavigationRailThemeData] can be customized with
/// [NavigationRailThemeData.copyWith].
///
/// Typically a [NavigationRailThemeData] is specified as part of the
/// overall [Theme] with [ThemeData.navigationRailTheme].
///
/// All [NavigationRailThemeData] properties are `null` by default.
/// When null, the [NavigationRail] will use the values from [ThemeData]
/// if they exist, otherwise it will provide its own defaults based on the
/// overall [Theme]'s textTheme and colorScheme. See the individual
/// [NavigationRail] properties for details.
///
/// See also:
///
/// * [ThemeData], which describes the overall theme information for the
/// application.
class
NavigationRailThemeData
with
Diagnosticable
{
/// Creates a theme that can be used for [ThemeData.navigationRailTheme].
const
NavigationRailThemeData
({
this
.
backgroundColor
,
this
.
elevation
,
this
.
unselectedLabelTextStyle
,
this
.
selectedLabelTextStyle
,
this
.
unselectedIconTheme
,
this
.
selectedIconTheme
,
this
.
groupAlignment
,
this
.
labelType
,
});
/// Color to be used for the [NavigationRail]'s background.
final
Color
backgroundColor
;
/// The z-coordinate to be used for the [NavigationRail]'s elevation.
final
double
elevation
;
/// The style to merge with the default text style for
/// [NavigationRailDestination] labels, when the destination is not selected.
final
TextStyle
unselectedLabelTextStyle
;
/// The style to merge with the default text style for
/// [NavigationRailDestination] labels, when the destination is selected.
final
TextStyle
selectedLabelTextStyle
;
/// The theme to merge with the default icon theme for
/// [NavigationRailDestination] icons, when the destination is not selected.
final
IconThemeData
unselectedIconTheme
;
/// The theme to merge with the default icon theme for
/// [NavigationRailDestination] icons, when the destination is selected.
final
IconThemeData
selectedIconTheme
;
/// The alignment for the [NavigationRailDestination]s as they are positioned
/// within the [NavigationRail].
final
double
groupAlignment
;
/// The type that defines the layout and behavior of the labels in the
/// [NavigationRail].
final
NavigationRailLabelType
labelType
;
/// Creates a copy of this object with the given fields replaced with the
/// new values.
NavigationRailThemeData
copyWith
({
Color
backgroundColor
,
double
elevation
,
TextStyle
unselectedLabelTextStyle
,
TextStyle
selectedLabelTextStyle
,
IconThemeData
unselectedIconTheme
,
IconThemeData
selectedIconTheme
,
double
groupAlignment
,
NavigationRailLabelType
labelType
,
})
{
return
NavigationRailThemeData
(
backgroundColor:
backgroundColor
??
this
.
backgroundColor
,
elevation:
elevation
??
this
.
elevation
,
unselectedLabelTextStyle:
unselectedLabelTextStyle
??
this
.
unselectedLabelTextStyle
,
selectedLabelTextStyle:
selectedLabelTextStyle
??
this
.
selectedLabelTextStyle
,
unselectedIconTheme:
unselectedIconTheme
??
this
.
unselectedIconTheme
,
selectedIconTheme:
selectedIconTheme
??
this
.
selectedIconTheme
,
groupAlignment:
groupAlignment
??
this
.
groupAlignment
,
labelType:
labelType
??
this
.
labelType
,
);
}
/// Linearly interpolate between two navigation rail themes.
///
/// If both arguments are null then null is returned.
///
/// {@macro dart.ui.shadow.lerp}
static
NavigationRailThemeData
lerp
(
NavigationRailThemeData
a
,
NavigationRailThemeData
b
,
double
t
)
{
assert
(
t
!=
null
);
if
(
a
==
null
&&
b
==
null
)
return
null
;
return
NavigationRailThemeData
(
backgroundColor:
Color
.
lerp
(
a
?.
backgroundColor
,
b
?.
backgroundColor
,
t
),
elevation:
lerpDouble
(
a
?.
elevation
,
b
?.
elevation
,
t
),
unselectedLabelTextStyle:
TextStyle
.
lerp
(
a
?.
unselectedLabelTextStyle
,
b
?.
unselectedLabelTextStyle
,
t
),
selectedLabelTextStyle:
TextStyle
.
lerp
(
a
?.
selectedLabelTextStyle
,
b
?.
selectedLabelTextStyle
,
t
),
unselectedIconTheme:
IconThemeData
.
lerp
(
a
?.
unselectedIconTheme
,
b
?.
unselectedIconTheme
,
t
),
selectedIconTheme:
IconThemeData
.
lerp
(
a
?.
selectedIconTheme
,
b
?.
selectedIconTheme
,
t
),
groupAlignment:
lerpDouble
(
a
?.
groupAlignment
,
b
?.
groupAlignment
,
t
),
labelType:
t
<
0.5
?
a
?.
labelType
:
b
?.
labelType
,
);
}
@override
int
get
hashCode
{
return
hashValues
(
backgroundColor
,
elevation
,
unselectedLabelTextStyle
,
selectedLabelTextStyle
,
unselectedIconTheme
,
selectedIconTheme
,
groupAlignment
,
labelType
,
);
}
@override
bool
operator
==(
Object
other
)
{
if
(
identical
(
this
,
other
))
return
true
;
if
(
other
.
runtimeType
!=
runtimeType
)
return
false
;
return
other
is
NavigationRailThemeData
&&
other
.
backgroundColor
==
backgroundColor
&&
other
.
elevation
==
elevation
&&
other
.
unselectedLabelTextStyle
==
unselectedLabelTextStyle
&&
other
.
selectedLabelTextStyle
==
selectedLabelTextStyle
&&
other
.
unselectedIconTheme
==
unselectedIconTheme
&&
other
.
selectedIconTheme
==
selectedIconTheme
&&
other
.
groupAlignment
==
groupAlignment
&&
other
.
labelType
==
labelType
;
}
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
super
.
debugFillProperties
(
properties
);
const
NavigationRailThemeData
defaultData
=
NavigationRailThemeData
();
properties
.
add
(
ColorProperty
(
'backgroundColor'
,
backgroundColor
,
defaultValue:
defaultData
.
backgroundColor
));
properties
.
add
(
DoubleProperty
(
'elevation'
,
elevation
,
defaultValue:
defaultData
.
elevation
));
properties
.
add
(
DiagnosticsProperty
<
TextStyle
>(
'unselectedLabelTextStyle'
,
unselectedLabelTextStyle
,
defaultValue:
defaultData
.
unselectedLabelTextStyle
));
properties
.
add
(
DiagnosticsProperty
<
TextStyle
>(
'selectedLabelTextStyle'
,
selectedLabelTextStyle
,
defaultValue:
defaultData
.
selectedLabelTextStyle
));
properties
.
add
(
DiagnosticsProperty
<
IconThemeData
>(
'unselectedIconTheme'
,
unselectedIconTheme
,
defaultValue:
defaultData
.
unselectedIconTheme
));
properties
.
add
(
DiagnosticsProperty
<
IconThemeData
>(
'selectedIconTheme'
,
selectedIconTheme
,
defaultValue:
defaultData
.
selectedIconTheme
));
properties
.
add
(
DoubleProperty
(
'groupAlignment'
,
groupAlignment
,
defaultValue:
defaultData
.
groupAlignment
));
properties
.
add
(
DiagnosticsProperty
<
NavigationRailLabelType
>(
'labelType'
,
labelType
,
defaultValue:
defaultData
.
labelType
));
}
}
/// An inherited widget that defines visual properties for [NavigationRail]s and
/// [NavigationRailDestination]s in this widget's subtree.
///
/// Values specified here are used for [NavigationRail] properties that are not
/// given an explicit non-null value.
class
NavigationRailTheme
extends
InheritedTheme
{
/// Creates a navigation rail theme that controls the
/// [NavigationRailThemeData] properties for a [NavigationRail].
///
/// The data argument must not be null.
const
NavigationRailTheme
({
Key
key
,
@required
this
.
data
,
Widget
child
,
})
:
assert
(
data
!=
null
),
super
(
key:
key
,
child:
child
);
/// Specifies the background color, elevation, label text style, icon theme,
/// group alignment, and label type and border values for descendant
/// [NavigationRail] widgets.
final
NavigationRailThemeData
data
;
/// The closest instance of this class that encloses the given context.
///
/// If there is no enclosing [NavigationRailTheme] widget, then
/// [ThemeData.navigationRailTheme] is used.
///
/// Typical usage is as follows:
///
/// ```dart
/// NavigationRailTheme theme = NavigationRailTheme.of(context);
/// ```
static
NavigationRailThemeData
of
(
BuildContext
context
)
{
final
NavigationRailTheme
navigationRailTheme
=
context
.
dependOnInheritedWidgetOfExactType
<
NavigationRailTheme
>();
return
navigationRailTheme
?.
data
??
Theme
.
of
(
context
).
navigationRailTheme
;
}
@override
Widget
wrap
(
BuildContext
context
,
Widget
child
)
{
final
NavigationRailTheme
ancestorTheme
=
context
.
findAncestorWidgetOfExactType
<
NavigationRailTheme
>();
return
identical
(
this
,
ancestorTheme
)
?
child
:
NavigationRailTheme
(
data:
data
,
child:
child
);
}
@override
bool
updateShouldNotify
(
NavigationRailTheme
oldWidget
)
=>
data
!=
oldWidget
.
data
;
}
packages/flutter/lib/src/material/theme_data.dart
View file @
0d111bc9
...
...
@@ -25,6 +25,7 @@ import 'floating_action_button_theme.dart';
import
'ink_splash.dart'
;
import
'ink_well.dart'
show
InteractiveInkFeatureFactory
;
import
'input_decorator.dart'
;
import
'navigation_rail_theme.dart'
;
import
'page_transitions_theme.dart'
;
import
'popup_menu_theme.dart'
;
import
'slider_theme.dart'
;
...
...
@@ -256,6 +257,7 @@ class ThemeData with Diagnosticable {
ColorScheme
colorScheme
,
DialogTheme
dialogTheme
,
FloatingActionButtonThemeData
floatingActionButtonTheme
,
NavigationRailThemeData
navigationRailTheme
,
Typography
typography
,
CupertinoThemeData
cupertinoOverrideTheme
,
SnackBarThemeData
snackBarTheme
,
...
...
@@ -364,6 +366,7 @@ class ThemeData with Diagnosticable {
);
dialogTheme
??=
const
DialogTheme
();
floatingActionButtonTheme
??=
const
FloatingActionButtonThemeData
();
navigationRailTheme
??=
const
NavigationRailThemeData
();
cupertinoOverrideTheme
=
cupertinoOverrideTheme
?.
noDefault
();
snackBarTheme
??=
const
SnackBarThemeData
();
bottomSheetTheme
??=
const
BottomSheetThemeData
();
...
...
@@ -428,6 +431,7 @@ class ThemeData with Diagnosticable {
colorScheme:
colorScheme
,
dialogTheme:
dialogTheme
,
floatingActionButtonTheme:
floatingActionButtonTheme
,
navigationRailTheme:
navigationRailTheme
,
typography:
typography
,
cupertinoOverrideTheme:
cupertinoOverrideTheme
,
snackBarTheme:
snackBarTheme
,
...
...
@@ -505,6 +509,7 @@ class ThemeData with Diagnosticable {
@required
this
.
colorScheme
,
@required
this
.
dialogTheme
,
@required
this
.
floatingActionButtonTheme
,
@required
this
.
navigationRailTheme
,
@required
this
.
typography
,
@required
this
.
cupertinoOverrideTheme
,
@required
this
.
snackBarTheme
,
...
...
@@ -566,6 +571,7 @@ class ThemeData with Diagnosticable {
assert
(
colorScheme
!=
null
),
assert
(
dialogTheme
!=
null
),
assert
(
floatingActionButtonTheme
!=
null
),
assert
(
navigationRailTheme
!=
null
),
assert
(
typography
!=
null
),
assert
(
snackBarTheme
!=
null
),
assert
(
bottomSheetTheme
!=
null
),
...
...
@@ -982,6 +988,10 @@ class ThemeData with Diagnosticable {
/// [FloatingActionButton].
final
FloatingActionButtonThemeData
floatingActionButtonTheme
;
/// A theme for customizing the background color, elevation, text style, and
/// icon themes of a [NavigationRail].
final
NavigationRailThemeData
navigationRailTheme
;
/// The color and geometry [TextTheme] values used to configure [textTheme],
/// [primaryTextTheme], and [accentTextTheme].
final
Typography
typography
;
...
...
@@ -1072,6 +1082,7 @@ class ThemeData with Diagnosticable {
ColorScheme
colorScheme
,
DialogTheme
dialogTheme
,
FloatingActionButtonThemeData
floatingActionButtonTheme
,
NavigationRailThemeData
navigationRailTheme
,
Typography
typography
,
CupertinoThemeData
cupertinoOverrideTheme
,
SnackBarThemeData
snackBarTheme
,
...
...
@@ -1138,6 +1149,7 @@ class ThemeData with Diagnosticable {
colorScheme:
colorScheme
??
this
.
colorScheme
,
dialogTheme:
dialogTheme
??
this
.
dialogTheme
,
floatingActionButtonTheme:
floatingActionButtonTheme
??
this
.
floatingActionButtonTheme
,
navigationRailTheme:
navigationRailTheme
??
this
.
navigationRailTheme
,
typography:
typography
??
this
.
typography
,
cupertinoOverrideTheme:
cupertinoOverrideTheme
??
this
.
cupertinoOverrideTheme
,
snackBarTheme:
snackBarTheme
??
this
.
snackBarTheme
,
...
...
@@ -1282,6 +1294,7 @@ class ThemeData with Diagnosticable {
colorScheme:
ColorScheme
.
lerp
(
a
.
colorScheme
,
b
.
colorScheme
,
t
),
dialogTheme:
DialogTheme
.
lerp
(
a
.
dialogTheme
,
b
.
dialogTheme
,
t
),
floatingActionButtonTheme:
FloatingActionButtonThemeData
.
lerp
(
a
.
floatingActionButtonTheme
,
b
.
floatingActionButtonTheme
,
t
),
navigationRailTheme:
NavigationRailThemeData
.
lerp
(
a
.
navigationRailTheme
,
b
.
navigationRailTheme
,
t
),
typography:
Typography
.
lerp
(
a
.
typography
,
b
.
typography
,
t
),
cupertinoOverrideTheme:
t
<
0.5
?
a
.
cupertinoOverrideTheme
:
b
.
cupertinoOverrideTheme
,
snackBarTheme:
SnackBarThemeData
.
lerp
(
a
.
snackBarTheme
,
b
.
snackBarTheme
,
t
),
...
...
@@ -1354,6 +1367,7 @@ class ThemeData with Diagnosticable {
&&
other
.
colorScheme
==
colorScheme
&&
other
.
dialogTheme
==
dialogTheme
&&
other
.
floatingActionButtonTheme
==
floatingActionButtonTheme
&&
other
.
navigationRailTheme
==
navigationRailTheme
&&
other
.
typography
==
typography
&&
other
.
cupertinoOverrideTheme
==
cupertinoOverrideTheme
&&
other
.
snackBarTheme
==
snackBarTheme
...
...
@@ -1425,6 +1439,7 @@ class ThemeData with Diagnosticable {
colorScheme
,
dialogTheme
,
floatingActionButtonTheme
,
navigationRailTheme
,
typography
,
cupertinoOverrideTheme
,
snackBarTheme
,
...
...
@@ -1492,6 +1507,7 @@ class ThemeData with Diagnosticable {
properties
.
add
(
DiagnosticsProperty
<
ColorScheme
>(
'colorScheme'
,
colorScheme
,
defaultValue:
defaultData
.
colorScheme
,
level:
DiagnosticLevel
.
debug
));
properties
.
add
(
DiagnosticsProperty
<
DialogTheme
>(
'dialogTheme'
,
dialogTheme
,
defaultValue:
defaultData
.
dialogTheme
,
level:
DiagnosticLevel
.
debug
));
properties
.
add
(
DiagnosticsProperty
<
FloatingActionButtonThemeData
>(
'floatingActionButtonThemeData'
,
floatingActionButtonTheme
,
defaultValue:
defaultData
.
floatingActionButtonTheme
,
level:
DiagnosticLevel
.
debug
));
properties
.
add
(
DiagnosticsProperty
<
NavigationRailThemeData
>(
'navigationRailThemeData'
,
navigationRailTheme
,
defaultValue:
defaultData
.
navigationRailTheme
,
level:
DiagnosticLevel
.
debug
));
properties
.
add
(
DiagnosticsProperty
<
Typography
>(
'typography'
,
typography
,
defaultValue:
defaultData
.
typography
,
level:
DiagnosticLevel
.
debug
));
properties
.
add
(
DiagnosticsProperty
<
CupertinoThemeData
>(
'cupertinoOverrideTheme'
,
cupertinoOverrideTheme
,
defaultValue:
defaultData
.
cupertinoOverrideTheme
,
level:
DiagnosticLevel
.
debug
));
properties
.
add
(
DiagnosticsProperty
<
SnackBarThemeData
>(
'snackBarTheme'
,
snackBarTheme
,
defaultValue:
defaultData
.
snackBarTheme
,
level:
DiagnosticLevel
.
debug
));
...
...
packages/flutter/test/material/navigation_rail_test.dart
0 → 100644
View file @
0d111bc9
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'../widgets/semantics_tester.dart'
;
void
main
(
)
{
testWidgets
(
'Custom selected and unselected textStyles are honored'
,
(
WidgetTester
tester
)
async
{
const
TextStyle
selectedTextStyle
=
TextStyle
(
fontWeight:
FontWeight
.
w300
,
fontSize:
17.0
);
const
TextStyle
unselectedTextStyle
=
TextStyle
(
fontWeight:
FontWeight
.
w800
,
fontSize:
11.0
);
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
labelType:
NavigationRailLabelType
.
all
,
selectedLabelTextStyle:
selectedTextStyle
,
unselectedLabelTextStyle:
unselectedTextStyle
,
),
);
final
TextStyle
actualSelectedTextStyle
=
tester
.
renderObject
<
RenderParagraph
>(
find
.
text
(
'Abc'
)).
text
.
style
;
final
TextStyle
actualUnselectedTextStyle
=
tester
.
renderObject
<
RenderParagraph
>(
find
.
text
(
'Def'
)).
text
.
style
;
expect
(
actualSelectedTextStyle
.
fontSize
,
equals
(
selectedTextStyle
.
fontSize
));
expect
(
actualSelectedTextStyle
.
fontWeight
,
equals
(
selectedTextStyle
.
fontWeight
));
expect
(
actualUnselectedTextStyle
.
fontSize
,
equals
(
actualUnselectedTextStyle
.
fontSize
));
expect
(
actualUnselectedTextStyle
.
fontWeight
,
equals
(
actualUnselectedTextStyle
.
fontWeight
));
});
testWidgets
(
'Custom selected and unselected iconThemes are honored'
,
(
WidgetTester
tester
)
async
{
const
IconThemeData
selectedIconTheme
=
IconThemeData
(
size:
36
,
color:
Color
(
0x00000001
));
const
IconThemeData
unselectedIconTheme
=
IconThemeData
(
size:
18
,
color:
Color
(
0x00000002
));
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
labelType:
NavigationRailLabelType
.
all
,
selectedIconTheme:
selectedIconTheme
,
unselectedIconTheme:
unselectedIconTheme
,
),
);
final
TextStyle
actualSelectedIconTheme
=
_iconStyle
(
tester
,
Icons
.
favorite
);
final
TextStyle
actualUnselectedIconTheme
=
_iconStyle
(
tester
,
Icons
.
bookmark_border
);
expect
(
actualSelectedIconTheme
.
color
,
equals
(
selectedIconTheme
.
color
));
expect
(
actualSelectedIconTheme
.
fontSize
,
equals
(
selectedIconTheme
.
size
));
expect
(
actualUnselectedIconTheme
.
color
,
equals
(
unselectedIconTheme
.
color
));
expect
(
actualUnselectedIconTheme
.
fontSize
,
equals
(
unselectedIconTheme
.
size
));
});
testWidgets
(
'backgroundColor can be changed'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
labelType:
NavigationRailLabelType
.
all
,
),
);
expect
(
_railMaterial
(
tester
).
color
,
equals
(
Colors
.
white
));
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
labelType:
NavigationRailLabelType
.
all
,
backgroundColor:
Colors
.
green
,
),
);
expect
(
_railMaterial
(
tester
).
color
,
equals
(
Colors
.
green
));
});
testWidgets
(
'elevation can be changed'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
labelType:
NavigationRailLabelType
.
all
,
),
);
expect
(
_railMaterial
(
tester
).
elevation
,
equals
(
0
));
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
labelType:
NavigationRailLabelType
.
all
,
elevation:
7
,
),
);
expect
(
_railMaterial
(
tester
).
elevation
,
equals
(
7
));
});
testWidgets
(
'Renders at the correct default width - [labelType]=none (default)'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
),
);
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byType
(
NavigationRail
));
expect
(
renderBox
.
size
.
width
,
72.0
);
});
testWidgets
(
'Renders at the correct default width - [labelType]=selected'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
labelType:
NavigationRailLabelType
.
selected
,
destinations:
_destinations
(),
),
);
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byType
(
NavigationRail
));
expect
(
renderBox
.
size
.
width
,
72.0
);
});
testWidgets
(
'Renders at the correct default width - [labelType]=all'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
labelType:
NavigationRailLabelType
.
all
,
destinations:
_destinations
(),
),
);
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byType
(
NavigationRail
));
expect
(
renderBox
.
size
.
width
,
72.0
);
});
testWidgets
(
'Renders wider for a destination with a long label - [labelType]=all'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
labelType:
NavigationRailLabelType
.
all
,
destinations:
const
<
NavigationRailDestination
>[
NavigationRailDestination
(
icon:
Icon
(
Icons
.
favorite_border
),
selectedIcon:
Icon
(
Icons
.
favorite
),
label:
Text
(
'Abc'
),
),
NavigationRailDestination
(
icon:
Icon
(
Icons
.
bookmark_border
),
selectedIcon:
Icon
(
Icons
.
bookmark
),
label:
Text
(
'Longer Label'
),
),
],
),
);
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byType
(
NavigationRail
));
// Total padding is 16 (8 on each side).
expect
(
renderBox
.
size
.
width
,
_labelRenderBox
(
tester
,
'Longer Label'
).
size
.
width
+
16.0
);
});
testWidgets
(
'Renders only icons - [labelType]=none (default)'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
),
);
expect
(
find
.
byIcon
(
Icons
.
favorite
),
findsOneWidget
);
expect
(
find
.
byIcon
(
Icons
.
bookmark_border
),
findsOneWidget
);
expect
(
find
.
byIcon
(
Icons
.
star_border
),
findsOneWidget
);
expect
(
find
.
byIcon
(
Icons
.
hotel
),
findsOneWidget
);
// When there are no labels, a 0 opacity label is still shown for semantics.
expect
(
_labelOpacity
(
tester
,
'Abc'
),
0
);
expect
(
_labelOpacity
(
tester
,
'Def'
),
0
);
expect
(
_labelOpacity
(
tester
,
'Ghi'
),
0
);
expect
(
_labelOpacity
(
tester
,
'Jkl'
),
0
);
});
testWidgets
(
'Renders icons and labels - [labelType]=all'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
labelType:
NavigationRailLabelType
.
all
,
),
);
expect
(
find
.
byIcon
(
Icons
.
favorite
),
findsOneWidget
);
expect
(
find
.
byIcon
(
Icons
.
bookmark_border
),
findsOneWidget
);
expect
(
find
.
byIcon
(
Icons
.
star_border
),
findsOneWidget
);
expect
(
find
.
byIcon
(
Icons
.
hotel
),
findsOneWidget
);
expect
(
find
.
text
(
'Abc'
),
findsOneWidget
);
expect
(
find
.
text
(
'Def'
),
findsOneWidget
);
expect
(
find
.
text
(
'Ghi'
),
findsOneWidget
);
expect
(
find
.
text
(
'Jkl'
),
findsOneWidget
);
// When displaying all labels, there is no opacity.
expect
(
_opacityAboveLabel
(
'Abc'
),
findsNothing
);
expect
(
_opacityAboveLabel
(
'Def'
),
findsNothing
);
expect
(
_opacityAboveLabel
(
'Ghi'
),
findsNothing
);
expect
(
_opacityAboveLabel
(
'Jkl'
),
findsNothing
);
});
testWidgets
(
'Renders icons and selected label - [labelType]=selected'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
labelType:
NavigationRailLabelType
.
selected
,
),
);
expect
(
find
.
byIcon
(
Icons
.
favorite
),
findsOneWidget
);
expect
(
find
.
byIcon
(
Icons
.
bookmark_border
),
findsOneWidget
);
expect
(
find
.
byIcon
(
Icons
.
star_border
),
findsOneWidget
);
expect
(
find
.
byIcon
(
Icons
.
hotel
),
findsOneWidget
);
// Only the selected label is visible.
expect
(
_labelOpacity
(
tester
,
'Abc'
),
1
);
expect
(
_labelOpacity
(
tester
,
'Def'
),
0
);
expect
(
_labelOpacity
(
tester
,
'Ghi'
),
0
);
expect
(
_labelOpacity
(
tester
,
'Jkl'
),
0
);
});
testWidgets
(
'Destination spacing is correct - [labelType]=none (default), [textScaleFactor]=1.0 (default)'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
),
);
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byType
(
NavigationRail
));
expect
(
renderBox
.
size
.
width
,
72.0
);
// The first destination is 8 from the top because of the default vertical
// padding at the to of the rail.
double
nextDestinationY
=
8.0
;
final
RenderBox
firstIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
favorite
);
expect
(
firstIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
firstIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
firstIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The second destination is 72 below the first destination.
nextDestinationY
+=
72.0
;
final
RenderBox
secondIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
bookmark_border
);
expect
(
secondIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
secondIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
secondIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The third destination is 72 below the second destination.
nextDestinationY
+=
72.0
;
final
RenderBox
thirdIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
star_border
);
expect
(
thirdIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
thirdIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
thirdIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The fourth destination is 72 below the third destination.
nextDestinationY
+=
72.0
;
final
RenderBox
fourthIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
hotel
);
expect
(
fourthIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
fourthIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
fourthIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
});
testWidgets
(
'Destination spacing is correct - [labelType]=none (default), [textScaleFactor]=3.0'
,
(
WidgetTester
tester
)
async
{
// Since the rail is icon only, its destinations should not be affected by
// textScaleFactor.
await
_pumpNavigationRail
(
tester
,
textScaleFactor:
3.0
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
),
);
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byType
(
NavigationRail
));
expect
(
renderBox
.
size
.
width
,
72.0
);
// The first destination is 8 from the top because of the default vertical
// padding at the to of the rail.
double
nextDestinationY
=
8.0
;
final
RenderBox
firstIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
favorite
);
expect
(
firstIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
firstIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
firstIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The second destination is 72 below the first destination.
nextDestinationY
+=
72.0
;
final
RenderBox
secondIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
bookmark_border
);
expect
(
secondIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
secondIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
secondIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The third destination is 72 below the second destination.
nextDestinationY
+=
72.0
;
final
RenderBox
thirdIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
star_border
);
expect
(
thirdIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
thirdIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
thirdIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The fourth destination is 72 below the third destination.
nextDestinationY
+=
72.0
;
final
RenderBox
fourthIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
hotel
);
expect
(
fourthIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
fourthIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
fourthIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
});
testWidgets
(
'Destination spacing is correct - [labelType]=none (default), [textScaleFactor]=0.75'
,
(
WidgetTester
tester
)
async
{
// Since the rail is icon only, its destinations should not be affected by
// textScaleFactor.
await
_pumpNavigationRail
(
tester
,
textScaleFactor:
0.75
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
),
);
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byType
(
NavigationRail
));
expect
(
renderBox
.
size
.
width
,
72.0
);
// The first destination is 8 from the top because of the default vertical
// padding at the to of the rail.
double
nextDestinationY
=
8.0
;
final
RenderBox
firstIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
favorite
);
expect
(
firstIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
firstIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
firstIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The second destination is 72 below the first destination.
nextDestinationY
+=
72.0
;
final
RenderBox
secondIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
bookmark_border
);
expect
(
secondIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
secondIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
secondIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The third destination is 72 below the second destination.
nextDestinationY
+=
72.0
;
final
RenderBox
thirdIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
star_border
);
expect
(
thirdIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
thirdIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
thirdIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The fourth destination is 72 below the third destination.
nextDestinationY
+=
72.0
;
final
RenderBox
fourthIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
hotel
);
expect
(
fourthIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
fourthIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
fourthIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
});
testWidgets
(
'Destination spacing is correct - [labelType]=selected, [textScaleFactor]=1.0 (default)'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
labelType:
NavigationRailLabelType
.
selected
,
),
);
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byType
(
NavigationRail
));
expect
(
renderBox
.
size
.
width
,
72.0
);
// The first destination is 8 from the top because of the default vertical
// padding at the to of the rail.
double
nextDestinationY
=
8.0
;
final
RenderBox
firstIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
favorite
);
final
RenderBox
firstLabelRenderBox
=
_labelRenderBox
(
tester
,
'Abc'
);
expect
(
firstIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
firstIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
firstIconRenderBox
.
size
.
height
-
firstLabelRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
expect
(
firstLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
firstLabelRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
+
firstIconRenderBox
.
size
.
height
-
firstLabelRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The second destination is 72 below the first destination.
nextDestinationY
+=
72.0
;
final
RenderBox
secondIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
bookmark_border
);
expect
(
secondIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
secondIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
secondIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The third destination is 72 below the second destination.
nextDestinationY
+=
72.0
;
final
RenderBox
thirdIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
star_border
);
expect
(
thirdIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
thirdIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
thirdIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The fourth destination is 72 below the third destination.
nextDestinationY
+=
72.0
;
final
RenderBox
fourthIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
hotel
);
expect
(
fourthIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
fourthIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
fourthIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
});
testWidgets
(
'Destination spacing is correct - [labelType]=selected, [textScaleFactor]=3.0'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
textScaleFactor:
3.0
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
labelType:
NavigationRailLabelType
.
selected
,
),
);
// The rail and destinations sizes grow to fit the larger text labels.
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byType
(
NavigationRail
));
expect
(
renderBox
.
size
.
width
,
142.0
);
// The first destination is 8 from the top because of the default vertical
// padding at the to of the rail.
double
nextDestinationY
=
8.0
;
final
RenderBox
firstIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
favorite
);
expect
(
firstIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
142.0
-
firstIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
,
),
),
);
// The first label sits right below the first icon.
final
RenderBox
firstLabelRenderBox
=
_labelRenderBox
(
tester
,
'Abc'
);
expect
(
firstLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
142.0
-
firstLabelRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
+
firstIconRenderBox
.
size
.
height
,
),
),
);
nextDestinationY
+=
16.0
+
firstIconRenderBox
.
size
.
height
+
firstLabelRenderBox
.
size
.
height
+
16.0
;
final
RenderBox
secondIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
bookmark_border
);
expect
(
secondIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
142.0
-
secondIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
24.0
,
),
),
);
nextDestinationY
+=
72.0
;
final
RenderBox
thirdIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
star_border
);
expect
(
thirdIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
142.0
-
thirdIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
24.0
,
),
),
);
nextDestinationY
+=
72.0
;
final
RenderBox
fourthIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
hotel
);
expect
(
fourthIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
142.0
-
fourthIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
24.0
,
),
),
);
});
testWidgets
(
'Destination spacing is correct - [labelType]=selected, [textScaleFactor]=0.75'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
textScaleFactor:
0.75
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
labelType:
NavigationRailLabelType
.
selected
,
),
);
// A smaller textScaleFactor will not reduce the default width of the rail
// since there is a minWidth.
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byType
(
NavigationRail
));
expect
(
renderBox
.
size
.
width
,
72.0
);
// The first destination is 8 from the top because of the default vertical
// padding at the to of the rail.
double
nextDestinationY
=
8.0
;
final
RenderBox
firstIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
favorite
);
final
RenderBox
firstLabelRenderBox
=
_labelRenderBox
(
tester
,
'Abc'
);
expect
(
firstIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
firstIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
firstIconRenderBox
.
size
.
height
-
firstLabelRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
expect
(
firstLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
firstLabelRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
+
firstIconRenderBox
.
size
.
height
-
firstLabelRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The second destination is 72 below the first destination.
nextDestinationY
+=
72.0
;
final
RenderBox
secondIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
bookmark_border
);
expect
(
secondIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
secondIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
secondIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The third destination is 72 below the second destination.
nextDestinationY
+=
72.0
;
final
RenderBox
thirdIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
star_border
);
expect
(
thirdIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
thirdIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
thirdIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The fourth destination is 72 below the third destination.
nextDestinationY
+=
72.0
;
final
RenderBox
fourthIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
hotel
);
expect
(
fourthIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
fourthIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
fourthIconRenderBox
.
size
.
height
)
/
2.0
,
)),
);
});
testWidgets
(
'Destination spacing is correct - [labelType]=all, [textScaleFactor]=1.0 (default)'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
labelType:
NavigationRailLabelType
.
all
,
),
);
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byType
(
NavigationRail
));
expect
(
renderBox
.
size
.
width
,
72.0
);
// The first destination is 8 from the top because of the default vertical
// padding at the to of the rail.
double
nextDestinationY
=
8.0
;
final
RenderBox
firstIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
favorite
);
final
RenderBox
firstLabelRenderBox
=
_labelRenderBox
(
tester
,
'Abc'
);
expect
(
firstIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
firstIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
,
),
),
);
expect
(
firstLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
firstLabelRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
+
firstIconRenderBox
.
size
.
height
,
),
),
);
// The second destination is 72 below the first destination.
nextDestinationY
+=
72.0
;
final
RenderBox
secondIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
bookmark_border
);
final
RenderBox
secondLabelRenderBox
=
_labelRenderBox
(
tester
,
'Def'
);
expect
(
secondIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
secondIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
,
),
),
);
expect
(
secondLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
secondLabelRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
+
secondIconRenderBox
.
size
.
height
,
),
),
);
// The third destination is 72 below the second destination.
nextDestinationY
+=
72.0
;
final
RenderBox
thirdIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
star_border
);
final
RenderBox
thirdLabelRenderBox
=
_labelRenderBox
(
tester
,
'Ghi'
);
expect
(
thirdIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
thirdIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
,
),
),
);
expect
(
thirdLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
thirdLabelRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
+
thirdIconRenderBox
.
size
.
height
,
),
),
);
// The fourth destination is 72 below the third destination.
nextDestinationY
+=
72.0
;
final
RenderBox
fourthIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
hotel
);
final
RenderBox
fourthLabelRenderBox
=
_labelRenderBox
(
tester
,
'Jkl'
);
expect
(
fourthIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
fourthIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
,
),
),
);
expect
(
fourthLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
fourthLabelRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
+
fourthIconRenderBox
.
size
.
height
,
),
),
);
});
testWidgets
(
'Destination spacing is correct - [labelType]=all, [textScaleFactor]=3.0'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
textScaleFactor:
3.0
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
labelType:
NavigationRailLabelType
.
all
,
),
);
// The rail and destinations sizes grow to fit the larger text labels.
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byType
(
NavigationRail
));
expect
(
renderBox
.
size
.
width
,
142.0
);
// The first destination is 8 from the top because of the default vertical
// padding at the to of the rail.
double
nextDestinationY
=
8.0
;
final
RenderBox
firstIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
favorite
);
final
RenderBox
firstLabelRenderBox
=
_labelRenderBox
(
tester
,
'Abc'
);
expect
(
firstIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
142.0
-
firstIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
,
),
),
);
expect
(
firstLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
142.0
-
firstLabelRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
+
firstIconRenderBox
.
size
.
height
,
),
),
);
nextDestinationY
+=
16.0
+
firstIconRenderBox
.
size
.
height
+
firstLabelRenderBox
.
size
.
height
+
16.0
;
final
RenderBox
secondIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
bookmark_border
);
final
RenderBox
secondLabelRenderBox
=
_labelRenderBox
(
tester
,
'Def'
);
expect
(
secondIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
142.0
-
secondIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
,
),
),
);
expect
(
secondLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
142.0
-
secondLabelRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
+
secondIconRenderBox
.
size
.
height
,
),
),
);
nextDestinationY
+=
16.0
+
secondIconRenderBox
.
size
.
height
+
secondLabelRenderBox
.
size
.
height
+
16.0
;
final
RenderBox
thirdIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
star_border
);
final
RenderBox
thirdLabelRenderBox
=
_labelRenderBox
(
tester
,
'Ghi'
);
expect
(
thirdIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
142.0
-
thirdIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
,
),
),
);
expect
(
thirdLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
142.0
-
thirdLabelRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
+
thirdIconRenderBox
.
size
.
height
,
),
),
);
nextDestinationY
+=
16.0
+
thirdIconRenderBox
.
size
.
height
+
thirdLabelRenderBox
.
size
.
height
+
16.0
;
final
RenderBox
fourthIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
hotel
);
final
RenderBox
fourthLabelRenderBox
=
_labelRenderBox
(
tester
,
'Jkl'
);
expect
(
fourthIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
142.0
-
fourthIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
,
),
),
);
expect
(
fourthLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
142.0
-
fourthLabelRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
+
fourthIconRenderBox
.
size
.
height
,
),
),
);
});
testWidgets
(
'Destination spacing is correct - [labelType]=all, [textScaleFactor]=0.75'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
textScaleFactor:
0.75
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
labelType:
NavigationRailLabelType
.
all
,
),
);
// A smaller textScaleFactor will not reduce the default size of the rail.
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byType
(
NavigationRail
));
expect
(
renderBox
.
size
.
width
,
72.0
);
// The first destination is 8 from the top because of the default vertical
// padding at the to of the rail.
double
nextDestinationY
=
8.0
;
final
RenderBox
firstIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
favorite
);
final
RenderBox
firstLabelRenderBox
=
_labelRenderBox
(
tester
,
'Abc'
);
expect
(
firstIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
firstIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
,
),
),
);
expect
(
firstLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
firstLabelRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
+
firstIconRenderBox
.
size
.
height
,
),
),
);
// The second destination is 72 below the first destination.
nextDestinationY
+=
72.0
;
final
RenderBox
secondIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
bookmark_border
);
final
RenderBox
secondLabelRenderBox
=
_labelRenderBox
(
tester
,
'Def'
);
expect
(
secondIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
secondIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
,
),
),
);
expect
(
secondLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
secondLabelRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
+
secondIconRenderBox
.
size
.
height
,
),
),
);
// The third destination is 72 below the second destination.
nextDestinationY
+=
72.0
;
final
RenderBox
thirdIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
star_border
);
final
RenderBox
thirdLabelRenderBox
=
_labelRenderBox
(
tester
,
'Ghi'
);
expect
(
thirdIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
thirdIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
,
),
),
);
expect
(
thirdLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
thirdLabelRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
+
thirdIconRenderBox
.
size
.
height
,
),
),
);
// The fourth destination is 72 below the third destination.
nextDestinationY
+=
72.0
;
final
RenderBox
fourthIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
hotel
);
final
RenderBox
fourthLabelRenderBox
=
_labelRenderBox
(
tester
,
'Jkl'
);
expect
(
fourthIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
fourthIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
,
),
),
);
expect
(
fourthLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
fourthLabelRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
16.0
+
fourthIconRenderBox
.
size
.
height
,
),
),
);
});
testWidgets
(
'Destination spacing is correct for a compact rail - [preferredWidth]=56, [textScaleFactor]=1.0 (default)'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
minWidth:
56.0
,
destinations:
_destinations
(),
),
);
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byType
(
NavigationRail
));
expect
(
renderBox
.
size
.
width
,
56.0
);
// The first destination is 8 from the top because of the default vertical
// padding at the to of the rail.
double
nextDestinationY
=
8.0
;
final
RenderBox
firstIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
favorite
);
expect
(
firstIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
56.0
-
firstIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
56.0
-
firstIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The second destination is 56 below the first destination.
nextDestinationY
+=
56.0
;
final
RenderBox
secondIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
bookmark_border
);
expect
(
secondIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
56.0
-
secondIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
56.0
-
secondIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The third destination is 56 below the second destination.
nextDestinationY
+=
56.0
;
final
RenderBox
thirdIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
star_border
);
expect
(
thirdIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
56.0
-
thirdIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
56.0
-
thirdIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The fourth destination is 56 below the third destination.
nextDestinationY
+=
56.0
;
final
RenderBox
fourthIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
hotel
);
expect
(
fourthIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
56.0
-
fourthIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
56.0
-
fourthIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
});
testWidgets
(
'Destination spacing is correct for a compact rail - [preferredWidth]=56, [textScaleFactor]=3.0'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
textScaleFactor:
3.0
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
minWidth:
56.0
,
destinations:
_destinations
(),
),
);
// Since the rail is icon only, its preferred width should not be affected
// by textScaleFactor.
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byType
(
NavigationRail
));
expect
(
renderBox
.
size
.
width
,
56.0
);
// The first destination is 8 from the top because of the default vertical
// padding at the to of the rail.
double
nextDestinationY
=
8.0
;
final
RenderBox
firstIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
favorite
);
expect
(
firstIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
56.0
-
firstIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
56.0
-
firstIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The second destination is 56 below the first destination.
nextDestinationY
+=
56.0
;
final
RenderBox
secondIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
bookmark_border
);
expect
(
secondIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
56.0
-
secondIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
56.0
-
secondIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The third destination is 56 below the second destination.
nextDestinationY
+=
56.0
;
final
RenderBox
thirdIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
star_border
);
expect
(
thirdIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
56.0
-
thirdIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
56.0
-
thirdIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The fourth destination is 56 below the third destination.
nextDestinationY
+=
56.0
;
final
RenderBox
fourthIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
hotel
);
expect
(
fourthIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
56.0
-
fourthIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
56.0
-
fourthIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
});
testWidgets
(
'Destination spacing is correct for a compact rail - [preferredWidth]=56, [textScaleFactor]=0.75'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
textScaleFactor:
3.0
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
minWidth:
56.0
,
destinations:
_destinations
(),
),
);
// Since the rail is icon only, its preferred width should not be affected
// by textScaleFactor.
final
RenderBox
renderBox
=
tester
.
renderObject
(
find
.
byType
(
NavigationRail
));
expect
(
renderBox
.
size
.
width
,
56.0
);
// The first destination is 8 from the top because of the default vertical
// padding at the to of the rail.
double
nextDestinationY
=
8.0
;
final
RenderBox
firstIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
favorite
);
expect
(
firstIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
56.0
-
firstIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
56.0
-
firstIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The second destination is 56 below the first destination.
nextDestinationY
+=
56.0
;
final
RenderBox
secondIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
bookmark_border
);
expect
(
secondIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
56.0
-
secondIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
56.0
-
secondIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The third destination is 56 below the second destination.
nextDestinationY
+=
56.0
;
final
RenderBox
thirdIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
star_border
);
expect
(
thirdIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
56.0
-
thirdIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
56.0
-
thirdIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The fourth destination is 56 below the third destination.
nextDestinationY
+=
56.0
;
final
RenderBox
fourthIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
hotel
);
expect
(
fourthIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
56.0
-
fourthIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
56.0
-
fourthIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
});
testWidgets
(
'Group alignment works - [groupAlignment]=-1.0 (default)'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
),
);
// The first destination is 8 from the top because of the default vertical
// padding at the to of the rail.
double
nextDestinationY
=
8.0
;
final
RenderBox
firstIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
favorite
);
expect
(
firstIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
firstIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
firstIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The second destination is 72 below the first destination.
nextDestinationY
+=
72.0
;
final
RenderBox
secondIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
bookmark_border
);
expect
(
secondIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
secondIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
secondIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The third destination is 72 below the second destination.
nextDestinationY
+=
72.0
;
final
RenderBox
thirdIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
star_border
);
expect
(
thirdIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
thirdIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
thirdIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The fourth destination is 72 below the third destination.
nextDestinationY
+=
72.0
;
final
RenderBox
fourthIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
hotel
);
expect
(
fourthIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
fourthIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
fourthIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
});
testWidgets
(
'Group alignment works - [groupAlignment]=0.0'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
groupAlignment:
0.0
,
destinations:
_destinations
(),
),
);
double
nextDestinationY
=
160.0
;
final
RenderBox
firstIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
favorite
);
expect
(
firstIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
firstIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
firstIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The second destination is 72 below the first destination.
nextDestinationY
+=
72.0
;
final
RenderBox
secondIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
bookmark_border
);
expect
(
secondIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
secondIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
secondIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The third destination is 72 below the second destination.
nextDestinationY
+=
72.0
;
final
RenderBox
thirdIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
star_border
);
expect
(
thirdIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
thirdIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
thirdIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The fourth destination is 72 below the third destination.
nextDestinationY
+=
72.0
;
final
RenderBox
fourthIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
hotel
);
expect
(
fourthIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
fourthIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
fourthIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
});
testWidgets
(
'Group alignment works - [groupAlignment]=1.0'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
groupAlignment:
1.0
,
destinations:
_destinations
(),
),
);
double
nextDestinationY
=
312.0
;
final
RenderBox
firstIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
favorite
);
expect
(
firstIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
firstIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
firstIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The second destination is 72 below the first destination.
nextDestinationY
+=
72.0
;
final
RenderBox
secondIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
bookmark_border
);
expect
(
secondIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
secondIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
secondIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The third destination is 72 below the second destination.
nextDestinationY
+=
72.0
;
final
RenderBox
thirdIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
star_border
);
expect
(
thirdIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
thirdIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
thirdIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The fourth destination is 72 below the third destination.
nextDestinationY
+=
72.0
;
final
RenderBox
fourthIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
hotel
);
expect
(
fourthIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
fourthIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
fourthIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
});
testWidgets
(
'Leading and trailing appear in the correct places'
,
(
WidgetTester
tester
)
async
{
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
leading:
FloatingActionButton
(
onPressed:
()
{
}),
trailing:
FloatingActionButton
(
onPressed:
()
{
}),
destinations:
_destinations
(),
),
);
final
RenderBox
leading
=
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
FloatingActionButton
).
at
(
0
));
final
RenderBox
trailing
=
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
FloatingActionButton
).
at
(
1
));
expect
(
leading
.
localToGlobal
(
Offset
.
zero
),
const
Offset
(
0.0
,
8.0
));
expect
(
trailing
.
localToGlobal
(
Offset
.
zero
),
const
Offset
(
0.0
,
360.0
));
});
testWidgets
(
'Extended rail animates the width and labels appear - [textDirection]=LTR'
,
(
WidgetTester
tester
)
async
{
bool
extended
=
false
;
StateSetter
stateSetter
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
stateSetter
=
setState
;
return
Scaffold
(
body:
Row
(
children:
<
Widget
>[
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
extended:
extended
,
),
const
Expanded
(
child:
Text
(
'body'
),
),
],
),
);
},
),
),
);
final
RenderBox
rail
=
tester
.
firstRenderObject
<
RenderBox
>(
find
.
byType
(
NavigationRail
));
expect
(
rail
.
size
.
width
,
equals
(
72.0
));
stateSetter
(()
{
extended
=
true
;
});
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
rail
.
size
.
width
,
equals
(
164.0
));
await
tester
.
pumpAndSettle
();
expect
(
rail
.
size
.
width
,
equals
(
256.0
));
// The first destination is 8 from the top because of the default vertical
// padding at the to of the rail.
double
nextDestinationY
=
8.0
;
final
RenderBox
firstIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
favorite
);
final
RenderBox
firstLabelRenderBox
=
_labelRenderBox
(
tester
,
'Abc'
);
expect
(
firstIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
firstIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
firstIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
expect
(
firstLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
72.0
,
nextDestinationY
+
(
72.0
-
firstLabelRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The second destination is 72 below the first destination.
nextDestinationY
+=
72.0
;
final
RenderBox
secondIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
bookmark_border
);
final
RenderBox
secondLabelRenderBox
=
_labelRenderBox
(
tester
,
'Def'
);
expect
(
secondIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
secondIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
secondIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
expect
(
secondLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
72.0
,
nextDestinationY
+
(
72.0
-
secondLabelRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The third destination is 72 below the second destination.
nextDestinationY
+=
72.0
;
final
RenderBox
thirdIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
star_border
);
final
RenderBox
thirdLabelRenderBox
=
_labelRenderBox
(
tester
,
'Ghi'
);
expect
(
thirdIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
thirdIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
thirdIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
expect
(
thirdLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
72.0
,
nextDestinationY
+
(
72.0
-
thirdLabelRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The fourth destination is 72 below the third destination.
nextDestinationY
+=
72.0
;
final
RenderBox
fourthIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
hotel
);
final
RenderBox
fourthLabelRenderBox
=
_labelRenderBox
(
tester
,
'Jkl'
);
expect
(
fourthIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
(
72.0
-
fourthIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
fourthIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
expect
(
fourthLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
72.0
,
nextDestinationY
+
(
72.0
-
fourthLabelRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
});
testWidgets
(
'Extended rail animates the width and labels appear - [textDirection]=RTL'
,
(
WidgetTester
tester
)
async
{
bool
extended
=
false
;
StateSetter
stateSetter
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
stateSetter
=
setState
;
return
Directionality
(
textDirection:
TextDirection
.
rtl
,
child:
Scaffold
(
body:
Row
(
textDirection:
TextDirection
.
rtl
,
children:
<
Widget
>[
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
extended:
extended
,
),
const
Expanded
(
child:
Text
(
'body'
),
),
],
),
),
);
},
),
),
);
final
RenderBox
rail
=
tester
.
firstRenderObject
<
RenderBox
>(
find
.
byType
(
NavigationRail
));
expect
(
rail
.
size
.
width
,
equals
(
72.0
));
expect
(
rail
.
localToGlobal
(
Offset
.
zero
),
equals
(
const
Offset
(
728.0
,
0.0
)));
stateSetter
(()
{
extended
=
true
;
});
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
rail
.
size
.
width
,
equals
(
164.0
));
expect
(
rail
.
localToGlobal
(
Offset
.
zero
),
equals
(
const
Offset
(
636.0
,
0.0
)));
await
tester
.
pumpAndSettle
();
expect
(
rail
.
size
.
width
,
equals
(
256.0
));
expect
(
rail
.
localToGlobal
(
Offset
.
zero
),
equals
(
const
Offset
(
544.0
,
0.0
)));
// The first destination is 8 from the top because of the default vertical
// padding at the to of the rail.
double
nextDestinationY
=
8.0
;
final
RenderBox
firstIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
favorite
);
final
RenderBox
firstLabelRenderBox
=
_labelRenderBox
(
tester
,
'Abc'
);
expect
(
firstIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
800.0
-
(
72.0
+
firstIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
firstIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
expect
(
firstLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
800.0
-
72.0
-
firstLabelRenderBox
.
size
.
width
,
nextDestinationY
+
(
72.0
-
firstLabelRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The second destination is 72 below the first destination.
nextDestinationY
+=
72.0
;
final
RenderBox
secondIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
bookmark_border
);
final
RenderBox
secondLabelRenderBox
=
_labelRenderBox
(
tester
,
'Def'
);
expect
(
secondIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
800.0
-
(
72.0
+
secondIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
secondIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
expect
(
secondLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
800.0
-
72.0
-
secondLabelRenderBox
.
size
.
width
,
nextDestinationY
+
(
72.0
-
secondLabelRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The third destination is 72 below the second destination.
nextDestinationY
+=
72.0
;
final
RenderBox
thirdIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
star_border
);
final
RenderBox
thirdLabelRenderBox
=
_labelRenderBox
(
tester
,
'Ghi'
);
expect
(
thirdIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
800.0
-
(
72.0
+
thirdIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
thirdIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
expect
(
thirdLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
800.0
-
72.0
-
thirdLabelRenderBox
.
size
.
width
,
nextDestinationY
+
(
72.0
-
thirdLabelRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
// The fourth destination is 72 below the third destination.
nextDestinationY
+=
72.0
;
final
RenderBox
fourthIconRenderBox
=
_iconRenderBox
(
tester
,
Icons
.
hotel
);
final
RenderBox
fourthLabelRenderBox
=
_labelRenderBox
(
tester
,
'Jkl'
);
expect
(
fourthIconRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
800.0
-
(
72.0
+
fourthIconRenderBox
.
size
.
width
)
/
2.0
,
nextDestinationY
+
(
72.0
-
fourthIconRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
expect
(
fourthLabelRenderBox
.
localToGlobal
(
Offset
.
zero
),
equals
(
Offset
(
800.0
-
72.0
-
fourthLabelRenderBox
.
size
.
width
,
nextDestinationY
+
(
72.0
-
fourthLabelRenderBox
.
size
.
height
)
/
2.0
,
),
),
);
});
testWidgets
(
'Extended rail gets wider with longer labels are larger text scale'
,
(
WidgetTester
tester
)
async
{
bool
extended
=
false
;
StateSetter
stateSetter
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
stateSetter
=
setState
;
return
Scaffold
(
body:
Row
(
children:
<
Widget
>[
MediaQuery
(
data:
MediaQuery
.
of
(
context
).
copyWith
(
textScaleFactor:
3.0
),
child:
NavigationRail
(
selectedIndex:
0
,
destinations:
const
<
NavigationRailDestination
>[
NavigationRailDestination
(
icon:
Icon
(
Icons
.
favorite_border
),
selectedIcon:
Icon
(
Icons
.
favorite
),
label:
Text
(
'Abc'
),
),
NavigationRailDestination
(
icon:
Icon
(
Icons
.
bookmark_border
),
selectedIcon:
Icon
(
Icons
.
bookmark
),
label:
Text
(
'Longer Label'
),
),
],
extended:
extended
,
),
),
const
Expanded
(
child:
Text
(
'body'
),
),
],
),
);
},
),
),
);
final
RenderBox
rail
=
tester
.
firstRenderObject
<
RenderBox
>(
find
.
byType
(
NavigationRail
));
expect
(
rail
.
size
.
width
,
equals
(
72.0
));
stateSetter
(()
{
extended
=
true
;
});
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
rail
.
size
.
width
,
equals
(
332.0
));
await
tester
.
pumpAndSettle
();
expect
(
rail
.
size
.
width
,
equals
(
584.0
));
});
testWidgets
(
'Extended rail final width can be changed'
,
(
WidgetTester
tester
)
async
{
bool
extended
=
false
;
StateSetter
stateSetter
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
stateSetter
=
setState
;
return
Scaffold
(
body:
Row
(
children:
<
Widget
>[
NavigationRail
(
selectedIndex:
0
,
minExtendedWidth:
300
,
destinations:
_destinations
(),
extended:
extended
,
),
const
Expanded
(
child:
Text
(
'body'
),
),
],
),
);
},
),
),
);
final
RenderBox
rail
=
tester
.
firstRenderObject
<
RenderBox
>(
find
.
byType
(
NavigationRail
));
expect
(
rail
.
size
.
width
,
equals
(
72.0
));
stateSetter
(()
{
extended
=
true
;
});
await
tester
.
pumpAndSettle
();
expect
(
rail
.
size
.
width
,
equals
(
300.0
));
});
testWidgets
(
'Extended rail animation can be consumed'
,
(
WidgetTester
tester
)
async
{
bool
extended
=
false
;
Animation
<
double
>
animation
;
StateSetter
stateSetter
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
stateSetter
=
setState
;
return
Scaffold
(
body:
Row
(
children:
<
Widget
>[
NavigationRail
(
selectedIndex:
0
,
leading:
Builder
(
builder:
(
BuildContext
context
)
{
animation
=
NavigationRail
.
extendedAnimation
(
context
);
return
FloatingActionButton
(
onPressed:
()
{
},);
},
),
destinations:
_destinations
(),
extended:
extended
,
),
const
Expanded
(
child:
Text
(
'body'
),
),
],
),
);
},
),
),
);
expect
(
animation
.
isDismissed
,
isTrue
);
stateSetter
(()
{
extended
=
true
;
});
await
tester
.
pumpAndSettle
();
expect
(
animation
.
isCompleted
,
isTrue
);
});
testWidgets
(
'onDestinationSelected is called'
,
(
WidgetTester
tester
)
async
{
int
selectedIndex
;
await
_pumpNavigationRail
(
tester
,
navigationRail:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
onDestinationSelected:
(
int
index
)
{
selectedIndex
=
index
;
},
labelType:
NavigationRailLabelType
.
all
,
),
);
await
tester
.
tap
(
find
.
text
(
'Def'
));
expect
(
selectedIndex
,
1
);
await
tester
.
tap
(
find
.
text
(
'Ghi'
));
expect
(
selectedIndex
,
2
);
});
testWidgets
(
'Changing destinations animate when [labelType]=selected'
,
(
WidgetTester
tester
)
async
{
int
selectedIndex
=
0
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
Scaffold
(
body:
Row
(
children:
<
Widget
>[
NavigationRail
(
destinations:
_destinations
(),
selectedIndex:
selectedIndex
,
labelType:
NavigationRailLabelType
.
selected
,
onDestinationSelected:
(
int
index
)
{
setState
(()
{
selectedIndex
=
index
;
});
},
),
const
Expanded
(
child:
Text
(
'body'
),
),
],
),
);
},
),
),
);
// Tap the second destination.
await
tester
.
tap
(
find
.
byIcon
(
Icons
.
bookmark_border
));
expect
(
selectedIndex
,
1
);
// The second destination animates in.
expect
(
_labelOpacity
(
tester
,
'Def'
),
equals
(
0.0
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
_labelOpacity
(
tester
,
'Def'
),
equals
(
0.5
));
await
tester
.
pumpAndSettle
();
expect
(
_labelOpacity
(
tester
,
'Def'
),
equals
(
1.0
));
// Tap the third destination.
await
tester
.
tap
(
find
.
byIcon
(
Icons
.
star_border
));
expect
(
selectedIndex
,
2
);
// The second destination animates out quickly and the third destination
// animates in.
expect
(
_labelOpacity
(
tester
,
'Ghi'
),
equals
(
0.0
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
25
));
expect
(
_labelOpacity
(
tester
,
'Def'
),
equals
(
0.5
));
expect
(
_labelOpacity
(
tester
,
'Ghi'
),
equals
(
0.0
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
25
));
expect
(
_labelOpacity
(
tester
,
'Def'
),
equals
(
0.0
));
expect
(
_labelOpacity
(
tester
,
'Ghi'
),
equals
(
0.0
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
50
));
expect
(
_labelOpacity
(
tester
,
'Ghi'
),
equals
(
0.5
));
await
tester
.
pumpAndSettle
();
expect
(
_labelOpacity
(
tester
,
'Ghi'
),
equals
(
1.0
));
});
testWidgets
(
'Semantics - labelType=[none]'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semantics
=
SemanticsTester
(
tester
);
await
_pumpLocalizedTestRail
(
tester
,
labelType:
NavigationRailLabelType
.
none
);
expect
(
semantics
,
hasSemantics
(
_expectedSemantics
(),
ignoreId:
true
,
ignoreTransform:
true
,
ignoreRect:
true
));
semantics
.
dispose
();
});
testWidgets
(
'Semantics - labelType=[selected]'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semantics
=
SemanticsTester
(
tester
);
await
_pumpLocalizedTestRail
(
tester
,
labelType:
NavigationRailLabelType
.
selected
);
expect
(
semantics
,
hasSemantics
(
_expectedSemantics
(),
ignoreId:
true
,
ignoreTransform:
true
,
ignoreRect:
true
));
semantics
.
dispose
();
});
testWidgets
(
'Semantics - labelType=[all]'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semantics
=
SemanticsTester
(
tester
);
await
_pumpLocalizedTestRail
(
tester
,
labelType:
NavigationRailLabelType
.
all
);
expect
(
semantics
,
hasSemantics
(
_expectedSemantics
(),
ignoreId:
true
,
ignoreTransform:
true
,
ignoreRect:
true
));
semantics
.
dispose
();
});
testWidgets
(
'Semantics - extended'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semantics
=
SemanticsTester
(
tester
);
await
_pumpLocalizedTestRail
(
tester
,
extended:
true
);
expect
(
semantics
,
hasSemantics
(
_expectedSemantics
(),
ignoreId:
true
,
ignoreTransform:
true
,
ignoreRect:
true
));
semantics
.
dispose
();
});
}
TestSemantics
_expectedSemantics
(
)
{
return
TestSemantics
.
root
(
children:
<
TestSemantics
>[
TestSemantics
(
textDirection:
TextDirection
.
ltr
,
children:
<
TestSemantics
>[
TestSemantics
(
flags:
<
SemanticsFlag
>[
SemanticsFlag
.
scopesRoute
],
children:
<
TestSemantics
>[
TestSemantics
(
flags:
<
SemanticsFlag
>[
SemanticsFlag
.
isSelected
,
SemanticsFlag
.
isFocusable
,
],
actions:
<
SemanticsAction
>[
SemanticsAction
.
tap
],
label:
'Abc
\n
Tab 1 of 4'
,
textDirection:
TextDirection
.
ltr
,
),
TestSemantics
(
flags:
<
SemanticsFlag
>[
SemanticsFlag
.
isFocusable
],
actions:
<
SemanticsAction
>[
SemanticsAction
.
tap
],
label:
'Def
\n
Tab 2 of 4'
,
textDirection:
TextDirection
.
ltr
,
),
TestSemantics
(
flags:
<
SemanticsFlag
>[
SemanticsFlag
.
isFocusable
],
actions:
<
SemanticsAction
>[
SemanticsAction
.
tap
],
label:
'Ghi
\n
Tab 3 of 4'
,
textDirection:
TextDirection
.
ltr
,
),
TestSemantics
(
flags:
<
SemanticsFlag
>[
SemanticsFlag
.
isFocusable
],
actions:
<
SemanticsAction
>[
SemanticsAction
.
tap
],
label:
'Jkl
\n
Tab 4 of 4'
,
textDirection:
TextDirection
.
ltr
,
),
TestSemantics
(
label:
'body'
,
textDirection:
TextDirection
.
ltr
,
),
],
),
],
),
],
);
}
List
<
NavigationRailDestination
>
_destinations
()
{
return
const
<
NavigationRailDestination
>[
NavigationRailDestination
(
icon:
Icon
(
Icons
.
favorite_border
),
selectedIcon:
Icon
(
Icons
.
favorite
),
label:
Text
(
'Abc'
),
),
NavigationRailDestination
(
icon:
Icon
(
Icons
.
bookmark_border
),
selectedIcon:
Icon
(
Icons
.
bookmark
),
label:
Text
(
'Def'
),
),
NavigationRailDestination
(
icon:
Icon
(
Icons
.
star_border
),
selectedIcon:
Icon
(
Icons
.
star
),
label:
Text
(
'Ghi'
),
),
NavigationRailDestination
(
icon:
Icon
(
Icons
.
hotel
),
selectedIcon:
Icon
(
Icons
.
home
),
label:
Text
(
'Jkl'
),
),
];
}
Future
<
void
>
_pumpNavigationRail
(
WidgetTester
tester
,
{
double
textScaleFactor
=
1.0
,
NavigationRail
navigationRail
,
})
async
{
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Builder
(
builder:
(
BuildContext
context
)
{
return
MediaQuery
(
data:
MediaQuery
.
of
(
context
).
copyWith
(
textScaleFactor:
textScaleFactor
),
child:
Scaffold
(
body:
Row
(
children:
<
Widget
>[
navigationRail
,
const
Expanded
(
child:
Text
(
'body'
),
),
],
),
),
);
},
),
),
);
}
Future
<
void
>
_pumpLocalizedTestRail
(
WidgetTester
tester
,
{
NavigationRailLabelType
labelType
,
bool
extended
=
false
})
async
{
await
tester
.
pumpWidget
(
Localizations
(
locale:
const
Locale
(
'en'
,
'US'
),
delegates:
const
<
LocalizationsDelegate
<
dynamic
>>[
DefaultMaterialLocalizations
.
delegate
,
DefaultWidgetsLocalizations
.
delegate
,
],
child:
MaterialApp
(
home:
Scaffold
(
body:
Row
(
children:
<
Widget
>[
NavigationRail
(
selectedIndex:
0
,
extended:
extended
,
destinations:
_destinations
(),
labelType:
labelType
,
),
const
Expanded
(
child:
Text
(
'body'
),
),
],
),
),
),
),
);
}
RenderBox
_iconRenderBox
(
WidgetTester
tester
,
IconData
iconData
)
{
return
tester
.
firstRenderObject
<
RenderBox
>(
find
.
descendant
(
of:
find
.
byIcon
(
iconData
),
matching:
find
.
byType
(
RichText
),
),
);
}
RenderBox
_labelRenderBox
(
WidgetTester
tester
,
String
text
)
{
return
tester
.
firstRenderObject
<
RenderBox
>(
find
.
descendant
(
of:
find
.
text
(
text
),
matching:
find
.
byType
(
RichText
),
),
);
}
TextStyle
_iconStyle
(
WidgetTester
tester
,
IconData
icon
)
{
return
tester
.
widget
<
RichText
>(
find
.
descendant
(
of:
find
.
byIcon
(
icon
),
matching:
find
.
byType
(
RichText
),
),
).
text
.
style
;
}
Finder
_opacityAboveLabel
(
String
text
)
{
return
find
.
ancestor
(
of:
find
.
text
(
text
),
matching:
find
.
byType
(
Opacity
),
);
}
// Only valid when labelType != all.
double
_labelOpacity
(
WidgetTester
tester
,
String
text
)
{
final
Opacity
opacityWidget
=
tester
.
widget
<
Opacity
>(
find
.
ancestor
(
of:
find
.
text
(
text
),
matching:
find
.
byType
(
Opacity
),
),
);
return
opacityWidget
.
opacity
;
}
Material
_railMaterial
(
WidgetTester
tester
)
{
// The first material is for the rail, and the rest are for the destinations.
return
tester
.
firstWidget
<
Material
>(
find
.
descendant
(
of:
find
.
byType
(
NavigationRail
),
matching:
find
.
byType
(
Material
),
),
);
}
\ No newline at end of file
packages/flutter/test/material/navigation_rail_theme_test.dart
0 → 100644
View file @
0d111bc9
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/foundation.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
test
(
'copyWith, ==, hashCode basics'
,
()
{
expect
(
const
NavigationRailThemeData
(),
const
NavigationRailThemeData
().
copyWith
());
expect
(
const
NavigationRailThemeData
().
hashCode
,
const
NavigationRailThemeData
().
copyWith
().
hashCode
);
});
testWidgets
(
'Default values are used when no NavigationRail or NavigationRailThemeData properties are specified'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
body:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
),
),
),
);
expect
(
_railMaterial
(
tester
).
color
,
ThemeData
().
colorScheme
.
surface
);
expect
(
_railMaterial
(
tester
).
elevation
,
0
);
expect
(
_selectedIconTheme
(
tester
).
size
,
24.0
);
expect
(
_selectedIconTheme
(
tester
).
color
,
ThemeData
().
colorScheme
.
primary
);
expect
(
_selectedIconTheme
(
tester
).
opacity
,
0.64
);
expect
(
_unselectedIconTheme
(
tester
).
size
,
24.0
);
expect
(
_unselectedIconTheme
(
tester
).
color
,
ThemeData
().
colorScheme
.
onSurface
);
expect
(
_unselectedIconTheme
(
tester
).
opacity
,
1.0
);
expect
(
_selectedLabelStyle
(
tester
).
fontSize
,
14.0
);
expect
(
_unselectedLabelStyle
(
tester
).
fontSize
,
14.0
);
expect
(
_destinationsAlign
(
tester
).
alignment
,
Alignment
.
topCenter
);
expect
(
_labelType
(
tester
),
NavigationRailLabelType
.
none
);
});
testWidgets
(
'NavigationRailThemeData values are used when no NavigationRail properties are specified'
,
(
WidgetTester
tester
)
async
{
const
Color
backgroundColor
=
Color
(
0x00000001
);
const
double
elevation
=
7.0
;
const
double
selectedIconSize
=
25.0
;
const
double
unselectedIconSize
=
23.0
;
const
Color
selectedIconColor
=
Color
(
0x00000002
);
const
Color
unselectedIconColor
=
Color
(
0x00000003
);
const
double
selectedIconOpacity
=
0.99
;
const
double
unselectedIconOpacity
=
0.98
;
const
double
selectedLabelFontSize
=
13.0
;
const
double
unselectedLabelFontSize
=
11.0
;
const
double
groupAlignment
=
0.0
;
const
NavigationRailLabelType
labelType
=
NavigationRailLabelType
.
all
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
body:
NavigationRailTheme
(
data:
const
NavigationRailThemeData
(
backgroundColor:
backgroundColor
,
elevation:
elevation
,
selectedIconTheme:
IconThemeData
(
size:
selectedIconSize
,
color:
selectedIconColor
,
opacity:
selectedIconOpacity
,
),
unselectedIconTheme:
IconThemeData
(
size:
unselectedIconSize
,
color:
unselectedIconColor
,
opacity:
unselectedIconOpacity
,
),
selectedLabelTextStyle:
TextStyle
(
fontSize:
selectedLabelFontSize
),
unselectedLabelTextStyle:
TextStyle
(
fontSize:
unselectedLabelFontSize
),
groupAlignment:
groupAlignment
,
labelType:
labelType
,
),
child:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
),
),
),
),
);
expect
(
_railMaterial
(
tester
).
color
,
backgroundColor
);
expect
(
_railMaterial
(
tester
).
elevation
,
elevation
);
expect
(
_selectedIconTheme
(
tester
).
size
,
selectedIconSize
);
expect
(
_selectedIconTheme
(
tester
).
color
,
selectedIconColor
);
expect
(
_selectedIconTheme
(
tester
).
opacity
,
selectedIconOpacity
);
expect
(
_unselectedIconTheme
(
tester
).
size
,
unselectedIconSize
);
expect
(
_unselectedIconTheme
(
tester
).
color
,
unselectedIconColor
);
expect
(
_unselectedIconTheme
(
tester
).
opacity
,
unselectedIconOpacity
);
expect
(
_selectedLabelStyle
(
tester
).
fontSize
,
selectedLabelFontSize
);
expect
(
_unselectedLabelStyle
(
tester
).
fontSize
,
unselectedLabelFontSize
);
expect
(
_destinationsAlign
(
tester
).
alignment
,
Alignment
.
center
);
expect
(
_labelType
(
tester
),
labelType
);
});
testWidgets
(
'NavigationRail values take priority over NavigationRailThemeData values when both properties are specified'
,
(
WidgetTester
tester
)
async
{
const
Color
backgroundColor
=
Color
(
0x00000001
);
const
double
elevation
=
7.0
;
const
double
selectedIconSize
=
25.0
;
const
double
unselectedIconSize
=
23.0
;
const
Color
selectedIconColor
=
Color
(
0x00000002
);
const
Color
unselectedIconColor
=
Color
(
0x00000003
);
const
double
selectedIconOpacity
=
0.99
;
const
double
unselectedIconOpacity
=
0.98
;
const
double
selectedLabelFontSize
=
13.0
;
const
double
unselectedLabelFontSize
=
11.0
;
const
double
groupAlignment
=
0.0
;
const
NavigationRailLabelType
labelType
=
NavigationRailLabelType
.
all
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
body:
NavigationRailTheme
(
data:
const
NavigationRailThemeData
(
backgroundColor:
Color
(
0x00000099
),
elevation:
5
,
selectedIconTheme:
IconThemeData
(
size:
31.0
,
color:
Color
(
0x00000098
),
opacity:
0.81
,
),
unselectedIconTheme:
IconThemeData
(
size:
37.0
,
color:
Color
(
0x00000097
),
opacity:
0.82
,
),
selectedLabelTextStyle:
TextStyle
(
fontSize:
9.0
),
unselectedLabelTextStyle:
TextStyle
(
fontSize:
7.0
),
groupAlignment:
1.0
,
labelType:
NavigationRailLabelType
.
selected
,
),
child:
NavigationRail
(
selectedIndex:
0
,
destinations:
_destinations
(),
backgroundColor:
backgroundColor
,
elevation:
elevation
,
selectedIconTheme:
const
IconThemeData
(
size:
selectedIconSize
,
color:
selectedIconColor
,
opacity:
selectedIconOpacity
,
),
unselectedIconTheme:
const
IconThemeData
(
size:
unselectedIconSize
,
color:
unselectedIconColor
,
opacity:
unselectedIconOpacity
,
),
selectedLabelTextStyle:
const
TextStyle
(
fontSize:
selectedLabelFontSize
),
unselectedLabelTextStyle:
const
TextStyle
(
fontSize:
unselectedLabelFontSize
),
groupAlignment:
groupAlignment
,
labelType:
labelType
,
),
),
),
),
);
expect
(
_railMaterial
(
tester
).
color
,
backgroundColor
);
expect
(
_railMaterial
(
tester
).
elevation
,
elevation
);
expect
(
_selectedIconTheme
(
tester
).
size
,
selectedIconSize
);
expect
(
_selectedIconTheme
(
tester
).
color
,
selectedIconColor
);
expect
(
_selectedIconTheme
(
tester
).
opacity
,
selectedIconOpacity
);
expect
(
_unselectedIconTheme
(
tester
).
size
,
unselectedIconSize
);
expect
(
_unselectedIconTheme
(
tester
).
color
,
unselectedIconColor
);
expect
(
_unselectedIconTheme
(
tester
).
opacity
,
unselectedIconOpacity
);
expect
(
_selectedLabelStyle
(
tester
).
fontSize
,
selectedLabelFontSize
);
expect
(
_unselectedLabelStyle
(
tester
).
fontSize
,
unselectedLabelFontSize
);
expect
(
_destinationsAlign
(
tester
).
alignment
,
Alignment
.
center
);
expect
(
_labelType
(
tester
),
labelType
);
});
testWidgets
(
'Default debugFillProperties'
,
(
WidgetTester
tester
)
async
{
final
DiagnosticPropertiesBuilder
builder
=
DiagnosticPropertiesBuilder
();
const
NavigationRailThemeData
().
debugFillProperties
(
builder
);
final
List
<
String
>
description
=
builder
.
properties
.
where
((
DiagnosticsNode
node
)
=>
!
node
.
isFiltered
(
DiagnosticLevel
.
info
))
.
map
((
DiagnosticsNode
node
)
=>
node
.
toString
())
.
toList
();
expect
(
description
,
<
String
>[]);
});
testWidgets
(
'Custom debugFillProperties'
,
(
WidgetTester
tester
)
async
{
final
DiagnosticPropertiesBuilder
builder
=
DiagnosticPropertiesBuilder
();
const
NavigationRailThemeData
(
backgroundColor:
Color
(
0x00000099
),
elevation:
5
,
selectedIconTheme:
IconThemeData
(
color:
Color
(
0x00000098
)),
unselectedIconTheme:
IconThemeData
(
color:
Color
(
0x00000097
)),
selectedLabelTextStyle:
TextStyle
(
fontSize:
9.0
),
unselectedLabelTextStyle:
TextStyle
(
fontSize:
7.0
),
groupAlignment:
1.0
,
labelType:
NavigationRailLabelType
.
selected
,
).
debugFillProperties
(
builder
);
final
List
<
String
>
description
=
builder
.
properties
.
where
((
DiagnosticsNode
node
)
=>
!
node
.
isFiltered
(
DiagnosticLevel
.
info
))
.
map
((
DiagnosticsNode
node
)
=>
node
.
toString
())
.
toList
();
expect
(
description
[
0
],
'backgroundColor: Color(0x00000099)'
);
expect
(
description
[
1
],
'elevation: 5.0'
);
expect
(
description
[
2
],
'unselectedLabelTextStyle: TextStyle(inherit: true, size: 7.0)'
);
expect
(
description
[
3
],
'selectedLabelTextStyle: TextStyle(inherit: true, size: 9.0)'
);
// Ignore instance address for IconThemeData.
expect
(
description
[
4
].
contains
(
'unselectedIconTheme: IconThemeData'
),
isTrue
);
expect
(
description
[
4
].
contains
(
'(color: Color(0x00000097))'
),
isTrue
);
expect
(
description
[
5
].
contains
(
'selectedIconTheme: IconThemeData'
),
isTrue
);
expect
(
description
[
5
].
contains
(
'(color: Color(0x00000098))'
),
isTrue
);
expect
(
description
[
6
],
'groupAlignment: 1.0'
);
expect
(
description
[
7
],
'labelType: NavigationRailLabelType.selected'
);
});
}
List
<
NavigationRailDestination
>
_destinations
()
{
return
const
<
NavigationRailDestination
>[
NavigationRailDestination
(
icon:
Icon
(
Icons
.
favorite_border
),
selectedIcon:
Icon
(
Icons
.
favorite
),
label:
Text
(
'Abc'
),
),
NavigationRailDestination
(
icon:
Icon
(
Icons
.
star_border
),
selectedIcon:
Icon
(
Icons
.
star
),
label:
Text
(
'Def'
),
),
];
}
Material
_railMaterial
(
WidgetTester
tester
)
{
// The first material is for the rail, and the rest are for the destinations.
return
tester
.
firstWidget
<
Material
>(
find
.
descendant
(
of:
find
.
byType
(
NavigationRail
),
matching:
find
.
byType
(
Material
),
),
);
}
IconThemeData
_selectedIconTheme
(
WidgetTester
tester
)
{
return
_iconTheme
(
tester
,
Icons
.
favorite
);
}
IconThemeData
_unselectedIconTheme
(
WidgetTester
tester
)
{
return
_iconTheme
(
tester
,
Icons
.
star_border
);
}
IconThemeData
_iconTheme
(
WidgetTester
tester
,
IconData
icon
)
{
// The first IconTheme is the one added by the navigation rail.
return
tester
.
firstWidget
<
IconTheme
>(
find
.
ancestor
(
of:
find
.
byIcon
(
icon
),
matching:
find
.
byType
(
IconTheme
),
),
).
data
;
}
TextStyle
_selectedLabelStyle
(
WidgetTester
tester
)
{
return
tester
.
widget
<
RichText
>(
find
.
descendant
(
of:
find
.
text
(
'Abc'
),
matching:
find
.
byType
(
RichText
),
),
).
text
.
style
;
}
TextStyle
_unselectedLabelStyle
(
WidgetTester
tester
)
{
return
tester
.
widget
<
RichText
>(
find
.
descendant
(
of:
find
.
text
(
'Def'
),
matching:
find
.
byType
(
RichText
),
),
).
text
.
style
;
}
Align
_destinationsAlign
(
WidgetTester
tester
)
{
// The first Expanded widget is the one within the main Column for the rail
// content.
return
tester
.
firstWidget
<
Align
>(
find
.
descendant
(
of:
find
.
byType
(
Expanded
),
matching:
find
.
byType
(
Align
),
),
);
}
NavigationRailLabelType
_labelType
(
WidgetTester
tester
)
{
if
(
_opacityAboveLabel
(
'Abc'
).
evaluate
().
isNotEmpty
&&
_opacityAboveLabel
(
'Def'
).
evaluate
().
isNotEmpty
)
{
return
_labelOpacity
(
tester
,
'Abc'
)
==
1
?
NavigationRailLabelType
.
selected
:
NavigationRailLabelType
.
none
;
}
else
{
return
NavigationRailLabelType
.
all
;
}
}
Finder
_opacityAboveLabel
(
String
text
)
{
return
find
.
ancestor
(
of:
find
.
text
(
text
),
matching:
find
.
byType
(
Opacity
),
);
}
// Only valid when labelType != all.
double
_labelOpacity
(
WidgetTester
tester
,
String
text
)
{
final
Opacity
opacityWidget
=
tester
.
widget
<
Opacity
>(
find
.
ancestor
(
of:
find
.
text
(
text
),
matching:
find
.
byType
(
Opacity
),
),
);
return
opacityWidget
.
opacity
;
}
packages/flutter/test/material/theme_data_test.dart
View file @
0d111bc9
...
...
@@ -256,6 +256,7 @@ void main() {
colorScheme:
const
ColorScheme
.
light
(),
dialogTheme:
const
DialogTheme
(
backgroundColor:
Colors
.
black
),
floatingActionButtonTheme:
const
FloatingActionButtonThemeData
(
backgroundColor:
Colors
.
black
),
navigationRailTheme:
const
NavigationRailThemeData
(
backgroundColor:
Colors
.
black
),
typography:
Typography
.
material2018
(
platform:
TargetPlatform
.
android
),
cupertinoOverrideTheme:
null
,
snackBarTheme:
const
SnackBarThemeData
(
backgroundColor:
Colors
.
black
),
...
...
@@ -335,6 +336,7 @@ void main() {
colorScheme:
const
ColorScheme
.
light
(),
dialogTheme:
const
DialogTheme
(
backgroundColor:
Colors
.
white
),
floatingActionButtonTheme:
const
FloatingActionButtonThemeData
(
backgroundColor:
Colors
.
white
),
navigationRailTheme:
const
NavigationRailThemeData
(
backgroundColor:
Colors
.
white
),
typography:
Typography
.
material2018
(
platform:
TargetPlatform
.
iOS
),
cupertinoOverrideTheme:
ThemeData
.
light
().
cupertinoOverrideTheme
,
snackBarTheme:
const
SnackBarThemeData
(
backgroundColor:
Colors
.
white
),
...
...
@@ -400,6 +402,7 @@ void main() {
colorScheme:
otherTheme
.
colorScheme
,
dialogTheme:
otherTheme
.
dialogTheme
,
floatingActionButtonTheme:
otherTheme
.
floatingActionButtonTheme
,
navigationRailTheme:
otherTheme
.
navigationRailTheme
,
typography:
otherTheme
.
typography
,
cupertinoOverrideTheme:
otherTheme
.
cupertinoOverrideTheme
,
snackBarTheme:
otherTheme
.
snackBarTheme
,
...
...
@@ -466,6 +469,7 @@ void main() {
expect
(
themeDataCopy
.
colorScheme
,
equals
(
otherTheme
.
colorScheme
));
expect
(
themeDataCopy
.
dialogTheme
,
equals
(
otherTheme
.
dialogTheme
));
expect
(
themeDataCopy
.
floatingActionButtonTheme
,
equals
(
otherTheme
.
floatingActionButtonTheme
));
expect
(
themeDataCopy
.
navigationRailTheme
,
equals
(
otherTheme
.
navigationRailTheme
));
expect
(
themeDataCopy
.
typography
,
equals
(
otherTheme
.
typography
));
expect
(
themeDataCopy
.
cupertinoOverrideTheme
,
equals
(
otherTheme
.
cupertinoOverrideTheme
));
expect
(
themeDataCopy
.
snackBarTheme
,
equals
(
otherTheme
.
snackBarTheme
));
...
...
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