From a67ba2a2ba820cdae1365beefc5ba24edc5e5e61 Mon Sep 17 00:00:00 2001 From: Ayush Bherwani Date: Thu, 8 Oct 2020 23:37:34 +0530 Subject: [PATCH] [AppBarTheme] adds titleSpacing parameter (#67105) * adds titleSpacing in AppBarTheme Co-authored-by: Shi-Hao Hong --- .../flutter/lib/src/material/app_bar.dart | 14 +-- .../lib/src/material/app_bar_theme.dart | 14 ++- .../flutter/test/material/app_bar_test.dart | 23 ++++ .../test/material/app_bar_theme_test.dart | 111 ++++++++++++++++++ 4 files changed, 153 insertions(+), 9 deletions(-) diff --git a/packages/flutter/lib/src/material/app_bar.dart b/packages/flutter/lib/src/material/app_bar.dart index 513ee54fca4..fe09faf42e4 100644 --- a/packages/flutter/lib/src/material/app_bar.dart +++ b/packages/flutter/lib/src/material/app_bar.dart @@ -203,7 +203,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { this.primary = true, this.centerTitle, this.excludeHeaderSemantics = false, - this.titleSpacing = NavigationToolbar.kMiddleSpacing, + this.titleSpacing, this.toolbarOpacity = 1.0, this.bottomOpacity = 1.0, this.toolbarHeight, @@ -211,7 +211,6 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { }) : assert(automaticallyImplyLeading != null), assert(elevation == null || elevation >= 0.0), assert(primary != null), - assert(titleSpacing != null), assert(toolbarOpacity != null), assert(bottomOpacity != null), preferredSize = Size.fromHeight(toolbarHeight ?? kToolbarHeight + (bottom?.preferredSize.height ?? 0.0)), @@ -424,7 +423,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { /// [title] to take all the space available, set this value to 0.0. /// /// Defaults to [NavigationToolbar.kMiddleSpacing]. - final double titleSpacing; + final double? titleSpacing; /// How opaque the toolbar part of the app bar is. /// @@ -634,7 +633,7 @@ class _AppBarState extends State { middle: title, trailing: actions, centerMiddle: widget._getEffectiveCenterTitle(theme!), - middleSpacing: widget.titleSpacing, + middleSpacing: widget.titleSpacing ?? appBarTheme.titleSpacing ?? NavigationToolbar.kMiddleSpacing, ); // If the toolbar is allocated less than toolbarHeight make it @@ -848,7 +847,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { final bool primary; final bool? centerTitle; final bool excludeHeaderSemantics; - final double titleSpacing; + final double? titleSpacing; final double? expandedHeight; final double collapsedHeight; final double topPadding; @@ -1064,7 +1063,7 @@ class SliverAppBar extends StatefulWidget { this.primary = true, this.centerTitle, this.excludeHeaderSemantics = false, - this.titleSpacing = NavigationToolbar.kMiddleSpacing, + this.titleSpacing, this.collapsedHeight, this.expandedHeight, this.floating = false, @@ -1079,7 +1078,6 @@ class SliverAppBar extends StatefulWidget { }) : assert(automaticallyImplyLeading != null), assert(forceElevated != null), assert(primary != null), - assert(titleSpacing != null), assert(floating != null), assert(pinned != null), assert(snap != null), @@ -1259,7 +1257,7 @@ class SliverAppBar extends StatefulWidget { /// [title] to take all the space available, set this value to 0.0. /// /// Defaults to [NavigationToolbar.kMiddleSpacing]. - final double titleSpacing; + final double? titleSpacing; /// Defines the height of the app bar when it is collapsed. /// diff --git a/packages/flutter/lib/src/material/app_bar_theme.dart b/packages/flutter/lib/src/material/app_bar_theme.dart index dd4005648fc..cc19e6cb03a 100644 --- a/packages/flutter/lib/src/material/app_bar_theme.dart +++ b/packages/flutter/lib/src/material/app_bar_theme.dart @@ -39,6 +39,7 @@ class AppBarTheme with Diagnosticable { this.actionsIconTheme, this.textTheme, this.centerTitle, + this.titleSpacing, }); /// Default value for [AppBar.brightness]. @@ -81,6 +82,11 @@ class AppBarTheme with Diagnosticable { /// If null, the value is adapted to current [TargetPlatform]. final bool? centerTitle; + /// Default value for [AppBar.titleSpacing]. + /// + /// If null, [AppBar] uses default value of [NavigationToolbar.kMiddleSpacing]. + final double? titleSpacing; + /// Creates a copy of this object with the given fields replaced with the /// new values. AppBarTheme copyWith({ @@ -92,6 +98,7 @@ class AppBarTheme with Diagnosticable { IconThemeData? iconTheme, TextTheme? textTheme, bool? centerTitle, + double? titleSpacing, }) { return AppBarTheme( brightness: brightness ?? this.brightness, @@ -102,6 +109,7 @@ class AppBarTheme with Diagnosticable { actionsIconTheme: actionsIconTheme ?? this.actionsIconTheme, textTheme: textTheme ?? this.textTheme, centerTitle: centerTitle ?? this.centerTitle, + titleSpacing: titleSpacing ?? this.titleSpacing, ); } @@ -126,6 +134,7 @@ class AppBarTheme with Diagnosticable { actionsIconTheme: IconThemeData.lerp(a?.actionsIconTheme, b?.actionsIconTheme, t), textTheme: TextTheme.lerp(a?.textTheme, b?.textTheme, t), centerTitle: t < 0.5 ? a?.centerTitle : b?.centerTitle, + titleSpacing: lerpDouble(a?.titleSpacing, b?.titleSpacing, t), ); } @@ -140,6 +149,7 @@ class AppBarTheme with Diagnosticable { actionsIconTheme, textTheme, centerTitle, + titleSpacing, ); } @@ -157,7 +167,8 @@ class AppBarTheme with Diagnosticable { && other.iconTheme == iconTheme && other.actionsIconTheme == actionsIconTheme && other.textTheme == textTheme - && other.centerTitle == centerTitle; + && other.centerTitle == centerTitle + && other.titleSpacing == titleSpacing; } @override @@ -171,5 +182,6 @@ class AppBarTheme with Diagnosticable { properties.add(DiagnosticsProperty('actionsIconTheme', actionsIconTheme, defaultValue: null)); properties.add(DiagnosticsProperty('textTheme', textTheme, defaultValue: null)); properties.add(DiagnosticsProperty('centerTitle', centerTitle, defaultValue: null)); + properties.add(DiagnosticsProperty('titleSpacing', titleSpacing, defaultValue: null)); } } diff --git a/packages/flutter/test/material/app_bar_test.dart b/packages/flutter/test/material/app_bar_test.dart index b79dab31fe2..e34170ccaa3 100644 --- a/packages/flutter/test/material/app_bar_test.dart +++ b/packages/flutter/test/material/app_bar_test.dart @@ -2211,4 +2211,27 @@ void main() { NavigationToolbar getAppBarWidget(Finder finder) => tester.widget(finder); expect(getAppBarWidget(appBarFinder).leading, null); }); + + testWidgets('AppBar.titleSpacing defaults to NavigationToolbar.kMiddleSpacing', (WidgetTester tester) async { + await tester.pumpWidget(MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Title'), + ), + ), + )); + + final NavigationToolbar navToolBar = tester.widget(find.byType(NavigationToolbar)); + expect(navToolBar.middleSpacing, NavigationToolbar.kMiddleSpacing); + }); + + testWidgets('SliverAppBar.titleSpacing defaults to NavigationToolbar.kMiddleSpacing', (WidgetTester tester) async { + await tester.pumpWidget(buildSliverAppBarApp( + floating: false, + pinned: false, + )); + + final NavigationToolbar navToolBar = tester.widget(find.byType(NavigationToolbar)); + expect(navToolBar.middleSpacing, NavigationToolbar.kMiddleSpacing); + }); } diff --git a/packages/flutter/test/material/app_bar_theme_test.dart b/packages/flutter/test/material/app_bar_theme_test.dart index c020fb69c0c..3adcf2750a2 100644 --- a/packages/flutter/test/material/app_bar_theme_test.dart +++ b/packages/flutter/test/material/app_bar_theme_test.dart @@ -7,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -245,6 +246,116 @@ void main() { // The AppBar.shadowColor should be used instead of AppBarTheme.shadowColor. expect(appBar.shadowColor, Colors.yellow); }); + + testWidgets('AppBar uses AppBarTheme.titleSpacing', (WidgetTester tester) async { + const double kTitleSpacing = 10; + await tester.pumpWidget(MaterialApp( + theme: ThemeData(appBarTheme: const AppBarTheme(titleSpacing: kTitleSpacing)), + home: Scaffold( + appBar: AppBar( + title: const Text('Title'), + ), + ), + )); + + final NavigationToolbar navToolBar = tester.widget(find.byType(NavigationToolbar)); + expect(navToolBar.middleSpacing, kTitleSpacing); + }); + + testWidgets('AppBar.titleSpacing takes priority over AppBarTheme.titleSpacing', (WidgetTester tester) async { + const double kTitleSpacing = 10; + await tester.pumpWidget(MaterialApp( + theme: ThemeData(appBarTheme: const AppBarTheme(titleSpacing: kTitleSpacing)), + home: Scaffold( + appBar: AppBar( + title: const Text('Title'), + titleSpacing: 40, + ), + ), + )); + + final NavigationToolbar navToolBar = tester.widget(find.byType(NavigationToolbar)); + expect(navToolBar.middleSpacing, 40); + }); + + testWidgets('SliverAppBar uses AppBarTheme.titleSpacing', (WidgetTester tester) async { + const double kTitleSpacing = 10; + await tester.pumpWidget(MaterialApp( + theme: ThemeData(appBarTheme: const AppBarTheme(titleSpacing: kTitleSpacing)), + home: const CustomScrollView( + slivers: [ + SliverAppBar( + title: Text('Title'), + ), + ], + ), + )); + + final NavigationToolbar navToolBar = tester.widget(find.byType(NavigationToolbar)); + expect(navToolBar.middleSpacing, kTitleSpacing); + }); + + testWidgets('SliverAppBar.titleSpacing takes priority over AppBarTheme.titleSpacing ', (WidgetTester tester) async { + const double kTitleSpacing = 10; + await tester.pumpWidget(MaterialApp( + theme: ThemeData(appBarTheme: const AppBarTheme(titleSpacing: kTitleSpacing)), + home: const CustomScrollView( + slivers: [ + SliverAppBar( + title: Text('Title'), + titleSpacing: 40, + ), + ], + ), + )); + + final NavigationToolbar navToolbar = tester.widget(find.byType(NavigationToolbar)); + expect(navToolbar.middleSpacing, 40); + }); + + testWidgets('Default AppBarTheme debugFillProperties', (WidgetTester tester) async { + final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); + const AppBarTheme().debugFillProperties(builder); + + final List description = builder.properties + .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) + .map((DiagnosticsNode node) => node.toString()) + .toList(); + + expect(description, []); + }); + + testWidgets('AppBarTheme implements debugFillProperties', (WidgetTester tester) async { + final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); + const AppBarTheme( + brightness: Brightness.dark, + color: Color(0xff000001), + elevation: 8.0, + shadowColor: Color(0xff000002), + centerTitle: true, + titleSpacing: 40.0, + ).debugFillProperties(builder); + + final List description = builder.properties + .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) + .map((DiagnosticsNode node) => node.toString()) + .toList(); + + expect(description, [ + 'brightness: Brightness.dark', + 'color: Color(0xff000001)', + 'elevation: 8.0', + 'shadowColor: Color(0xff000002)', + 'centerTitle: true', + 'titleSpacing: 40.0', + ]); + + // On the web, Dart doubles and ints are backed by the same kind of object because + // JavaScript does not support integers. So, the Dart double "4.0" is identical + // to "4", which results in the web evaluating to the value "4" regardless of which + // one is used. This results in a difference for doubles in debugFillProperties between + // the web and the rest of Flutter's target platforms. + }, skip: kIsWeb); } AppBarTheme _appBarTheme() {