From bb13a0e60975b0fad6d6761ba555692bf31a34a0 Mon Sep 17 00:00:00 2001 From: Robert Nystrom Date: Wed, 11 Feb 2026 07:19:00 -0800 Subject: [PATCH] Handle private named parameters in "Extract Widget". When creating the constructor parameters for the widget, if the field is private, prefer to use a private named parameter unless it collides with another public field. Bug: https://github.com/dart-lang/sdk/issues/61644 Change-Id: I2776e4a350f5034e33bd80f4686e4164e8b0bda8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/479740 Auto-Submit: Bob Nystrom Commit-Queue: Brian Wilkerson Reviewed-by: Brian Wilkerson --- .../refactoring/legacy/extract_widget.dart | 22 +++++--- .../legacy/extract_widget_test.dart | 53 ++++++++++++++++++- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/pkg/analysis_server/lib/src/services/refactoring/legacy/extract_widget.dart b/pkg/analysis_server/lib/src/services/refactoring/legacy/extract_widget.dart index 1a33cf6dd20..54a248cb538 100644 --- a/pkg/analysis_server/lib/src/services/refactoring/legacy/extract_widget.dart +++ b/pkg/analysis_server/lib/src/services/refactoring/legacy/extract_widget.dart @@ -2,6 +2,7 @@ // for details. 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:_fe_analyzer_shared/src/scanner/token_impl.dart'; import 'package:analysis_server/src/protocol_server.dart' hide Element; import 'package:analysis_server/src/services/correction/status.dart'; import 'package:analysis_server/src/services/refactoring/legacy/naming_conventions.dart'; @@ -347,18 +348,24 @@ class ExtractWidgetRefactoringImpl extends RefactoringImpl } } - // Give each private parameter a public name for the constructor. + // Give each private parameter a non-colliding public name if needed. for (var parameter in _parameters) { var name = parameter.name; if (name.startsWith('_')) { + // If the library supports private named parameters, we only need to + // rename if the public name collides. var baseName = name.substring(1); - for (var i = 1; ; i++) { - name = i == 1 ? baseName : '$baseName$i'; - if (usedNames.add(name)) { - break; + if (!_featureSet.isEnabled(Feature.private_named_parameters) || + !usedNames.add(baseName)) { + for (var i = 1; ; i++) { + name = i == 1 ? baseName : '$baseName$i'; + if (usedNames.add(name)) { + break; + } } } } + parameter.constructorName = name; } @@ -589,7 +596,10 @@ class ExtractWidgetRefactoringImpl extends RefactoringImpl if (parameter != _parameters.first) { builder.write(', '); } - builder.write(parameter.constructorName); + builder.write( + correspondingPublicName(parameter.constructorName) ?? + parameter.constructorName, + ); builder.write(': '); builder.write(parameter.name); } diff --git a/pkg/analysis_server/test/services/refactoring/legacy/extract_widget_test.dart b/pkg/analysis_server/test/services/refactoring/legacy/extract_widget_test.dart index 162ebb34564..f9d96536652 100644 --- a/pkg/analysis_server/test/services/refactoring/legacy/extract_widget_test.dart +++ b/pkg/analysis_server/test/services/refactoring/legacy/extract_widget_test.dart @@ -1034,8 +1034,8 @@ class MyWidget extends StatelessWidget { class Test extends StatelessWidget { const Test({ super.key, - required String field, - }) : _field = field; + required this._field, + }); final String _field; @@ -1098,6 +1098,55 @@ class Test extends StatelessWidget { '''); } + Future test_parameters_private_unsupported() async { + await indexTestUnit(r''' +// @dart=3.10 +import 'package:flutter/material.dart'; + +class MyWidget extends StatelessWidget { + final String _field; + + MyWidget(this._field); + + @override + Widget build(BuildContext context) { + return ^Text(_field); + } +} +'''); + _createRefactoring(); + + await _assertSuccessfulRefactoring(''' +// @dart=3.10 +import 'package:flutter/material.dart'; + +class MyWidget extends StatelessWidget { + final String _field; + + MyWidget(this._field); + + @override + Widget build(BuildContext context) { + return Test(field: _field); + } +} + +class Test extends StatelessWidget { + const Test({ + super.key, + required String field, + }) : _field = field; + + final String _field; + + @override + Widget build(BuildContext context) { + return Text(_field); + } +} +'''); + } + Future test_parameters_readField_readLocal() async { await indexTestUnit(r''' import 'package:flutter/material.dart';