Skip to content

Commit 0852456

Browse files
AlexV525Mairramer
authored andcommitted
flutter config --list (flutter#135401)
Resolves flutter#81831. The PR improves the `config` command in below ways: - Does not print the settings in usages or other options. - Adds the `--list` flag to print the full settings list. - Separates usages for settings and analytics. - Prints the restart tip when clearing features. Update editable_text.dart change property name fix text_field.dart fix editable_text.dart fix editable_text.dart fix text_field_test.dart Update editable_text.dart Update text_field_test.dart
1 parent 217b222 commit 0852456

File tree

8 files changed

+117
-60
lines changed

8 files changed

+117
-60
lines changed

packages/flutter/lib/src/cupertino/text_field.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ class CupertinoTextField extends StatefulWidget {
285285
this.contextMenuBuilder = _defaultContextMenuBuilder,
286286
this.spellCheckConfiguration,
287287
this.magnifierConfiguration,
288-
this.canTapOutsideFocus = true,
288+
this.onTapOutsideRequiresFocus = false,
289289
}) : assert(obscuringCharacter.length == 1),
290290
smartDashesType = smartDashesType ?? (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled),
291291
smartQuotesType = smartQuotesType ?? (obscureText ? SmartQuotesType.disabled : SmartQuotesType.enabled),
@@ -412,7 +412,7 @@ class CupertinoTextField extends StatefulWidget {
412412
this.contextMenuBuilder = _defaultContextMenuBuilder,
413413
this.spellCheckConfiguration,
414414
this.magnifierConfiguration,
415-
this.canTapOutsideFocus = true,
415+
this.onTapOutsideRequiresFocus = false,
416416
}) : assert(obscuringCharacter.length == 1),
417417
smartDashesType = smartDashesType ?? (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled),
418418
smartQuotesType = smartQuotesType ?? (obscureText ? SmartQuotesType.disabled : SmartQuotesType.enabled),
@@ -815,8 +815,8 @@ class CupertinoTextField extends StatefulWidget {
815815
/// {@macro flutter.widgets.undoHistory.controller}
816816
final UndoHistoryController? undoController;
817817

818-
/// {@macro flutter.widgets.editableText.canTapOutsideFocus}
819-
final bool canTapOutsideFocus;
818+
/// {@macro flutter.widgets.editableText.onTapOutsideRequiresFocus}
819+
final bool onTapOutsideRequiresFocus;
820820

821821
@override
822822
State<CupertinoTextField> createState() => _CupertinoTextFieldState();

packages/flutter/lib/src/material/text_field.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ class TextField extends StatefulWidget {
311311
this.canRequestFocus = true,
312312
this.spellCheckConfiguration,
313313
this.magnifierConfiguration,
314-
this.canTapOutsideFocus = true,
314+
this.onTapOutsideRequiresFocus = false,
315315
}) : assert(obscuringCharacter.length == 1),
316316
smartDashesType = smartDashesType ?? (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled),
317317
smartQuotesType = smartQuotesType ?? (obscureText ? SmartQuotesType.disabled : SmartQuotesType.enabled),
@@ -776,8 +776,8 @@ class TextField extends StatefulWidget {
776776
/// {@macro flutter.widgets.undoHistory.controller}
777777
final UndoHistoryController? undoController;
778778

779-
/// {@macro flutter.widgets.editableText.canTapOutsideFocus}
780-
final bool canTapOutsideFocus;
779+
/// {@macro flutter.widgets.editableText.onTapOutsideRequiresFocus}
780+
final bool onTapOutsideRequiresFocus;
781781

782782
static Widget _defaultContextMenuBuilder(BuildContext context, EditableTextState editableTextState) {
783783
return AdaptiveTextSelectionToolbar.editableText(
@@ -1436,7 +1436,7 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements
14361436
contextMenuBuilder: widget.contextMenuBuilder,
14371437
spellCheckConfiguration: spellCheckConfiguration,
14381438
magnifierConfiguration: widget.magnifierConfiguration ?? TextMagnifier.adaptiveMagnifierConfiguration,
1439-
canTapOutsideFocus: widget.canTapOutsideFocus,
1439+
onTapOutsideRequiresFocus: widget.onTapOutsideRequiresFocus,
14401440
),
14411441
),
14421442
);

