Commit f3b3ff47 authored by Mehmet Fidanboylu's avatar Mehmet Fidanboylu Committed by GitHub

Allow apps to change the title spacing in app bar. (#12076)

* Allow apps to change the title margin in app bar.

* Fix documentation links and the shouldLayout function in the _ToolbarLayout

* Rename margin to spacing

* review comments

* Test fixes. Now we also test the width to make sure enough space is left for trailing widget

* Expose the middle spacing default and use it in app bar.

* Fix analyzer break

* Doc fixes due to review
parent 3bab533c
......@@ -148,11 +148,13 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
this.textTheme,
this.primary: true,
this.centerTitle,
this.titleSpacing: NavigationToolbar.kMiddleSpacing,
this.toolbarOpacity: 1.0,
this.bottomOpacity: 1.0,
}) : assert(automaticallyImplyLeading != null),
assert(elevation != null),
assert(primary != null),
assert(titleSpacing != null),
assert(toolbarOpacity != null),
assert(bottomOpacity != null),
preferredSize = new Size.fromHeight(kToolbarHeight + (bottom?.preferredSize?.height ?? 0.0)),
......@@ -268,6 +270,13 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// Defaults to being adapted to the current [TargetPlatform].
final bool centerTitle;
/// The spacing around [title] content on the horizontal axis. This spacing is
/// applied even if there is no [leading] content or [actions]. If you want
/// [title] to take all the space available, set this value to 0.0.
///
/// Defaults to [NavigationToolbar.kMiddleSpacing].
final double titleSpacing;
/// How opaque the toolbar part of the app bar is.
///
/// A value of 1.0 is fully opaque, and a value of 0.0 is fully transparent.
......@@ -393,6 +402,7 @@ class _AppBarState extends State<AppBar> {
middle: title,
trailing: actions,
centerMiddle: widget._getEffectiveCenterTitle(themeData),
middleSpacing: widget.titleSpacing,
),
);
......@@ -528,6 +538,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
@required this.textTheme,
@required this.primary,
@required this.centerTitle,
@required this.titleSpacing,
@required this.expandedHeight,
@required this.collapsedHeight,
@required this.topPadding,
......@@ -551,6 +562,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
final TextTheme textTheme;
final bool primary;
final bool centerTitle;
final double titleSpacing;
final double expandedHeight;
final double collapsedHeight;
final double topPadding;
......@@ -592,6 +604,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
textTheme: textTheme,
primary: primary,
centerTitle: centerTitle,
titleSpacing: titleSpacing,
toolbarOpacity: toolbarOpacity,
bottomOpacity: pinned ? 1.0 : (visibleMainHeight / _bottomHeight).clamp(0.0, 1.0),
),
......@@ -615,6 +628,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
|| textTheme != oldDelegate.textTheme
|| primary != oldDelegate.primary
|| centerTitle != oldDelegate.centerTitle
|| titleSpacing != oldDelegate.titleSpacing
|| expandedHeight != oldDelegate.expandedHeight
|| topPadding != oldDelegate.topPadding
|| pinned != oldDelegate.pinned
......@@ -699,6 +713,7 @@ class SliverAppBar extends StatefulWidget {
this.textTheme,
this.primary: true,
this.centerTitle,
this.titleSpacing: NavigationToolbar.kMiddleSpacing,
this.expandedHeight,
this.floating: false,
this.pinned: false,
......@@ -706,6 +721,7 @@ class SliverAppBar extends StatefulWidget {
}) : assert(automaticallyImplyLeading != null),
assert(forceElevated != null),
assert(primary != null),
assert(titleSpacing != null),
assert(floating != null),
assert(pinned != null),
assert(!pinned || !floating || bottom != null, 'A pinned and floating app bar must have a bottom widget.'),
......@@ -841,6 +857,13 @@ class SliverAppBar extends StatefulWidget {
/// Defaults to being adapted to the current [TargetPlatform].
final bool centerTitle;
/// The spacing around [title] content on the horizontal axis. This spacing is
/// applied even if there is no [leading] content or [actions]. If you want
/// [title] to take all the space available, set this value to 0.0.
///
/// Defaults to [NavigationToolbar.kMiddleSpacing].
final double titleSpacing;
/// The size of the app bar when it is fully expanded.
///
/// By default, the total height of the toolbar and the bottom widget (if
......@@ -939,6 +962,7 @@ class _SliverAppBarState extends State<SliverAppBar> with TickerProviderStateMix
textTheme: widget.textTheme,
primary: widget.primary,
centerTitle: widget.centerTitle,
titleSpacing: widget.titleSpacing,
expandedHeight: widget.expandedHeight,
collapsedHeight: collapsedHeight,
topPadding: topPadding,
......
......@@ -22,6 +22,7 @@ import 'framework.dart';
/// the iOS [CupertinoNavigationBar] or wrap this widget with more theming
/// specifications for your own custom app bar.
class NavigationToolbar extends StatelessWidget {
/// Creates a widget that lays out its children in a manner suitable for a
/// toolbar.
const NavigationToolbar({
......@@ -30,9 +31,14 @@ class NavigationToolbar extends StatelessWidget {
this.middle,
this.trailing,
this.centerMiddle: true,
this.middleSpacing: kMiddleSpacing,
}) : assert(centerMiddle != null),
assert(middleSpacing != null),
super(key: key);
/// The default spacing around the [middle] widget in dp.
static const double kMiddleSpacing = 16.0;
/// Widget to place at the start of the horizontal toolbar.
final Widget leading;
......@@ -47,6 +53,11 @@ class NavigationToolbar extends StatelessWidget {
/// next to the [leading] widget when false.
final bool centerMiddle;
/// The spacing around the [middle] widget on horizontal axis.
///
/// Defaults to [kMiddleSpacing].
final double middleSpacing;
@override
Widget build(BuildContext context) {
final List<Widget> children = <Widget>[];
......@@ -65,6 +76,7 @@ class NavigationToolbar extends StatelessWidget {
return new CustomMultiChildLayout(
delegate: new _ToolbarLayout(
centerMiddle: centerMiddle,
middleSpacing: middleSpacing,
textDirection: textDirection,
),
children: children,
......@@ -78,13 +90,13 @@ enum _ToolbarSlot {
trailing,
}
const double _kMiddleMargin = 16.0;
class _ToolbarLayout extends MultiChildLayoutDelegate {
_ToolbarLayout({
this.centerMiddle,
@required this.middleSpacing,
@required this.textDirection,
}) : assert(textDirection != null);
}) : assert(middleSpacing != null),
assert(textDirection != null);
// If false the middle widget should be start-justified within the space
// between the leading and trailing widgets.
......@@ -92,6 +104,9 @@ class _ToolbarLayout extends MultiChildLayoutDelegate {
// space between the leading and trailing widgets).
final bool centerMiddle;
/// The spacing around middle widget on horizontal axis.
final double middleSpacing;
final TextDirection textDirection;
@override
......@@ -137,11 +152,11 @@ class _ToolbarLayout extends MultiChildLayoutDelegate {
}
if (hasChild(_ToolbarSlot.middle)) {
final double maxWidth = math.max(size.width - leadingWidth - trailingWidth - _kMiddleMargin * 2.0, 0.0);
final double maxWidth = math.max(size.width - leadingWidth - trailingWidth - middleSpacing * 2.0, 0.0);
final BoxConstraints constraints = new BoxConstraints.loose(size).copyWith(maxWidth: maxWidth);
final Size middleSize = layoutChild(_ToolbarSlot.middle, constraints);
final double middleStartMargin = hasChild(_ToolbarSlot.leading) ? leadingWidth + _kMiddleMargin : 0.0;
final double middleStartMargin = leadingWidth + middleSpacing;
double middleStart = middleStartMargin;
final double middleY = (size.height - middleSize.height) / 2.0;
// If the centered middle will not fit between the leading and trailing
......@@ -171,6 +186,7 @@ class _ToolbarLayout extends MultiChildLayoutDelegate {
@override
bool shouldRelayout(_ToolbarLayout oldDelegate) {
return oldDelegate.centerMiddle != centerMiddle
|| oldDelegate.middleSpacing != middleSpacing
|| oldDelegate.textDirection != textDirection;
}
}
......@@ -157,22 +157,25 @@ void main() {
expect(center.dx, lessThan(400 + size.width / 2.0));
});
testWidgets('AppBar centerTitle:false title start edge is 0.0 (LTR)', (WidgetTester tester) async {
testWidgets('AppBar centerTitle:false title start edge is 16.0 (LTR)', (WidgetTester tester) async {
await tester.pumpWidget(
new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
centerTitle: false,
title: const Text('X'),
title: const Placeholder(key: const Key('X')),
),
),
),
);
expect(tester.getTopLeft(find.text('X')).dx, 0.0);
final Finder titleWidget = find.byKey(const Key('X'));
expect(tester.getTopLeft(titleWidget).dx, 16.0);
// 4.0 is due to AppBar right padding.
expect(tester.getTopRight(titleWidget).dx, 800 - 16.0 - 4.0);
});
testWidgets('AppBar centerTitle:false title start edge is 0.0 (RTL)', (WidgetTester tester) async {
testWidgets('AppBar centerTitle:false title start edge is 16.0 (RTL)', (WidgetTester tester) async {
await tester.pumpWidget(
new MaterialApp(
home: new Directionality(
......@@ -180,14 +183,58 @@ void main() {
child: new Scaffold(
appBar: new AppBar(
centerTitle: false,
title: const Text('X'),
title: const Placeholder(key: const Key('X')),
),
),
),
),
);
final Finder titleWidget = find.byKey(const Key('X'));
expect(tester.getTopRight(titleWidget).dx, 800.0 - 16.0);
// 4.0 is due to AppBar right padding.
expect(tester.getTopLeft(titleWidget).dx, 16.0 + 4.0);
});
testWidgets('AppBar titleSpacing:32 title start edge is 32.0 (LTR)', (WidgetTester tester) async {
await tester.pumpWidget(
new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
centerTitle: false,
titleSpacing: 32.0,
title: const Placeholder(key: const Key('X')),
),
),
),
);
final Finder titleWidget = find.byKey(const Key('X'));
expect(tester.getTopLeft(titleWidget).dx, 32.0);
// 4.0 is due to AppBar right padding.
expect(tester.getTopRight(titleWidget).dx, 800 - 32.0 - 4.0);
});
testWidgets('AppBar titleSpacing:32 title start edge is 32.0 (RTL)', (WidgetTester tester) async {
await tester.pumpWidget(
new MaterialApp(
home: new Directionality(
textDirection: TextDirection.rtl,
child: new Scaffold(
appBar: new AppBar(
centerTitle: false,
titleSpacing: 32.0,
title: const Placeholder(key: const Key('X')),
),
),
),
),
);
expect(tester.getTopRight(find.text('X')).dx, 800.0);
final Finder titleWidget = find.byKey(const Key('X'));
expect(tester.getTopRight(titleWidget).dx, 800.0 - 32.0);
// 4.0 is due to AppBar right padding.
expect(tester.getTopLeft(titleWidget).dx, 32.0 + 4.0);
});
testWidgets(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment