Avoid using a Key whose only purpose is to be looked up in a test (#170952)

This PR investigates moving away from the few places where we rely on
Keys to instead solely look up a widget in a test. Reasons:

 * No extra stuff in users' apps that they don't need for production.
* Avoid collisions between Keys in users' apps that might have the same
identifier string.

I'm definitely still up for discussion on whether or not this is a good
idea!

Written in response to the discussion on
https://github.com/flutter/flutter/pull/170761#discussion_r2159760635.
This commit is contained in:
Justin McCandless 2025-06-26 16:18:05 -07:00 committed by GitHub
parent 6b7662649f
commit 142cce676e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 24 additions and 41 deletions

View File

@ -435,7 +435,6 @@ class PopupMenuItemState<T, W extends PopupMenuItem<T>> extends State<W> {
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: widget.height),
child: Padding(
key: const Key('menu item padding'),
padding: padding,
child: Align(alignment: AlignmentDirectional.centerStart, child: buildChild()),
),

View File

@ -1615,7 +1615,7 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin {
onPanUpdate: _handlePanUpdate,
onPanEnd: _handlePanEnd,
onTapUp: _handleTapUp,
child: CustomPaint(key: const ValueKey<String>('time-picker-dial'), painter: painter),
child: CustomPaint(painter: painter),
);
}
}

View File

@ -1756,14 +1756,7 @@ void main() {
await tester.pumpAndSettle();
EdgeInsetsGeometry paddingFor(String text) {
return tester
.widget<Padding>(
find.ancestor(
of: find.widgetWithText(Align, 'Item 0'),
matching: find.byKey(const Key('menu item padding')),
),
)
.padding;
return tester.widget<Padding>(find.widgetWithText(Padding, 'Item 0').first).padding;
}
expect(paddingFor('Item 0'), const EdgeInsets.symmetric(horizontal: 12.0));
@ -1831,14 +1824,7 @@ void main() {
await tester.pumpAndSettle();
EdgeInsetsGeometry paddingFor(String text) {
return tester
.widget<Padding>(
find.ancestor(
of: find.widgetWithText(Align, 'Item 0'),
matching: find.byKey(const Key('menu item padding')),
),
)
.padding;
return tester.widget<Padding>(find.widgetWithText(Padding, 'Item 0').first).padding;
}
expect(paddingFor('Item 0'), const EdgeInsets.symmetric(horizontal: 16.0));

View File

@ -2653,9 +2653,11 @@ Future<Offset?> startPicker(
);
await tester.tap(find.text('X'));
await tester.pumpAndSettle(const Duration(seconds: 1));
return entryMode == TimePickerEntryMode.dial
? tester.getCenter(find.byKey(const ValueKey<String>('time-picker-dial')))
: null;
final Finder customPaintFinder = find.descendant(
of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_Dial'),
matching: find.byType(CustomPaint),
);
return entryMode == TimePickerEntryMode.dial ? tester.getCenter(customPaintFinder) : null;
}
Future<void> finishPicker(WidgetTester tester) async {

View File

@ -257,12 +257,11 @@ void main() {
SystemChannels.system.codec.encodeMessage(data),
(ByteData? data) {},
);
final RenderObject renderObject = tester.renderObject(
find.descendant(
of: find.byKey(const Key('parent')),
matching: find.byKey(const ValueKey<String>('time-picker-dial')),
),
final Finder customPaintFinder = find.descendant(
of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_Dial'),
matching: find.byType(CustomPaint),
);
final RenderObject renderObject = tester.renderObject(customPaintFinder);
expect(renderObject.debugNeedsPaint, isTrue);
});
}

View File

@ -6,6 +6,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
final Finder dialCustomPaintFinder = find.descendant(
of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_Dial'),
matching: find.byType(CustomPaint),
);
void main() {
testWidgets('Material2 - can localize the header in all known formats - portrait', (
WidgetTester tester,
@ -567,7 +572,7 @@ void main() {
locale: locale,
useMaterial3: false,
);
final Size size = tester.getSize(find.byKey(const Key('time-picker-dial')));
final Size size = tester.getSize(dialCustomPaintFinder);
final double dy = (size.height / 2.0 / 10) * i;
await tester.tapAt(Offset(center.dx, center.dy - dy));
await finishPicker(tester);
@ -598,7 +603,7 @@ void main() {
locale: locale,
useMaterial3: true,
);
final Size size = tester.getSize(find.byKey(const Key('time-picker-dial')));
final Size size = tester.getSize(dialCustomPaintFinder);
final double dy = (size.height / 2.0 / 10) * factor;
await tester.tapAt(Offset(center.dx, center.dy - dy));
await finishPicker(tester);
@ -740,9 +745,7 @@ void main() {
) async {
await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: false, useMaterial3: false);
final CustomPaint dialPaint = tester.widget(
find.byKey(const ValueKey<String>('time-picker-dial')),
);
final CustomPaint dialPaint = tester.widget(dialCustomPaintFinder);
final dynamic dialPainter = dialPaint.painter;
// ignore: avoid_dynamic_calls
final List<dynamic> primaryLabels = dialPainter.primaryLabels as List<dynamic>;
@ -770,9 +773,7 @@ void main() {
) async {
await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: false, useMaterial3: true);
final CustomPaint dialPaint = tester.widget(
find.byKey(const ValueKey<String>('time-picker-dial')),
);
final CustomPaint dialPaint = tester.widget(dialCustomPaintFinder);
final dynamic dialPainter = dialPaint.painter;
// ignore: avoid_dynamic_calls
final List<dynamic> primaryLabels = dialPainter.primaryLabels as List<dynamic>;
@ -800,9 +801,7 @@ void main() {
) async {
await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, useMaterial3: true);
final CustomPaint dialPaint = tester.widget(
find.byKey(const ValueKey<String>('time-picker-dial')),
);
final CustomPaint dialPaint = tester.widget(dialCustomPaintFinder);
final dynamic dialPainter = dialPaint.painter;
// ignore: avoid_dynamic_calls
final List<dynamic> primaryLabels = dialPainter.primaryLabels as List<dynamic>;
@ -830,9 +829,7 @@ void main() {
) async {
await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, useMaterial3: false);
final CustomPaint dialPaint = tester.widget(
find.byKey(const ValueKey<String>('time-picker-dial')),
);
final CustomPaint dialPaint = tester.widget(dialCustomPaintFinder);
final dynamic dialPainter = dialPaint.painter;
// ignore: avoid_dynamic_calls
final List<dynamic> primaryLabels = dialPainter.primaryLabels as List<dynamic>;
@ -968,7 +965,7 @@ Future<Offset> startPicker(
);
await tester.tap(find.text('X'));
await tester.pumpAndSettle(const Duration(seconds: 1));
return tester.getCenter(find.byKey(const Key('time-picker-dial')));
return tester.getCenter(dialCustomPaintFinder);
}
Future<void> finishPicker(WidgetTester tester) async {