From 753e9613323dac358c265ee47417607da00571fa Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Mon, 16 Jun 2025 19:46:22 -0700 Subject: [PATCH] Replace `LinkedHashMap.*` with `Map.*`, which _is_ `Linked` (#170713) Pre-work to make it easier to land https://github.com/flutter/flutter/pull/170435. The default `Map` constructor is and always has been a `LinkedHashMap`: https://api.dart.dev/dart-core/Map/Map.html This change make its easier to relax the type annotation rules in a future PR. /cc @eernstg --- .../new_gallery/lib/data/demos.dart | 4 +- .../new_gallery/lib/pages/settings.dart | 46 ++++++++--------- .../lib/pages/settings_list_item.dart | 4 +- .../lib/src/foundation/observer_list.dart | 2 +- .../flutter/lib/src/rendering/editable.dart | 5 +- .../lib/src/rendering/mouse_tracker.dart | 49 ++++++++----------- .../flutter/lib/src/rendering/paragraph.dart | 5 +- 7 files changed, 49 insertions(+), 66 deletions(-) diff --git a/dev/integration_tests/new_gallery/lib/data/demos.dart b/dev/integration_tests/new_gallery/lib/data/demos.dart index e15b43995af..d902a207325 100644 --- a/dev/integration_tests/new_gallery/lib/data/demos.dart +++ b/dev/integration_tests/new_gallery/lib/data/demos.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:collection'; - import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -110,7 +108,7 @@ Future pumpDeferredLibraries() { class Demos { static Map asSlugToDemoMap(BuildContext context) { final GalleryLocalizations localizations = GalleryLocalizations.of(context)!; - return LinkedHashMap.fromIterable( + return Map.fromIterable( all(localizations), // ignore: avoid_dynamic_calls key: (dynamic demo) => demo.slug as String?, diff --git a/dev/integration_tests/new_gallery/lib/pages/settings.dart b/dev/integration_tests/new_gallery/lib/pages/settings.dart index 3412b0cffdd..ee0eed555e1 100644 --- a/dev/integration_tests/new_gallery/lib/pages/settings.dart +++ b/dev/integration_tests/new_gallery/lib/pages/settings.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:collection'; - import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_localized_locales/flutter_localized_locales.dart'; @@ -100,9 +98,9 @@ class _SettingsPageState extends State { /// Create a sorted — by native name – map of supported locales to their /// intended display string, with a system option as the first element. - LinkedHashMap _getLocaleOptions() { - final LinkedHashMap localeOptions = - LinkedHashMap.of({ + Map _getLocaleOptions() { + final Map localeOptions = + Map.of({ systemLocaleOption: DisplayOption( GalleryLocalizations.of(context)!.settingsSystemDefault + (deviceLocale != null @@ -123,7 +121,7 @@ class _SettingsPageState extends State { compareAsciiUpperCase(l1.value.title, l2.value.title), ); - localeOptions.addAll(LinkedHashMap.fromEntries(displayLocales)); + localeOptions.addAll(Map.fromEntries(displayLocales)); return localeOptions; } @@ -138,7 +136,7 @@ class _SettingsPageState extends State { SettingsListItem( title: localizations.settingsTextScaling, selectedOption: options.textScaleFactor(context, useSentinel: true), - optionsMap: LinkedHashMap.of({ + optionsMap: Map.of({ systemTextScaleFactorOption: DisplayOption(localizations.settingsSystemDefault), 0.8: DisplayOption(localizations.settingsTextScalingSmall), 1.0: DisplayOption(localizations.settingsTextScalingNormal), @@ -154,15 +152,14 @@ class _SettingsPageState extends State { SettingsListItem( title: localizations.settingsTextDirection, selectedOption: options.customTextDirection, - optionsMap: LinkedHashMap.of( - { - CustomTextDirection.localeBased: DisplayOption( - localizations.settingsTextDirectionLocaleBased, - ), - CustomTextDirection.ltr: DisplayOption(localizations.settingsTextDirectionLTR), - CustomTextDirection.rtl: DisplayOption(localizations.settingsTextDirectionRTL), - }, - ), + optionsMap: + Map.of({ + CustomTextDirection.localeBased: DisplayOption( + localizations.settingsTextDirectionLocaleBased, + ), + CustomTextDirection.ltr: DisplayOption(localizations.settingsTextDirectionLTR), + CustomTextDirection.rtl: DisplayOption(localizations.settingsTextDirectionRTL), + }), onOptionChanged: (CustomTextDirection? newTextDirection) => GalleryOptions.update( context, @@ -187,14 +184,13 @@ class _SettingsPageState extends State { SettingsListItem( title: localizations.settingsPlatformMechanics, selectedOption: options.platform, - optionsMap: - LinkedHashMap.of({ - TargetPlatform.android: DisplayOption('Android'), - TargetPlatform.iOS: DisplayOption('iOS'), - TargetPlatform.macOS: DisplayOption('macOS'), - TargetPlatform.linux: DisplayOption('Linux'), - TargetPlatform.windows: DisplayOption('Windows'), - }), + optionsMap: Map.of({ + TargetPlatform.android: DisplayOption('Android'), + TargetPlatform.iOS: DisplayOption('iOS'), + TargetPlatform.macOS: DisplayOption('macOS'), + TargetPlatform.linux: DisplayOption('Linux'), + TargetPlatform.windows: DisplayOption('Windows'), + }), onOptionChanged: (TargetPlatform? newPlatform) => GalleryOptions.update(context, options.copyWith(platform: newPlatform)), @@ -204,7 +200,7 @@ class _SettingsPageState extends State { SettingsListItem( title: localizations.settingsTheme, selectedOption: options.themeMode, - optionsMap: LinkedHashMap.of({ + optionsMap: Map.of({ ThemeMode.system: DisplayOption(localizations.settingsSystemDefault), ThemeMode.dark: DisplayOption(localizations.settingsDarkTheme), ThemeMode.light: DisplayOption(localizations.settingsLightTheme), diff --git a/dev/integration_tests/new_gallery/lib/pages/settings_list_item.dart b/dev/integration_tests/new_gallery/lib/pages/settings_list_item.dart index c8c618020d2..c1216551f97 100644 --- a/dev/integration_tests/new_gallery/lib/pages/settings_list_item.dart +++ b/dev/integration_tests/new_gallery/lib/pages/settings_list_item.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:collection'; - import 'package:flutter/material.dart'; // Common constants between SlowMotionSetting and SettingsListItem. @@ -81,7 +79,7 @@ class SettingsListItem extends StatefulWidget { required this.isExpanded, }); - final LinkedHashMap optionsMap; + final Map optionsMap; final String title; final T selectedOption; final ValueChanged onOptionChanged; diff --git a/packages/flutter/lib/src/foundation/observer_list.dart b/packages/flutter/lib/src/foundation/observer_list.dart index 76ec03f9c21..8bfe1691fb8 100644 --- a/packages/flutter/lib/src/foundation/observer_list.dart +++ b/packages/flutter/lib/src/foundation/observer_list.dart @@ -106,7 +106,7 @@ class ObserverList extends Iterable { /// /// * [ObserverList] for a list that is fast for small numbers of observers. class HashedObserverList extends Iterable { - final LinkedHashMap _map = LinkedHashMap(); + final Map _map = {}; /// Adds an item to the end of this list. /// diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart index 28fd5ff767e..a2c240145d9 100644 --- a/packages/flutter/lib/src/rendering/editable.dart +++ b/packages/flutter/lib/src/rendering/editable.dart @@ -5,7 +5,6 @@ /// @docImport 'package:flutter/cupertino.dart'; library; -import 'dart:collection'; import 'dart:math' as math; import 'dart:ui' as ui show BoxHeightStyle, BoxWidthStyle, LineMetrics, SemanticsInputType, TextBox; @@ -1308,7 +1307,7 @@ class RenderEditable extends RenderBox // can be re-used when [assembleSemanticsNode] is called again. This ensures // stable ids for the [SemanticsNode]s of [TextSpan]s across // [assembleSemanticsNode] invocations. - LinkedHashMap? _cachedChildNodes; + Map? _cachedChildNodes; /// Returns a list of rects that bound the given selection, and the text /// direction. The text direction is used by the engine to calculate @@ -1436,7 +1435,7 @@ class RenderEditable extends RenderBox int placeholderIndex = 0; int childIndex = 0; RenderBox? child = firstChild; - final LinkedHashMap newChildCache = LinkedHashMap(); + final Map newChildCache = {}; _cachedCombinedSemanticsInfos ??= combineSemanticsInfo(_semanticsInfo!); for (final InlineSpanSemanticsInformation info in _cachedCombinedSemanticsInfos!) { final TextSelection selection = TextSelection( diff --git a/packages/flutter/lib/src/rendering/mouse_tracker.dart b/packages/flutter/lib/src/rendering/mouse_tracker.dart index ed51791ea9d..60afeee98f6 100644 --- a/packages/flutter/lib/src/rendering/mouse_tracker.dart +++ b/packages/flutter/lib/src/rendering/mouse_tracker.dart @@ -5,7 +5,6 @@ /// @docImport 'binding.dart'; library; -import 'dart:collection' show LinkedHashMap; import 'dart:ui'; import 'package:flutter/foundation.dart'; @@ -27,16 +26,13 @@ class _MouseState { _MouseState({required PointerEvent initialEvent}) : _latestEvent = initialEvent; // The list of annotations that contains this device. - // - // It uses [LinkedHashMap] to keep the insertion order. - LinkedHashMap get annotations => _annotations; - LinkedHashMap _annotations = - LinkedHashMap(); + Map get annotations => _annotations; + Map _annotations = {}; - LinkedHashMap replaceAnnotations( - LinkedHashMap value, + Map replaceAnnotations( + Map value, ) { - final LinkedHashMap previous = _annotations; + final Map previous = _annotations; _annotations = value; return previous; } @@ -93,12 +89,12 @@ class _MouseTrackerUpdateDetails with Diagnosticable { /// The annotations that the device is hovering before the update. /// /// It is never null. - final LinkedHashMap lastAnnotations; + final Map lastAnnotations; /// The annotations that the device is hovering after the update. /// /// It is never null. - final LinkedHashMap nextAnnotations; + final Map nextAnnotations; /// The last event that the device observed before the update. /// @@ -232,11 +228,8 @@ class MouseTracker extends ChangeNotifier { lastEvent.position != event.position; } - LinkedHashMap _hitTestInViewResultToAnnotations( - HitTestResult result, - ) { - final LinkedHashMap annotations = - LinkedHashMap(); + Map _hitTestInViewResultToAnnotations(HitTestResult result) { + final Map annotations = {}; for (final HitTestEntry entry in result.path) { final Object target = entry.target; if (target is MouseTrackerAnnotation) { @@ -251,12 +244,12 @@ class MouseTracker extends ChangeNotifier { // // If the device is not connected or not a mouse, an empty map is returned // without calling `hitTest`. - LinkedHashMap _findAnnotations(_MouseState state) { + Map _findAnnotations(_MouseState state) { final Offset globalPosition = state.latestEvent.position; final int device = state.device; final int viewId = state.latestEvent.viewId; if (!_mouseStates.containsKey(device)) { - return LinkedHashMap(); + return {}; } return _hitTestInViewResultToAnnotations(_hitTestInView(globalPosition, viewId)); @@ -341,12 +334,13 @@ class MouseTracker extends ChangeNotifier { final _MouseState targetState = _mouseStates[device] ?? existingState!; final PointerEvent lastEvent = targetState.replaceLatestEvent(event); - final LinkedHashMap nextAnnotations = + final Map nextAnnotations = event is PointerRemovedEvent - ? LinkedHashMap() + ? {} : _hitTestInViewResultToAnnotations(result); - final LinkedHashMap lastAnnotations = targetState - .replaceAnnotations(nextAnnotations); + final Map lastAnnotations = targetState.replaceAnnotations( + nextAnnotations, + ); _handleDeviceUpdate( _MouseTrackerUpdateDetails.byPointerEvent( @@ -374,11 +368,10 @@ class MouseTracker extends ChangeNotifier { _deviceUpdatePhase(() { for (final _MouseState dirtyState in _mouseStates.values) { final PointerEvent lastEvent = dirtyState.latestEvent; - final LinkedHashMap nextAnnotations = _findAnnotations( - dirtyState, + final Map nextAnnotations = _findAnnotations(dirtyState); + final Map lastAnnotations = dirtyState.replaceAnnotations( + nextAnnotations, ); - final LinkedHashMap lastAnnotations = dirtyState - .replaceAnnotations(nextAnnotations); _handleDeviceUpdate( _MouseTrackerUpdateDetails.byNewFrame( @@ -407,8 +400,8 @@ class MouseTracker extends ChangeNotifier { static void _handleDeviceUpdateMouseEvents(_MouseTrackerUpdateDetails details) { final PointerEvent latestEvent = details.latestEvent; - final LinkedHashMap lastAnnotations = details.lastAnnotations; - final LinkedHashMap nextAnnotations = details.nextAnnotations; + final Map lastAnnotations = details.lastAnnotations; + final Map nextAnnotations = details.nextAnnotations; // Order is important for mouse event callbacks. The // `_hitTestInViewResultToAnnotations` returns annotations in the visual order diff --git a/packages/flutter/lib/src/rendering/paragraph.dart b/packages/flutter/lib/src/rendering/paragraph.dart index 3f5cea89e5a..5bb7e053e7f 100644 --- a/packages/flutter/lib/src/rendering/paragraph.dart +++ b/packages/flutter/lib/src/rendering/paragraph.dart @@ -7,7 +7,6 @@ /// @docImport 'editable.dart'; library; -import 'dart:collection'; import 'dart:math' as math; import 'dart:ui' as ui @@ -1268,7 +1267,7 @@ class RenderParagraph extends RenderBox // can be re-used when [assembleSemanticsNode] is called again. This ensures // stable ids for the [SemanticsNode]s of [TextSpan]s across // [assembleSemanticsNode] invocations. - LinkedHashMap? _cachedChildNodes; + Map? _cachedChildNodes; @override void assembleSemanticsNode( @@ -1285,7 +1284,7 @@ class RenderParagraph extends RenderBox int placeholderIndex = 0; int childIndex = 0; RenderBox? child = firstChild; - final LinkedHashMap newChildCache = LinkedHashMap(); + final Map newChildCache = {}; _cachedCombinedSemanticsInfos ??= combineSemanticsInfo(_semanticsInfo!); for (final InlineSpanSemanticsInformation info in _cachedCombinedSemanticsInfos!) { final TextSelection selection = TextSelection(