mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
WIP Commits separated as follows: - Update lints in analysis_options files - Run `dart fix --apply` - Clean up leftover analysis issues - Run `dart format .` in the right places. Local analysis and testing passes. Checking CI now. Part of https://github.com/flutter/flutter/issues/178827 - Adoption of flutter_lints in examples/api coming in a separate change (cc @loic-sharma) ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
284 lines
11 KiB
Dart
284 lines
11 KiB
Dart
// 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('SearchViewThemeData copyWith, ==, hashCode basics', () {
|
|
expect(const SearchViewThemeData(), const SearchViewThemeData().copyWith());
|
|
expect(const SearchViewThemeData().hashCode, const SearchViewThemeData().copyWith().hashCode);
|
|
});
|
|
|
|
test('SearchViewThemeData lerp special cases', () {
|
|
expect(SearchViewThemeData.lerp(null, null, 0), null);
|
|
const data = SearchViewThemeData();
|
|
expect(identical(SearchViewThemeData.lerp(data, data, 0.5), data), true);
|
|
});
|
|
|
|
test('SearchViewThemeData defaults', () {
|
|
const themeData = SearchViewThemeData();
|
|
expect(themeData.backgroundColor, null);
|
|
expect(themeData.elevation, null);
|
|
expect(themeData.surfaceTintColor, null);
|
|
expect(themeData.constraints, null);
|
|
expect(themeData.side, null);
|
|
expect(themeData.shape, null);
|
|
expect(themeData.headerHeight, null);
|
|
expect(themeData.headerTextStyle, null);
|
|
expect(themeData.headerHintStyle, null);
|
|
expect(themeData.padding, null);
|
|
expect(themeData.barPadding, null);
|
|
expect(themeData.shrinkWrap, null);
|
|
expect(themeData.dividerColor, null);
|
|
|
|
const theme = SearchViewTheme(data: SearchViewThemeData(), child: SizedBox());
|
|
expect(theme.data.backgroundColor, null);
|
|
expect(theme.data.elevation, null);
|
|
expect(theme.data.surfaceTintColor, null);
|
|
expect(theme.data.constraints, null);
|
|
expect(theme.data.side, null);
|
|
expect(theme.data.shape, null);
|
|
expect(theme.data.headerHeight, null);
|
|
expect(theme.data.headerTextStyle, null);
|
|
expect(theme.data.headerHintStyle, null);
|
|
expect(themeData.padding, null);
|
|
expect(themeData.barPadding, null);
|
|
expect(themeData.shrinkWrap, null);
|
|
expect(theme.data.dividerColor, null);
|
|
});
|
|
|
|
testWidgets('Default SearchViewThemeData debugFillProperties', (WidgetTester tester) async {
|
|
final builder = DiagnosticPropertiesBuilder();
|
|
const SearchViewThemeData().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('SearchViewThemeData implements debugFillProperties', (WidgetTester tester) async {
|
|
final builder = DiagnosticPropertiesBuilder();
|
|
const SearchViewThemeData(
|
|
backgroundColor: Color(0xfffffff1),
|
|
elevation: 3.5,
|
|
surfaceTintColor: Color(0xfffffff3),
|
|
side: BorderSide(width: 2.5, color: Color(0xfffffff5)),
|
|
shape: RoundedRectangleBorder(),
|
|
headerHeight: 35.5,
|
|
headerTextStyle: TextStyle(fontSize: 24.0),
|
|
headerHintStyle: TextStyle(fontSize: 16.0),
|
|
constraints: BoxConstraints(minWidth: 350, minHeight: 240),
|
|
padding: EdgeInsets.only(bottom: 32.0),
|
|
barPadding: EdgeInsets.zero,
|
|
shrinkWrap: true,
|
|
).debugFillProperties(builder);
|
|
|
|
final List<String> description = builder.properties
|
|
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
|
.map((DiagnosticsNode node) => node.toString())
|
|
.toList();
|
|
|
|
expect(description[0], 'backgroundColor: ${const Color(0xfffffff1)}');
|
|
expect(description[1], 'elevation: 3.5');
|
|
expect(description[2], 'surfaceTintColor: ${const Color(0xfffffff3)}');
|
|
expect(description[3], 'side: BorderSide(color: ${const Color(0xfffffff5)}, width: 2.5)');
|
|
expect(
|
|
description[4],
|
|
'shape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero)',
|
|
);
|
|
expect(description[5], 'headerHeight: 35.5');
|
|
expect(description[6], 'headerTextStyle: TextStyle(inherit: true, size: 24.0)');
|
|
expect(description[7], 'headerHintStyle: TextStyle(inherit: true, size: 16.0)');
|
|
expect(description[8], 'constraints: BoxConstraints(350.0<=w<=Infinity, 240.0<=h<=Infinity)');
|
|
expect(description[9], 'padding: EdgeInsets(0.0, 0.0, 0.0, 32.0)');
|
|
expect(description[10], 'barPadding: EdgeInsets.zero');
|
|
expect(description[11], 'shrinkWrap: true');
|
|
});
|
|
|
|
group('[Theme, SearchViewTheme, SearchView properties overrides]', () {
|
|
const backgroundColor = Color(0xff000001);
|
|
const elevation = 5.0;
|
|
const surfaceTintColor = Color(0xff000002);
|
|
const side = BorderSide(color: Color(0xff000003), width: 2.0);
|
|
const OutlinedBorder shape = RoundedRectangleBorder(
|
|
side: side,
|
|
borderRadius: BorderRadius.all(Radius.circular(20.0)),
|
|
);
|
|
const headerHeight = 45.0;
|
|
const headerTextStyle = TextStyle(color: Color(0xff000004), fontSize: 20.0);
|
|
const headerHintStyle = TextStyle(color: Color(0xff000005), fontSize: 18.0);
|
|
const constraints = BoxConstraints(minWidth: 250.0, maxWidth: 300.0, minHeight: 450.0);
|
|
|
|
const searchViewTheme = SearchViewThemeData(
|
|
backgroundColor: backgroundColor,
|
|
elevation: elevation,
|
|
surfaceTintColor: surfaceTintColor,
|
|
side: side,
|
|
shape: shape,
|
|
headerHeight: headerHeight,
|
|
headerTextStyle: headerTextStyle,
|
|
headerHintStyle: headerHintStyle,
|
|
constraints: constraints,
|
|
);
|
|
|
|
Widget buildFrame({
|
|
bool useSearchViewProperties = false,
|
|
SearchViewThemeData? searchViewThemeData,
|
|
SearchViewThemeData? overallTheme,
|
|
}) {
|
|
final Widget child = Builder(
|
|
builder: (BuildContext context) {
|
|
if (!useSearchViewProperties) {
|
|
return SearchAnchor(
|
|
viewHintText: 'hint text',
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return const Icon(Icons.search);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
isFullScreen: false,
|
|
);
|
|
}
|
|
return SearchAnchor(
|
|
viewHintText: 'hint text',
|
|
builder: (BuildContext context, SearchController controller) {
|
|
return const Icon(Icons.search);
|
|
},
|
|
suggestionsBuilder: (BuildContext context, SearchController controller) {
|
|
return <Widget>[];
|
|
},
|
|
isFullScreen: false,
|
|
viewElevation: elevation,
|
|
viewBackgroundColor: backgroundColor,
|
|
viewSurfaceTintColor: surfaceTintColor,
|
|
viewSide: side,
|
|
viewShape: shape,
|
|
headerHeight: headerHeight,
|
|
headerTextStyle: headerTextStyle,
|
|
headerHintStyle: headerHintStyle,
|
|
viewConstraints: constraints,
|
|
);
|
|
},
|
|
);
|
|
return MaterialApp(
|
|
theme: ThemeData.from(
|
|
colorScheme: const ColorScheme.light(),
|
|
).copyWith(searchViewTheme: overallTheme),
|
|
home: Scaffold(
|
|
body: Center(
|
|
// If the SearchViewThemeData widget is present, it's used
|
|
// instead of the Theme's ThemeData.searchViewTheme.
|
|
child: searchViewThemeData == null
|
|
? child
|
|
: SearchViewTheme(data: searchViewThemeData, child: child),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Finder findViewContent() {
|
|
return find.byWidgetPredicate((Widget widget) {
|
|
return widget.runtimeType.toString() == '_ViewContent';
|
|
});
|
|
}
|
|
|
|
Material getSearchViewMaterial(WidgetTester tester) {
|
|
return tester.widget<Material>(
|
|
find.descendant(of: findViewContent(), matching: find.byType(Material)).first,
|
|
);
|
|
}
|
|
|
|
Future<void> checkSearchView(WidgetTester tester) async {
|
|
final Material material = getSearchViewMaterial(tester);
|
|
expect(material.elevation, elevation);
|
|
expect(material.color, backgroundColor);
|
|
expect(material.surfaceTintColor, surfaceTintColor);
|
|
expect(material.shape, shape);
|
|
|
|
final Size size = tester.getSize(
|
|
find.descendant(of: findViewContent(), matching: find.byType(ConstrainedBox)).first,
|
|
);
|
|
expect(size.width, 250.0);
|
|
expect(size.height, 450.0);
|
|
|
|
final Text hintText = tester.widget(find.text('hint text'));
|
|
expect(hintText.style?.color, headerHintStyle.color);
|
|
expect(hintText.style?.fontSize, headerHintStyle.fontSize);
|
|
|
|
final RenderBox box = tester.renderObject(
|
|
find.descendant(of: findViewContent(), matching: find.byType(SearchBar)),
|
|
);
|
|
expect(box.size.height, headerHeight);
|
|
await tester.enterText(find.byType(TextField), 'input');
|
|
final EditableText inputText = tester.widget(find.text('input'));
|
|
expect(inputText.style.color, headerTextStyle.color);
|
|
expect(inputText.style.fontSize, headerTextStyle.fontSize);
|
|
}
|
|
|
|
testWidgets('SearchView properties overrides defaults', (WidgetTester tester) async {
|
|
await tester.pumpWidget(buildFrame(useSearchViewProperties: true));
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle(); // allow the animations to finish
|
|
checkSearchView(tester);
|
|
});
|
|
|
|
testWidgets('SearchView theme data overrides defaults', (WidgetTester tester) async {
|
|
await tester.pumpWidget(buildFrame(searchViewThemeData: searchViewTheme));
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
checkSearchView(tester);
|
|
});
|
|
|
|
testWidgets('Overall Theme SearchView theme overrides defaults', (WidgetTester tester) async {
|
|
await tester.pumpWidget(buildFrame(overallTheme: searchViewTheme));
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle();
|
|
checkSearchView(tester);
|
|
});
|
|
|
|
// Same as the previous tests with empty SearchViewThemeData's instead of null.
|
|
|
|
testWidgets('SearchView properties overrides defaults, empty theme and overall theme', (
|
|
WidgetTester tester,
|
|
) async {
|
|
await tester.pumpWidget(
|
|
buildFrame(
|
|
useSearchViewProperties: true,
|
|
searchViewThemeData: const SearchViewThemeData(),
|
|
overallTheme: const SearchViewThemeData(),
|
|
),
|
|
);
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle(); // allow the animations to finish
|
|
checkSearchView(tester);
|
|
});
|
|
|
|
testWidgets('SearchView theme overrides defaults and overall theme', (
|
|
WidgetTester tester,
|
|
) async {
|
|
await tester.pumpWidget(
|
|
buildFrame(searchViewThemeData: searchViewTheme, overallTheme: const SearchViewThemeData()),
|
|
);
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle(); // allow the animations to finish
|
|
checkSearchView(tester);
|
|
});
|
|
|
|
testWidgets('Overall Theme SearchView theme overrides defaults and null theme', (
|
|
WidgetTester tester,
|
|
) async {
|
|
await tester.pumpWidget(buildFrame(overallTheme: searchViewTheme));
|
|
await tester.tap(find.byIcon(Icons.search));
|
|
await tester.pumpAndSettle(); // allow the animations to finish
|
|
checkSearchView(tester);
|
|
});
|
|
});
|
|
}
|