packages/flutter/lib/src/material/text_form_field.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ class TextFormField extends FormField<String> {
169169
Clip clipBehavior = Clip.hardEdge,
170170
bool scribbleEnabled = true,
171171
bool canRequestFocus = true,
172-
bool canTapOutsideFocus = true,
172+
bool onTapOutsideRequiresFocus = false,
173173
}) : assert(initialValue == null || controller == null),
174174
assert(obscuringCharacter.length == 1),
175175
assert(maxLines == null || maxLines > 0),
@@ -260,7 +260,7 @@ class TextFormField extends FormField<String> {
260260
clipBehavior: clipBehavior,
261261
scribbleEnabled: scribbleEnabled,
262262
canRequestFocus: canRequestFocus,
263-
canTapOutsideFocus: canTapOutsideFocus,
263+
onTapOutsideRequiresFocus: onTapOutsideRequiresFocus,
264264
),
265265
);
266266
},

packages/flutter/lib/src/widgets/editable_text.dart

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@ class EditableText extends StatefulWidget {
811811
this.spellCheckConfiguration,
812812
this.magnifierConfiguration = TextMagnifierConfiguration.disabled,
813813
this.undoController,
814-
this.canTapOutsideFocus = true,
814+
this.onTapOutsideRequiresFocus = false,
815815
}) : assert(obscuringCharacter.length == 1),
816816
smartDashesType = smartDashesType ?? (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled),
817817
smartQuotesType = smartQuotesType ?? (obscureText ? SmartQuotesType.disabled : SmartQuotesType.enabled),
@@ -1402,8 +1402,9 @@ class EditableText extends StatefulWidget {
14021402

14031403
/// {@template flutter.widgets.editableText.onTapOutside}
14041404
/// Called for each tap that occurs outside of the [TextFieldTapRegion] group
1405-
/// when [canTapOutsideFocus] is set to true.
1406-
/// When [canTapOutsideFocus] is set to false, this method will be called only
1405+
/// when [onTapOutsideRequiresFocus] is set to false.
1406+
///
1407+
/// When [onTapOutsideRequiresFocus] is set to true, this method will be called only
14071408
/// when the text field is focused.
14081409
///
14091410
/// If this is null, [FocusNode.unfocus] will be called on the [focusNode] for
@@ -1848,12 +1849,12 @@ class EditableText extends StatefulWidget {
18481849
/// {@macro flutter.widgets.magnifier.TextMagnifierConfiguration.details}
18491850
final TextMagnifierConfiguration magnifierConfiguration;
18501851

1851-
/// {@template flutter.widgets.editableText.canTapOutsideFocus}
1852+
/// {@template flutter.widgets.editableText.onTapOutsideRequiresFocus}
18521853
/// Determine whether [onTapOutside] is called when this widget is not focused.
18531854
///
1854-
/// Defaults to true.
1855+
/// Defaults to false.
18551856
/// {@endtemplate}
1856-
final bool canTapOutsideFocus;
1857+
final bool onTapOutsideRequiresFocus;
18571858

18581859
bool get _userSelectionEnabled => enableInteractiveSelection && (!readOnly || !obscureText);
18591860

@@ -4781,7 +4782,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
47814782
compositeCallback: _compositeCallback,
47824783
enabled: _hasInputConnection,
47834784
child: TextFieldTapRegion(
4784-
onTapOutside: _hasFocus || widget.canTapOutsideFocus ? widget.onTapOutside ?? _defaultOnTapOutside : null,
4785+
onTapOutside: _hasFocus || !widget.onTapOutsideRequiresFocus ? widget.onTapOutside ?? _defaultOnTapOutside : null,
47854786
debugLabel: kReleaseMode ? null : 'EditableText',
47864787
child: MouseRegion(
47874788
cursor: widget.mouseCursor ?? SystemMouseCursors.text,

packages/flutter/test/material/text_field_test.dart

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16893,7 +16893,7 @@ void main() {
1689316893
TextField(
1689416894
autofocus: true,
1689516895
focusNode: focusNodeA,
16896-
canTapOutsideFocus: false,
16896+
onTapOutsideRequiresFocus: true,
1689716897
onTapOutside: (PointerDownEvent event) {
1689816898
timesTriggered += 1;
1689916899
},
@@ -16922,7 +16922,15 @@ void main() {
1692216922
await tester.pumpAndSettle();
1692316923

1692416924
expect(timesTriggered, 0);
16925-
}, variant: TargetPlatformVariant.all());
16925+
16926+
focusNodeA..requestFocus();
16927+
await tester.pump();
16928+
16929+
await click(find.text('Outside'));
16930+
await tester.pumpAndSettle();
16931+
16932+
expect(timesTriggered, 1);
16933+
}, variant: TargetPlatformVariant.all());
1692616934
}}
1692716935
});
1692816936

