// 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'; void main() { test('SearchBarThemeData copyWith, ==, hashCode basics', () { expect(const SearchBarThemeData(), const SearchBarThemeData().copyWith()); expect(const SearchBarThemeData().hashCode, const SearchBarThemeData() .copyWith() .hashCode); }); test('SearchBarThemeData lerp special cases', () { expect(SearchBarThemeData.lerp(null, null, 0), null); const SearchBarThemeData data = SearchBarThemeData(); expect(identical(SearchBarThemeData.lerp(data, data, 0.5), data), true); }); test('SearchBarThemeData defaults', () { const SearchBarThemeData themeData = SearchBarThemeData(); expect(themeData.elevation, null); expect(themeData.backgroundColor, null); expect(themeData.shadowColor, null); expect(themeData.surfaceTintColor, null); expect(themeData.overlayColor, null); expect(themeData.side, null); expect(themeData.shape, null); expect(themeData.padding, null); expect(themeData.textStyle, null); expect(themeData.hintStyle, null); expect(themeData.constraints, null); const SearchBarTheme theme = SearchBarTheme(data: SearchBarThemeData(), child: SizedBox()); expect(theme.data.elevation, null); expect(theme.data.backgroundColor, null); expect(theme.data.shadowColor, null); expect(theme.data.surfaceTintColor, null); expect(theme.data.overlayColor, null); expect(theme.data.side, null); expect(theme.data.shape, null); expect(theme.data.padding, null); expect(theme.data.textStyle, null); expect(theme.data.hintStyle, null); expect(theme.data.constraints, null); }); testWidgets('Default SearchBarThemeData debugFillProperties', (WidgetTester tester) async { final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); const SearchBarThemeData().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('SearchBarThemeData implements debugFillProperties', ( WidgetTester tester) async { final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); const SearchBarThemeData( elevation: MaterialStatePropertyAll<double>(3.0), backgroundColor: MaterialStatePropertyAll<Color>(Color(0xfffffff1)), shadowColor: MaterialStatePropertyAll<Color>(Color(0xfffffff2)), surfaceTintColor: MaterialStatePropertyAll<Color>(Color(0xfffffff3)), overlayColor: MaterialStatePropertyAll<Color>(Color(0xfffffff4)), side: MaterialStatePropertyAll<BorderSide>(BorderSide(width: 2.0, color: Color(0xfffffff5))), shape: MaterialStatePropertyAll<OutlinedBorder>(StadiumBorder()), padding: MaterialStatePropertyAll<EdgeInsets>(EdgeInsets.all(16.0)), textStyle: MaterialStatePropertyAll<TextStyle>(TextStyle(fontSize: 24.0)), hintStyle: MaterialStatePropertyAll<TextStyle>(TextStyle(fontSize: 16.0)), constraints: BoxConstraints(minWidth: 350, maxWidth: 850), ).debugFillProperties(builder); final List<String> description = builder.properties .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) .map((DiagnosticsNode node) => node.toString()) .toList(); expect(description[0], 'elevation: MaterialStatePropertyAll(3.0)'); expect(description[1], 'backgroundColor: MaterialStatePropertyAll(Color(0xfffffff1))'); expect(description[2], 'shadowColor: MaterialStatePropertyAll(Color(0xfffffff2))'); expect(description[3], 'surfaceTintColor: MaterialStatePropertyAll(Color(0xfffffff3))'); expect(description[4], 'overlayColor: MaterialStatePropertyAll(Color(0xfffffff4))'); expect(description[5], 'side: MaterialStatePropertyAll(BorderSide(color: Color(0xfffffff5), width: 2.0))'); expect(description[6], 'shape: MaterialStatePropertyAll(StadiumBorder(BorderSide(width: 0.0, style: none)))'); expect(description[7], 'padding: MaterialStatePropertyAll(EdgeInsets.all(16.0))'); expect(description[8], 'textStyle: MaterialStatePropertyAll(TextStyle(inherit: true, size: 24.0))'); expect(description[9], 'hintStyle: MaterialStatePropertyAll(TextStyle(inherit: true, size: 16.0))'); expect(description[10], 'constraints: BoxConstraints(350.0<=w<=850.0, 0.0<=h<=Infinity)'); }); group('[Theme, SearchBarTheme, SearchBar properties overrides]', () { const double elevationValue = 5.0; const Color backgroundColorValue = Color(0xff000001); const Color shadowColorValue = Color(0xff000001); const Color surfaceTintColorValue = Color(0xff000001); const Color overlayColorValue = Color(0xff000001); const BorderSide sideValue = BorderSide(color: Color(0xff000004), width: 2.0); const OutlinedBorder shapeValue = RoundedRectangleBorder(side: sideValue, borderRadius: BorderRadius.all(Radius.circular(2.0))); const EdgeInsets paddingValue = EdgeInsets.symmetric(horizontal: 16.0); const TextStyle textStyleValue = TextStyle(color: Color(0xff000005), fontSize: 20.0); const TextStyle hintStyleValue = TextStyle(color: Color(0xff000006), fontSize: 18.0); const MaterialStateProperty<double?> elevation = MaterialStatePropertyAll<double>(elevationValue); const MaterialStateProperty<Color?> backgroundColor = MaterialStatePropertyAll<Color>(backgroundColorValue); const MaterialStateProperty<Color?> shadowColor = MaterialStatePropertyAll<Color>(shadowColorValue); const MaterialStateProperty<Color?> surfaceTintColor = MaterialStatePropertyAll<Color>(surfaceTintColorValue); const MaterialStateProperty<Color?> overlayColor = MaterialStatePropertyAll<Color>(overlayColorValue); const MaterialStateProperty<BorderSide?> side = MaterialStatePropertyAll<BorderSide>(sideValue); const MaterialStateProperty<OutlinedBorder?> shape = MaterialStatePropertyAll<OutlinedBorder>(shapeValue); const MaterialStateProperty<EdgeInsetsGeometry?> padding = MaterialStatePropertyAll<EdgeInsets>(paddingValue); const MaterialStateProperty<TextStyle?> textStyle = MaterialStatePropertyAll<TextStyle>(textStyleValue); const MaterialStateProperty<TextStyle?> hintStyle = MaterialStatePropertyAll<TextStyle>(hintStyleValue); const BoxConstraints constraints = BoxConstraints(minWidth: 250.0, maxWidth: 300.0, minHeight: 80.0); const SearchBarThemeData searchBarTheme = SearchBarThemeData( elevation: elevation, backgroundColor: backgroundColor, shadowColor: shadowColor, surfaceTintColor: surfaceTintColor, overlayColor: overlayColor, side: side, shape: shape, padding: padding, textStyle: textStyle, hintStyle: hintStyle, constraints: constraints, ); Widget buildFrame({ bool useSearchBarProperties = false, SearchBarThemeData? searchBarThemeData, SearchBarThemeData? overallTheme }) { final Widget child = Builder( builder: (BuildContext context) { if (!useSearchBarProperties) { return const SearchBar( hintText: 'hint', leading: Icon(Icons.search), trailing: <Widget>[ Icon(Icons.menu)], ); } return const SearchBar( hintText: 'hint', leading: Icon(Icons.search), trailing: <Widget>[ Icon(Icons.menu)], elevation: elevation, backgroundColor: backgroundColor, shadowColor: shadowColor, surfaceTintColor: surfaceTintColor, overlayColor: overlayColor, side: side, shape: shape, padding: padding, textStyle: textStyle, hintStyle: hintStyle, constraints: constraints, ); }, ); return MaterialApp( theme: ThemeData.from( colorScheme: const ColorScheme.light(), useMaterial3: true) .copyWith( searchBarTheme: overallTheme, ), home: Scaffold( body: Center( // If the SearchBarThemeData widget is present, it's used // instead of the Theme's ThemeData.searchBarTheme. child: searchBarThemeData == null ? child : SearchBarTheme( data: searchBarThemeData, child: child, ), ), ), ); } final Finder findMaterial = find.descendant( of: find.byType(SearchBar), matching: find.byType(Material), ); final Finder findInkWell = find.descendant( of: find.byType(SearchBar), matching: find.byType(InkWell), ); const Set<MaterialState> hovered = <MaterialState>{ MaterialState.hovered}; const Set<MaterialState> focused = <MaterialState>{ MaterialState.focused}; const Set<MaterialState> pressed = <MaterialState>{ MaterialState.pressed}; Future<void> checkSearchBar(WidgetTester tester) async { final Material material = tester.widget<Material>(findMaterial); final InkWell inkWell = tester.widget<InkWell>(findInkWell); expect(material.elevation, elevationValue); expect(material.color, backgroundColorValue); expect(material.shadowColor, shadowColorValue); expect(material.surfaceTintColor, surfaceTintColorValue); expect(material.shape, shapeValue); expect(inkWell.overlayColor!.resolve(hovered), overlayColor.resolve(hovered)); expect(inkWell.overlayColor!.resolve(focused), overlayColor.resolve(focused)); expect(inkWell.overlayColor!.resolve(pressed), overlayColor.resolve(pressed)); expect(inkWell.customBorder, shapeValue); expect(tester.getSize(find.byType(SearchBar)), const Size(300.0, 80.0)); final Text hintText = tester.widget(find.text('hint')); expect(hintText.style?.color, hintStyleValue.color); expect(hintText.style?.fontSize, hintStyleValue.fontSize); await tester.enterText(find.byType(TextField), 'input'); final EditableText inputText = tester.widget(find.text('input')); expect(inputText.style.color, textStyleValue.color); expect(inputText.style.fontSize, textStyleValue.fontSize); final Rect barRect = tester.getRect(find.byType(SearchBar)); final Rect leadingRect = tester.getRect(find.byIcon(Icons.search)); final Rect textFieldRect = tester.getRect(find.byType(TextField)); final Rect trailingRect = tester.getRect(find.byIcon(Icons.menu)); expect(barRect.left, leadingRect.left - 16.0); expect(leadingRect.right, textFieldRect.left - 16.0); expect(textFieldRect.right, trailingRect.left - 16.0); expect(trailingRect.right, barRect.right - 16.0); } testWidgets('SearchBar properties overrides defaults', (WidgetTester tester) async { await tester.pumpWidget(buildFrame(useSearchBarProperties: true)); await tester.pumpAndSettle(); // allow the animations to finish checkSearchBar(tester); }); testWidgets('SearchBar theme data overrides defaults', (WidgetTester tester) async { await tester.pumpWidget(buildFrame(searchBarThemeData: searchBarTheme)); await tester.pumpAndSettle(); checkSearchBar(tester); }); testWidgets('Overall Theme SearchBar theme overrides defaults', (WidgetTester tester) async { await tester.pumpWidget(buildFrame(overallTheme: searchBarTheme)); await tester.pumpAndSettle(); checkSearchBar(tester); }); // Same as the previous tests with empty SearchBarThemeData's instead of null. testWidgets('SearchBar properties overrides defaults, empty theme and overall theme', (WidgetTester tester) async { await tester.pumpWidget(buildFrame(useSearchBarProperties: true, searchBarThemeData: const SearchBarThemeData(), overallTheme: const SearchBarThemeData())); await tester.pumpAndSettle(); // allow the animations to finish checkSearchBar(tester); }); testWidgets('SearchBar theme overrides defaults and overall theme', (WidgetTester tester) async { await tester.pumpWidget(buildFrame(searchBarThemeData: searchBarTheme, overallTheme: const SearchBarThemeData())); await tester.pumpAndSettle(); // allow the animations to finish checkSearchBar(tester); }); testWidgets('Overall Theme SearchBar theme overrides defaults and null theme', (WidgetTester tester) async { await tester.pumpWidget(buildFrame(overallTheme: searchBarTheme)); await tester.pumpAndSettle(); // allow the animations to finish checkSearchBar(tester); }); }); }