// 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/services.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { test('AppBarTheme copyWith, ==, hashCode basics', () { expect(const AppBarTheme(), const AppBarTheme().copyWith()); expect(const AppBarTheme().hashCode, const AppBarTheme().copyWith().hashCode); }); testWidgets('Passing no AppBarTheme returns defaults', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( home: Scaffold(appBar: AppBar( actions: <Widget>[ IconButton(icon: const Icon(Icons.share), onPressed: () { }), ], )), )); final Material widget = _getAppBarMaterial(tester); final IconTheme iconTheme = _getAppBarIconTheme(tester); final IconTheme actionsIconTheme = _getAppBarActionsIconTheme(tester); final RichText actionIconText = _getAppBarIconRichText(tester); final DefaultTextStyle text = _getAppBarText(tester); expect(SystemChrome.latestStyle!.statusBarBrightness, Brightness.dark); expect(widget.color, Colors.blue); expect(widget.elevation, 4.0); expect(widget.shadowColor, Colors.black); expect(iconTheme.data, const IconThemeData(color: Colors.white)); expect(actionsIconTheme.data, const IconThemeData(color: Colors.white)); expect(actionIconText.text.style!.color, Colors.white); expect(text.style, Typography.material2014().englishLike.bodyText2!.merge(Typography.material2014().white.bodyText2)); }); testWidgets('AppBar uses values from AppBarTheme', (WidgetTester tester) async { final AppBarTheme appBarTheme = _appBarTheme(); await tester.pumpWidget(MaterialApp( theme: ThemeData(appBarTheme: appBarTheme), home: Scaffold(appBar: AppBar( title: const Text('App Bar Title'), actions: <Widget>[ IconButton(icon: const Icon(Icons.share), onPressed: () { }), ], )), )); final Material widget = _getAppBarMaterial(tester); final IconTheme iconTheme = _getAppBarIconTheme(tester); final IconTheme actionsIconTheme = _getAppBarActionsIconTheme(tester); final RichText actionIconText = _getAppBarIconRichText(tester); final DefaultTextStyle text = _getAppBarText(tester); expect(SystemChrome.latestStyle!.statusBarBrightness, appBarTheme.brightness); expect(widget.color, appBarTheme.color); expect(widget.elevation, appBarTheme.elevation); expect(widget.shadowColor, appBarTheme.shadowColor); expect(iconTheme.data, appBarTheme.iconTheme); expect(actionsIconTheme.data, appBarTheme.actionsIconTheme); expect(actionIconText.text.style!.color, appBarTheme.actionsIconTheme!.color); expect(text.style, appBarTheme.textTheme!.bodyText2); }); testWidgets('AppBar widget properties take priority over theme', (WidgetTester tester) async { const Brightness brightness = Brightness.dark; const Color color = Colors.orange; const double elevation = 3.0; const Color shadowColor = Colors.red; const IconThemeData iconThemeData = IconThemeData(color: Colors.green); const IconThemeData actionsIconThemeData = IconThemeData(color: Colors.lightBlue); const TextTheme textTheme = TextTheme(headline6: TextStyle(color: Colors.orange), bodyText2: TextStyle(color: Colors.pink)); final ThemeData themeData = _themeData().copyWith(appBarTheme: _appBarTheme()); await tester.pumpWidget(MaterialApp( theme: themeData, home: Scaffold(appBar: AppBar( backgroundColor: color, brightness: brightness, elevation: elevation, shadowColor: shadowColor, iconTheme: iconThemeData, actionsIconTheme: actionsIconThemeData, textTheme: textTheme, actions: <Widget>[ IconButton(icon: const Icon(Icons.share), onPressed: () { }), ], )), )); final Material widget = _getAppBarMaterial(tester); final IconTheme iconTheme = _getAppBarIconTheme(tester); final IconTheme actionsIconTheme = _getAppBarActionsIconTheme(tester); final RichText actionIconText = _getAppBarIconRichText(tester); final DefaultTextStyle text = _getAppBarText(tester); expect(SystemChrome.latestStyle!.statusBarBrightness, brightness); expect(widget.color, color); expect(widget.elevation, elevation); expect(widget.shadowColor, shadowColor); expect(iconTheme.data, iconThemeData); expect(actionsIconTheme.data, actionsIconThemeData); expect(actionIconText.text.style!.color, actionsIconThemeData.color); expect(text.style, textTheme.bodyText2); }); testWidgets('AppBar icon color takes priority over everything', (WidgetTester tester) async { const Color color = Colors.lime; const IconThemeData iconThemeData = IconThemeData(color: Colors.green); const IconThemeData actionsIconThemeData = IconThemeData(color: Colors.lightBlue); final ThemeData themeData = _themeData().copyWith(appBarTheme: _appBarTheme()); await tester.pumpWidget(MaterialApp( theme: themeData, home: Scaffold(appBar: AppBar( iconTheme: iconThemeData, actionsIconTheme: actionsIconThemeData, actions: <Widget>[ IconButton(icon: const Icon(Icons.share), color: color, onPressed: () { }), ], )), )); final RichText actionIconText = _getAppBarIconRichText(tester); expect(actionIconText.text.style!.color, color); }); testWidgets('AppBarTheme properties take priority over ThemeData properties', (WidgetTester tester) async { final AppBarTheme appBarTheme = _appBarTheme(); final ThemeData themeData = _themeData().copyWith(appBarTheme: _appBarTheme()); await tester.pumpWidget(MaterialApp( theme: themeData, home: Scaffold(appBar: AppBar( actions: <Widget>[ IconButton(icon: const Icon(Icons.share), onPressed: () { }), ], )), )); final Material widget = _getAppBarMaterial(tester); final IconTheme iconTheme = _getAppBarIconTheme(tester); final IconTheme actionsIconTheme = _getAppBarActionsIconTheme(tester); final RichText actionIconText = _getAppBarIconRichText(tester); final DefaultTextStyle text = _getAppBarText(tester); expect(SystemChrome.latestStyle!.statusBarBrightness, appBarTheme.brightness); expect(widget.color, appBarTheme.color); expect(widget.elevation, appBarTheme.elevation); expect(widget.shadowColor, appBarTheme.shadowColor); expect(iconTheme.data, appBarTheme.iconTheme); expect(actionsIconTheme.data, appBarTheme.actionsIconTheme); expect(actionIconText.text.style!.color, appBarTheme.actionsIconTheme!.color); expect(text.style, appBarTheme.textTheme!.bodyText2); }); testWidgets('ThemeData properties are used when no AppBarTheme is set', (WidgetTester tester) async { final ThemeData themeData = _themeData(); await tester.pumpWidget(MaterialApp( theme: themeData, home: Scaffold(appBar: AppBar( actions: <Widget>[ IconButton(icon: const Icon(Icons.share), onPressed: () { }), ], )), )); final Material widget = _getAppBarMaterial(tester); final IconTheme iconTheme = _getAppBarIconTheme(tester); final IconTheme actionsIconTheme = _getAppBarActionsIconTheme(tester); final RichText actionIconText = _getAppBarIconRichText(tester); final DefaultTextStyle text = _getAppBarText(tester); expect(SystemChrome.latestStyle!.statusBarBrightness, themeData.brightness); expect(widget.color, themeData.primaryColor); expect(widget.elevation, 4.0); expect(widget.shadowColor, Colors.black); expect(iconTheme.data, themeData.primaryIconTheme); expect(actionsIconTheme.data, themeData.primaryIconTheme); expect(actionIconText.text.style!.color, themeData.primaryIconTheme.color); // Default value for ThemeData.typography is Typography.material2014() expect(text.style, Typography.material2014().englishLike.bodyText2!.merge(Typography.material2014().white.bodyText2).merge(themeData.primaryTextTheme.bodyText2)); }); testWidgets('AppBar uses AppBarTheme.centerTitle when centerTitle is null', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( theme: ThemeData(appBarTheme: const AppBarTheme(centerTitle: true)), home: Scaffold(appBar: AppBar(title: const Text('Title'))), )); final NavigationToolbar navToolBar = tester.widget(find.byType(NavigationToolbar)); expect(navToolBar.centerMiddle, true); }); testWidgets('AppBar.centerTitle takes priority over AppBarTheme.centerTitle', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( theme: ThemeData(appBarTheme: const AppBarTheme(centerTitle: true)), home: Scaffold( appBar: AppBar( title: const Text('Title'), centerTitle: false, ), ), )); final NavigationToolbar navToolBar = tester.widget(find.byType(NavigationToolbar)); // The AppBar.centerTitle should be used instead of AppBarTheme.centerTitle. expect(navToolBar.centerMiddle, false); }); testWidgets('AppBar.centerTitle adapts to TargetPlatform when AppBarTheme.centerTitle is null', (WidgetTester tester) async{ await tester.pumpWidget(MaterialApp( theme: ThemeData(platform: TargetPlatform.iOS), home: Scaffold(appBar: AppBar(title: const Text('Title'))), )); final NavigationToolbar navToolBar = tester.widget(find.byType(NavigationToolbar)); // When ThemeData.platform is TargetPlatform.iOS, and AppBarTheme is null, // the value of NavigationToolBar.centerMiddle should be true. expect(navToolBar.centerMiddle, true); }); testWidgets('AppBar.shadowColor takes priority over AppBarTheme.shadowColor', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp( theme: ThemeData(appBarTheme: const AppBarTheme(shadowColor: Colors.red)), home: Scaffold( appBar: AppBar( title: const Text('Title'), shadowColor: Colors.yellow, ), ), )); final AppBar appBar = tester.widget(find.byType(AppBar)); // 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: <Widget>[ 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: <Widget>[ 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<String> description = builder.properties .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) .map((DiagnosticsNode node) => node.toString()) .toList(); expect(description, <String>[]); }); 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<String> description = builder.properties .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) .map((DiagnosticsNode node) => node.toString()) .toList(); expect(description, <String>[ '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() { const Brightness brightness = Brightness.light; const Color color = Colors.lightBlue; const double elevation = 6.0; const Color shadowColor = Colors.red; const IconThemeData iconThemeData = IconThemeData(color: Colors.black); const IconThemeData actionsIconThemeData = IconThemeData(color: Colors.pink); const TextTheme textTheme = TextTheme(bodyText2: TextStyle(color: Colors.yellow)); return const AppBarTheme( actionsIconTheme: actionsIconThemeData, brightness: brightness, color: color, elevation: elevation, shadowColor: shadowColor, iconTheme: iconThemeData, textTheme: textTheme, ); } ThemeData _themeData() { return ThemeData( primaryColor: Colors.purple, brightness: Brightness.dark, primaryIconTheme: const IconThemeData(color: Colors.green), primaryTextTheme: const TextTheme(headline6: TextStyle(color: Colors.orange), bodyText2: TextStyle(color: Colors.pink)), ); } Material _getAppBarMaterial(WidgetTester tester) { return tester.widget<Material>( find.descendant( of: find.byType(AppBar), matching: find.byType(Material), ), ); } IconTheme _getAppBarIconTheme(WidgetTester tester) { return tester.widget<IconTheme>( find.descendant( of: find.byType(AppBar), matching: find.byType(IconTheme), ).first, ); } IconTheme _getAppBarActionsIconTheme(WidgetTester tester) { return tester.widget<IconTheme>( find.descendant( of: find.byType(NavigationToolbar), matching: find.byType(IconTheme), ).first, ); } RichText _getAppBarIconRichText(WidgetTester tester) { return tester.widget<RichText>( find.descendant( of: find.byType(Icon), matching: find.byType(RichText), ).first, ); } DefaultTextStyle _getAppBarText(WidgetTester tester) { return tester.widget<DefaultTextStyle>( find.descendant( of: find.byType(CustomSingleChildLayout), matching: find.byType(DefaultTextStyle), ).first, ); }