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
33777817
Unverified
Commit
33777817
authored
Jun 24, 2020
by
Ayush Bherwani
Committed by
GitHub
Jun 24, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[AppBar] adds toolbarHeight property to customize AppBar height (#59405)
parent
4d2ddb91
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
119 additions
and
24 deletions
+119
-24
app_bar.dart
packages/flutter/lib/src/material/app_bar.dart
+48
-24
app_bar_test.dart
packages/flutter/test/material/app_bar_test.dart
+71
-0
No files found.
packages/flutter/lib/src/material/app_bar.dart
View file @
33777817
...
...
@@ -28,18 +28,20 @@ import 'theme.dart';
const
double
_kLeadingWidth
=
kToolbarHeight
;
// So the leading button is square.
const
double
_kMaxTitleTextScaleFactor
=
1.34
;
// TODO(perc): Add link to Material spec when available, https://github.com/flutter/flutter/issues/58769.
// Bottom justify the
kT
oolbarHeight child which may overflow the top.
// Bottom justify the
t
oolbarHeight child which may overflow the top.
class
_ToolbarContainerLayout
extends
SingleChildLayoutDelegate
{
const
_ToolbarContainerLayout
();
const
_ToolbarContainerLayout
(
this
.
toolbarHeight
);
final
double
toolbarHeight
;
@override
BoxConstraints
getConstraintsForChild
(
BoxConstraints
constraints
)
{
return
constraints
.
tighten
(
height:
kT
oolbarHeight
);
return
constraints
.
tighten
(
height:
t
oolbarHeight
);
}
@override
Size
getSize
(
BoxConstraints
constraints
)
{
return
Size
(
constraints
.
maxWidth
,
kT
oolbarHeight
);
return
Size
(
constraints
.
maxWidth
,
t
oolbarHeight
);
}
@override
...
...
@@ -48,7 +50,8 @@ class _ToolbarContainerLayout extends SingleChildLayoutDelegate {
}
@override
bool
shouldRelayout
(
_ToolbarContainerLayout
oldDelegate
)
=>
false
;
bool
shouldRelayout
(
_ToolbarContainerLayout
oldDelegate
)
=>
toolbarHeight
!=
oldDelegate
.
toolbarHeight
;
}
// TODO(eseidel): Toolbar needs to change size based on orientation:
...
...
@@ -204,13 +207,14 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
this
.
titleSpacing
=
NavigationToolbar
.
kMiddleSpacing
,
this
.
toolbarOpacity
=
1.0
,
this
.
bottomOpacity
=
1.0
,
this
.
toolbarHeight
,
})
:
assert
(
automaticallyImplyLeading
!=
null
),
assert
(
elevation
==
null
||
elevation
>=
0.0
),
assert
(
primary
!=
null
),
assert
(
titleSpacing
!=
null
),
assert
(
toolbarOpacity
!=
null
),
assert
(
bottomOpacity
!=
null
),
preferredSize
=
Size
.
fromHeight
(
kToolbarHeight
+
(
bottom
?.
preferredSize
?.
height
??
0.0
)),
preferredSize
=
Size
.
fromHeight
(
toolbarHeight
??
kToolbarHeight
+
(
bottom
?.
preferredSize
?.
height
??
0.0
)),
super
(
key:
key
);
/// A widget to display before the [title].
...
...
@@ -219,7 +223,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
///
/// Becomes the leading component of the [NavigationToolBar] built
/// by this widget. The [leading] widget's width and height are constrained to
/// be no bigger than
toolbar's height, which is [kToolbarHeight]
.
/// be no bigger than
[kToolbarHeight] and [toolbarHeight] respectively
.
///
/// If this is null and [automaticallyImplyLeading] is set to true, the
/// [AppBar] will imply an appropriate widget. For example, if the [AppBar] is
...
...
@@ -276,12 +280,12 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// The [title]'s width is constrained to fit within the remaining space
/// between the toolbar's [leading] and [actions] widgets. Its height is
/// _not_ constrained. The [title] is vertically centered and clipped to fit
/// within the toolbar, whose height is [
kT
oolbarHeight]. Typically this
/// within the toolbar, whose height is [
t
oolbarHeight]. Typically this
/// isn't noticeable because a simple [Text] [title] will fit within the
/// toolbar by default. On the other hand, it is noticeable when a
/// widget with an intrinsic height that is greater than [
kT
oolbarHeight]
/// widget with an intrinsic height that is greater than [
t
oolbarHeight]
/// is used as the [title]. For example, when the height of an Image used
/// as the [title] exceeds [
kT
oolbarHeight], it will be centered and
/// as the [title] exceeds [
t
oolbarHeight], it will be centered and
/// clipped (top and bottom), which may be undesirable. In cases like this
/// the height of the [title] widget can be constrained. For example:
///
...
...
@@ -290,9 +294,10 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// home: Scaffold(
/// appBar: AppBar(
/// title: SizedBox(
/// height:
kT
oolbarHeight,
///
child: child: Image.asset(logoAsset),
/// height:
t
oolbarHeight,
/// child: child: Image.asset(logoAsset),
/// ),
/// toolbarHeight: toolbarHeight,
/// ),
/// )
/// ```
...
...
@@ -306,7 +311,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
///
/// The [actions] become the trailing component of the [NavigationToolBar] built
/// by this widget. The height of each action is constrained to be no bigger
/// than the
toolbar's height, which is [kT
oolbarHeight].
/// than the
[t
oolbarHeight].
final
List
<
Widget
>
actions
;
/// This widget is stacked behind the toolbar and the tab bar. It's height will
...
...
@@ -433,13 +438,18 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// bar is scrolled.
final
double
bottomOpacity
;
/// A size whose height is the sum of [
kT
oolbarHeight] and the [bottom] widget's
/// A size whose height is the sum of [
t
oolbarHeight] and the [bottom] widget's
/// preferred height.
///
/// [Scaffold] uses this size to set its app bar's height.
@override
final
Size
preferredSize
;
/// Defines the height of the toolbar component of an [AppBar].
///
/// By default, the value of `toolbarHeight` is [kToolbarHeight].
final
double
toolbarHeight
;
bool
_getEffectiveCenterTitle
(
ThemeData
theme
)
{
if
(
centerTitle
!=
null
)
return
centerTitle
;
...
...
@@ -489,6 +499,8 @@ class _AppBarState extends State<AppBar> {
final
bool
canPop
=
parentRoute
?.
canPop
??
false
;
final
bool
useCloseButton
=
parentRoute
is
PageRoute
<
dynamic
>
&&
parentRoute
.
fullscreenDialog
;
final
double
toolbarHeight
=
widget
.
toolbarHeight
??
kToolbarHeight
;
IconThemeData
overallIconTheme
=
widget
.
iconTheme
??
appBarTheme
.
iconTheme
??
theme
.
primaryIconTheme
;
...
...
@@ -618,11 +630,11 @@ class _AppBarState extends State<AppBar> {
middleSpacing:
widget
.
titleSpacing
,
);
// If the toolbar is allocated less than
kT
oolbarHeight make it
// If the toolbar is allocated less than
t
oolbarHeight make it
// appear to scroll upwards within its shrinking container.
Widget
appBar
=
ClipRect
(
child:
CustomSingleChildLayout
(
delegate:
const
_ToolbarContainerLayout
(
),
delegate:
_ToolbarContainerLayout
(
toolbarHeight
),
child:
IconTheme
.
merge
(
data:
overallIconTheme
,
child:
DefaultTextStyle
(
...
...
@@ -638,7 +650,7 @@ class _AppBarState extends State<AppBar> {
children:
<
Widget
>[
Flexible
(
child:
ConstrainedBox
(
constraints:
const
BoxConstraints
(
maxHeight:
kT
oolbarHeight
),
constraints:
BoxConstraints
(
maxHeight:
t
oolbarHeight
),
child:
appBar
,
),
),
...
...
@@ -788,6 +800,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
@required
this
.
snapConfiguration
,
@required
this
.
stretchConfiguration
,
@required
this
.
shape
,
@required
this
.
toolbarHeight
,
})
:
assert
(
primary
||
topPadding
==
0.0
),
_bottomHeight
=
bottom
?.
preferredSize
?.
height
??
0.0
;
...
...
@@ -815,6 +828,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
final
bool
floating
;
final
bool
pinned
;
final
ShapeBorder
shape
;
final
double
toolbarHeight
;
final
double
_bottomHeight
;
...
...
@@ -822,7 +836,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
double
get
minExtent
=>
collapsedHeight
;
@override
double
get
maxExtent
=>
math
.
max
(
topPadding
+
(
expandedHeight
??
kToolbarHeight
+
_bottomHeight
),
minExtent
);
double
get
maxExtent
=>
math
.
max
(
topPadding
+
(
expandedHeight
??
(
toolbarHeight
??
kToolbarHeight
)
+
_bottomHeight
),
minExtent
);
@override
final
FloatingHeaderSnapConfiguration
snapConfiguration
;
...
...
@@ -833,12 +847,12 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
@override
Widget
build
(
BuildContext
context
,
double
shrinkOffset
,
bool
overlapsContent
)
{
final
double
visibleMainHeight
=
maxExtent
-
shrinkOffset
-
topPadding
;
final
double
extraToolbarHeight
=
math
.
max
(
minExtent
-
_bottomHeight
-
topPadding
-
kToolbarHeight
,
0.0
);
final
double
extraToolbarHeight
=
math
.
max
(
minExtent
-
_bottomHeight
-
topPadding
-
(
toolbarHeight
??
kToolbarHeight
)
,
0.0
);
final
double
visibleToolbarHeight
=
visibleMainHeight
-
_bottomHeight
-
extraToolbarHeight
;
final
bool
isPinnedWithOpacityFade
=
pinned
&&
floating
&&
bottom
!=
null
&&
extraToolbarHeight
==
0.0
;
final
double
toolbarOpacity
=
!
pinned
||
isPinnedWithOpacityFade
?
(
visibleToolbarHeight
/
kToolbarHeight
).
clamp
(
0.0
,
1.0
)
as
double
?
(
visibleToolbarHeight
/
(
toolbarHeight
??
kToolbarHeight
)
).
clamp
(
0.0
,
1.0
)
as
double
:
1.0
;
final
Widget
appBar
=
FlexibleSpaceBar
.
createSettings
(
...
...
@@ -869,6 +883,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
shape:
shape
,
toolbarOpacity:
toolbarOpacity
,
bottomOpacity:
pinned
?
1.0
:
((
visibleMainHeight
/
_bottomHeight
).
clamp
(
0.0
,
1.0
)
as
double
),
toolbarHeight:
toolbarHeight
,
),
);
return
floating
?
_FloatingAppBar
(
child:
appBar
)
:
appBar
;
...
...
@@ -899,7 +914,8 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
||
floating
!=
oldDelegate
.
floating
||
snapConfiguration
!=
oldDelegate
.
snapConfiguration
||
stretchConfiguration
!=
oldDelegate
.
stretchConfiguration
||
forceElevated
!=
oldDelegate
.
forceElevated
;
||
forceElevated
!=
oldDelegate
.
forceElevated
||
toolbarHeight
!=
oldDelegate
.
toolbarHeight
;
}
@override
...
...
@@ -1020,6 +1036,7 @@ class SliverAppBar extends StatefulWidget {
this
.
stretchTriggerOffset
=
100.0
,
this
.
onStretchTrigger
,
this
.
shape
,
this
.
toolbarHeight
=
kToolbarHeight
,
})
:
assert
(
automaticallyImplyLeading
!=
null
),
assert
(
forceElevated
!=
null
),
assert
(
primary
!=
null
),
...
...
@@ -1028,9 +1045,10 @@ class SliverAppBar extends StatefulWidget {
assert
(
pinned
!=
null
),
assert
(
snap
!=
null
),
assert
(
stretch
!=
null
),
assert
(
toolbarHeight
!=
null
),
assert
(
floating
||
!
snap
,
'The "snap" argument only makes sense for floating app bars.'
),
assert
(
stretchTriggerOffset
>
0.0
),
assert
(
collapsedHeight
==
null
||
collapsedHeight
>
kToolbarHeight
,
'The "collapsedHeight" argument has to be larger than [kT
oolbarHeight].'
),
assert
(
collapsedHeight
==
null
||
collapsedHeight
>
toolbarHeight
,
'The "collapsedHeight" argument has to be larger than [t
oolbarHeight].'
),
super
(
key:
key
);
/// A widget to display before the [title].
...
...
@@ -1198,7 +1216,7 @@ class SliverAppBar extends StatefulWidget {
/// Defines the height of the app bar when it is collapsed.
///
/// By default, the collapsed height is [
kT
oolbarHeight]. If [bottom] widget
/// By default, the collapsed height is [
t
oolbarHeight]. If [bottom] widget
/// is specified, then its [bottom.preferredSize.height] is added to the
/// height. If [primary] is true, then the [MediaQuery] top padding,
/// [MediaQueryData.padding.top], is added as well.
...
...
@@ -1312,6 +1330,11 @@ class SliverAppBar extends StatefulWidget {
/// offset specified by [stretchTriggerOffset].
final
AsyncCallback
onStretchTrigger
;
/// Defines the height of the toolbar component of an [AppBar].
///
/// By default, the value of `toolbarHeight` is [kToolbarHeight].
final
double
toolbarHeight
;
@override
_SliverAppBarState
createState
()
=>
_SliverAppBarState
();
}
...
...
@@ -1368,7 +1391,7 @@ class _SliverAppBarState extends State<SliverAppBar> with TickerProviderStateMix
final
double
topPadding
=
widget
.
primary
?
MediaQuery
.
of
(
context
).
padding
.
top
:
0.0
;
final
double
collapsedHeight
=
(
widget
.
pinned
&&
widget
.
floating
&&
widget
.
bottom
!=
null
)
?
(
widget
.
collapsedHeight
??
0.0
)
+
bottomHeight
+
topPadding
:
(
widget
.
collapsedHeight
??
kT
oolbarHeight
)
+
bottomHeight
+
topPadding
;
:
(
widget
.
collapsedHeight
??
widget
.
t
oolbarHeight
)
+
bottomHeight
+
topPadding
;
return
MediaQuery
.
removePadding
(
context:
context
,
...
...
@@ -1403,6 +1426,7 @@ class _SliverAppBarState extends State<SliverAppBar> with TickerProviderStateMix
shape:
widget
.
shape
,
snapConfiguration:
_snapConfiguration
,
stretchConfiguration:
_stretchConfiguration
,
toolbarHeight:
widget
.
toolbarHeight
,
),
),
);
...
...
packages/flutter/test/material/app_bar_test.dart
View file @
33777817
...
...
@@ -18,6 +18,7 @@ Widget buildSliverAppBarApp({
double
collapsedHeight
,
double
expandedHeight
,
bool
snap
=
false
,
double
toolbarHeight
=
kToolbarHeight
,
})
{
return
Localizations
(
locale:
const
Locale
(
'en'
,
'US'
),
...
...
@@ -41,6 +42,7 @@ Widget buildSliverAppBarApp({
pinned:
pinned
,
collapsedHeight:
collapsedHeight
,
expandedHeight:
expandedHeight
,
toolbarHeight:
toolbarHeight
,
snap:
snap
,
bottom:
TabBar
(
tabs:
<
String
>[
'A'
,
'B'
,
'C'
].
map
<
Widget
>((
String
t
)
=>
Tab
(
text:
'TAB
$t
'
)).
toList
(),
...
...
@@ -1940,4 +1942,73 @@ void main() {
expect
(
tester
.
getRect
(
appBarTitle
),
const
Rect
.
fromLTRB
(
200
,
-
12
,
800.0
-
200.0
,
68
));
expect
(
tester
.
getCenter
(
appBarTitle
).
dy
,
tester
.
getCenter
(
toolbar
).
dy
);
});
testWidgets
(
'AppBar respects toolbarHeight'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
appBar:
AppBar
(
title:
const
Text
(
'Title'
),
toolbarHeight:
48
,
),
body:
Container
(),
),
)
);
expect
(
appBarHeight
(
tester
),
48
);
});
testWidgets
(
'SliverAppBar default collapsedHeight with respect to toolbarHeight'
,
(
WidgetTester
tester
)
async
{
const
double
toolbarHeight
=
100.0
;
await
tester
.
pumpWidget
(
buildSliverAppBarApp
(
floating:
false
,
pinned:
false
,
toolbarHeight:
toolbarHeight
,
));
final
ScrollController
controller
=
primaryScrollController
(
tester
);
final
double
initialTabBarHeight
=
tabBarHeight
(
tester
);
// Scroll the not-pinned appbar out of view, to its collapsed height.
controller
.
jumpTo
(
300.0
);
await
tester
.
pump
();
expect
(
find
.
byType
(
SliverAppBar
),
findsNothing
);
// By default, the collapsedHeight is toolbarHeight + bottom.preferredSize.height,
// in this case initialTabBarHeight.
expect
(
appBarHeight
(
tester
),
toolbarHeight
+
initialTabBarHeight
);
});
testWidgets
(
'SliverAppBar collapsedHeight with toolbarHeight'
,
(
WidgetTester
tester
)
async
{
const
double
toolbarHeight
=
100.0
;
const
double
collapsedHeight
=
150.0
;
await
tester
.
pumpWidget
(
buildSliverAppBarApp
(
floating:
false
,
pinned:
false
,
toolbarHeight:
toolbarHeight
,
collapsedHeight:
collapsedHeight
));
final
ScrollController
controller
=
primaryScrollController
(
tester
);
final
double
initialTabBarHeight
=
tabBarHeight
(
tester
);
// Scroll the not-pinned appbar out of view, to its collapsed height.
controller
.
jumpTo
(
300.0
);
await
tester
.
pump
();
expect
(
find
.
byType
(
SliverAppBar
),
findsNothing
);
expect
(
appBarHeight
(
tester
),
collapsedHeight
+
initialTabBarHeight
);
});
test
(
'SliverApp toolbarHeight cannot be null'
,
()
{
try
{
SliverAppBar
(
toolbarHeight:
null
,
);
}
on
AssertionError
catch
(
error
)
{
expect
(
error
.
toString
(),
contains
(
'toolbarHeight != null'
));
expect
(
error
.
toString
(),
contains
(
'is not true'
));
}
});
}
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