packages/flutter_tools/lib/src/commands/config.dart

Lines changed: 59 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ import '../runner/flutter_command_runner.dart';
1515

1616
class ConfigCommand extends FlutterCommand {
1717
ConfigCommand({ bool verboseHelp = false }) {
18+
argParser.addFlag(
19+
'list',
20+
help: 'List all settings and their current values.',
21+
negatable: false,
22+
);
1823
argParser.addFlag('analytics',
1924
hide: !verboseHelp,
2025
help: 'Enable or disable reporting anonymously tool usage statistics and crash reports.\n'
@@ -73,37 +78,7 @@ class ConfigCommand extends FlutterCommand {
7378
bool get shouldUpdateCache => false;
7479

7580
@override
76-
String get usageFooter {
77-
// List all config settings. for feature flags, include whether they
78-
// are available.
79-
final Map<String, Feature> featuresByName = <String, Feature>{};
80-
final String channel = globals.flutterVersion.channel;
81-
for (final Feature feature in allFeatures) {
82-
final String? configSetting = feature.configSetting;
83-
if (configSetting != null) {
84-
featuresByName[configSetting] = feature;
85-
}
86-
}
87-
String values = globals.config.keys
88-
.map<String>((String key) {
89-
String configFooter = '';
90-
if (featuresByName.containsKey(key)) {
91-
final FeatureChannelSetting setting = featuresByName[key]!.getSettingForChannel(channel);
92-
if (!setting.available) {
93-
configFooter = '(Unavailable)';
94-
}
95-
}
96-
return ' $key: ${globals.config.getValue(key)} $configFooter';
97-
}).join('\n');
98-
if (values.isEmpty) {
99-
values = ' No settings have been configured.';
100-
}
101-
final bool analyticsEnabled = globals.flutterUsage.enabled &&
102-
!globals.flutterUsage.suppressAnalytics;
103-
return
104-
'\nSettings:\n$values\n\n'
105-
'Analytics reporting is currently ${analyticsEnabled ? 'enabled' : 'disabled'}.';
106-
}
81+
String get usageFooter => '\n$analyticsUsage';
10782

10883
/// Return null to disable analytics recording of the `config` command.
10984
@override
@@ -121,6 +96,11 @@ class ConfigCommand extends FlutterCommand {
12196
' flutter config --android-studio-dir "/opt/Android Studio"');
12297
}
12398

99+
if (boolArg('list')) {
100+
globals.printStatus(settingsText);
101+
return FlutterCommandResult.success();
102+
}
103+
124104
if (boolArg('machine')) {
125105
await handleMachine();
126106
return FlutterCommandResult.success();
@@ -133,6 +113,7 @@ class ConfigCommand extends FlutterCommand {
133113
globals.config.removeValue(configSetting);
134114
}
135115
}
116+
globals.printStatus(requireReloadTipText);
136117
return FlutterCommandResult.success();
137118
}
138119

@@ -195,7 +176,7 @@ class ConfigCommand extends FlutterCommand {
195176
if (argResults == null || argResults!.arguments.isEmpty) {
196177
globals.printStatus(usage);
197178
} else {
198-
globals.printStatus('\nYou may need to restart any open editors for them to read new settings.');
179+
globals.printStatus('\n$requireReloadTipText');
199180
}
200181

201182
return FlutterCommandResult.success();
@@ -234,4 +215,50 @@ class ConfigCommand extends FlutterCommand {
234215
globals.printStatus('Setting "$keyName" value to "$keyValue".');
235216
}
236217
}
218+
219+
/// List all config settings. for feature flags, include whether they are available.
220+
String get settingsText {
221+
final Map<String, Feature> featuresByName = <String, Feature>{};
222+
final String channel = globals.flutterVersion.channel;
223+
for (final Feature feature in allFeatures) {
224+
final String? configSetting = feature.configSetting;
225+
if (configSetting != null) {
226+
featuresByName[configSetting] = feature;
227+
}
228+
}
229+
final Set<String> keys = <String>{
230+
...allFeatures.map((Feature e) => e.configSetting).whereType<String>(),
231+
...globals.config.keys,
232+
};
233+
final Iterable<String> settings = keys.map<String>((String key) {
234+
Object? value = globals.config.getValue(key);
235+
value ??= '(Not set)';
236+
final StringBuffer buffer = StringBuffer(' $key: $value');
237+
if (featuresByName.containsKey(key)) {
238+
final FeatureChannelSetting setting = featuresByName[key]!.getSettingForChannel(channel);
239+
if (!setting.available) {
240+
buffer.write(' (Unavailable)');
241+
}
242+
}
243+
return buffer.toString();
244+
});
245+
final StringBuffer buffer = StringBuffer();
246+
buffer.writeln('All Settings:');
247+
if (settings.isEmpty) {
248+
buffer.writeln(' No configs have been configured.');
249+
} else {
250+
buffer.writeln(settings.join('\n'));
251+
}
252+
return buffer.toString();
253+
}
254+
255+
/// List the status of the analytics reporting.
256+
String get analyticsUsage {
257+
final bool analyticsEnabled =
258+
globals.flutterUsage.enabled && !globals.flutterUsage.suppressAnalytics;
259+
return 'Analytics reporting is currently ${analyticsEnabled ? 'enabled' : 'disabled'}.';
260+
}
261+
262+
/// Raising the reload tip for setting changes.
263+
final String requireReloadTipText = 'You may need to restart any open editors for them to read new settings.';
237264
}

packages/flutter_tools/test/commands.shard/hermetic/config_test.dart

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import 'package:flutter_tools/src/base/file_system.dart';
1212
import 'package:flutter_tools/src/build_info.dart';
1313
import 'package:flutter_tools/src/cache.dart';
1414
import 'package:flutter_tools/src/commands/config.dart';
15+
import 'package:flutter_tools/src/features.dart';
1516
import 'package:flutter_tools/src/globals.dart' as globals;
1617
import 'package:flutter_tools/src/reporting/reporting.dart';
1718
import 'package:flutter_tools/src/version.dart';
@@ -48,6 +49,23 @@ void main() {
4849
}
4950

5051
group('config', () {
52+
testUsingContext('prints all settings with --list', () async {
53+
final ConfigCommand configCommand = ConfigCommand();
54+
final CommandRunner<void> commandRunner = createTestCommandRunner(configCommand);
55+
await commandRunner.run(<String>['config', '--list']);
56+
expect(
57+
testLogger.statusText,
58+
'All Settings:\n'
59+
'${allFeatures
60+
.where((Feature e) => e.configSetting != null)
61+
.map((Feature e) => ' ${e.configSetting}: (Not set)')
62+
.join('\n')}'
63+
'\n\n',
64+
);
65+
}, overrides: <Type, Generator>{
66+
Usage: () => testUsage,
67+
});
68+
5169
testUsingContext('throws error on excess arguments', () {
5270
final ConfigCommand configCommand = ConfigCommand();
5371
final CommandRunner<void> commandRunner = createTestCommandRunner(configCommand);
@@ -196,6 +214,7 @@ void main() {
196214

197215
await commandRunner.run(<String>[
198216
'config',
217+
'--list'
199218
]);
200219

201220
expect(
@@ -270,20 +289,21 @@ void main() {
270289
Usage: () => testUsage,
271290
});
272291

273-
testUsingContext('analytics reported disabled when suppressed', () async {
292+
testUsingContext('analytics reported with help usages', () async {
274293
final ConfigCommand configCommand = ConfigCommand();
275-
final CommandRunner<void> commandRunner = createTestCommandRunner(configCommand);
294+
createTestCommandRunner(configCommand);
276295

277296
testUsage.suppressAnalytics = true;
278-
279-
await commandRunner.run(<String>[
280-
'config',
281-
]);
282-
283297
expect(
284-
testLogger.statusText,
298+
configCommand.usage,
285299
containsIgnoringWhitespace('Analytics reporting is currently disabled'),
286300
);
301+
302+
testUsage.suppressAnalytics = false;
303+
expect(
304+
configCommand.usage,
305+
containsIgnoringWhitespace('Analytics reporting is currently enabled'),
306+
);
287307
}, overrides: <Type, Generator>{
288308
Usage: () => testUsage,
289309
});

packages/flutter_tools/test/integration.shard/command_output_test.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,12 @@ void main() {
7171
expect(result.stdout, contains('Shutdown hooks complete'));
7272
});
7373

74-
testWithoutContext('flutter config contains all features', () async {
74+
testWithoutContext('flutter config --list contains all features', () async {
7575
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
7676
final ProcessResult result = await processManager.run(<String>[
7777
flutterBin,
7878
'config',
79+
'--list'
7980
]);
8081

8182
// contains all of the experiments in features.dart

0 commit comments

Comments
 (0)