Remove the fast reassemble / single widget reload feature (#132255)
Fixes https://github.com/flutter/flutter/issues/132157
This commit is contained in:
parent
6cf5dbe371
commit
a2e2574941
@ -168,13 +168,6 @@ abstract class BindingBase {
|
||||
static Type? _debugInitializedType;
|
||||
static bool _debugServiceExtensionsRegistered = false;
|
||||
|
||||
/// Additional configuration used by the framework during hot reload.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [DebugReassembleConfig], which describes the configuration.
|
||||
static DebugReassembleConfig? debugReassembleConfig;
|
||||
|
||||
/// Deprecated. Will be removed in a future version of Flutter.
|
||||
///
|
||||
/// This property has been deprecated to prepare for Flutter's upcoming
|
||||
@ -989,23 +982,3 @@ abstract class BindingBase {
|
||||
Future<void> _exitApplication() async {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/// Additional configuration used for hot reload reassemble optimizations.
|
||||
///
|
||||
/// Do not extend, implement, or mixin this class. This may only be instantiated
|
||||
/// in debug mode.
|
||||
class DebugReassembleConfig {
|
||||
/// Create a new [DebugReassembleConfig].
|
||||
///
|
||||
/// Throws a [FlutterError] if this is called in profile or release mode.
|
||||
DebugReassembleConfig({
|
||||
this.widgetName,
|
||||
}) {
|
||||
if (!kDebugMode) {
|
||||
throw FlutterError('Cannot instantiate DebugReassembleConfig in profile or release mode.');
|
||||
}
|
||||
}
|
||||
|
||||
/// The name of the widget that was modified, or `null` if the change was elsewhere.
|
||||
final String? widgetName;
|
||||
}
|
||||
|
@ -603,18 +603,16 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
|
||||
@override
|
||||
Future<void> performReassemble() async {
|
||||
await super.performReassemble();
|
||||
if (BindingBase.debugReassembleConfig?.widgetName == null) {
|
||||
if (!kReleaseMode) {
|
||||
FlutterTimeline.startSync('Preparing Hot Reload (layout)');
|
||||
if (!kReleaseMode) {
|
||||
FlutterTimeline.startSync('Preparing Hot Reload (layout)');
|
||||
}
|
||||
try {
|
||||
for (final RenderView renderView in renderViews) {
|
||||
renderView.reassemble();
|
||||
}
|
||||
try {
|
||||
for (final RenderView renderView in renderViews) {
|
||||
renderView.reassemble();
|
||||
}
|
||||
} finally {
|
||||
if (!kReleaseMode) {
|
||||
FlutterTimeline.finishSync();
|
||||
}
|
||||
} finally {
|
||||
if (!kReleaseMode) {
|
||||
FlutterTimeline.finishSync();
|
||||
}
|
||||
}
|
||||
scheduleWarmUpFrame();
|
||||
|
@ -505,23 +505,6 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
|
||||
},
|
||||
);
|
||||
|
||||
registerServiceExtension(
|
||||
name: WidgetsServiceExtensions.fastReassemble.name,
|
||||
callback: (Map<String, Object> params) async {
|
||||
// This mirrors the implementation of the 'reassemble' callback registration
|
||||
// in lib/src/foundation/binding.dart, but with the extra binding config used
|
||||
// to skip some reassemble work.
|
||||
final String? className = params['className'] as String?;
|
||||
BindingBase.debugReassembleConfig = DebugReassembleConfig(widgetName: className);
|
||||
try {
|
||||
await reassembleApplication();
|
||||
} finally {
|
||||
BindingBase.debugReassembleConfig = null;
|
||||
}
|
||||
return <String, String>{'type': 'Success'};
|
||||
},
|
||||
);
|
||||
|
||||
// Expose the ability to send Widget rebuilds as [Timeline] events.
|
||||
registerBoolServiceExtension(
|
||||
name: WidgetsServiceExtensions.profileWidgetBuilds.name,
|
||||
@ -560,7 +543,7 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
|
||||
|
||||
Future<void> _forceRebuild() {
|
||||
if (rootElement != null) {
|
||||
buildOwner!.reassemble(rootElement!, null);
|
||||
buildOwner!.reassemble(rootElement!);
|
||||
return endOfFrame;
|
||||
}
|
||||
return Future<void>.value();
|
||||
@ -1090,7 +1073,7 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
|
||||
}());
|
||||
|
||||
if (rootElement != null) {
|
||||
buildOwner!.reassemble(rootElement!, BindingBase.debugReassembleConfig);
|
||||
buildOwner!.reassemble(rootElement!);
|
||||
}
|
||||
return super.performReassemble();
|
||||
}
|
||||
|
@ -3252,14 +3252,13 @@ class BuildOwner {
|
||||
/// changed implementations.
|
||||
///
|
||||
/// This is expensive and should not be called except during development.
|
||||
void reassemble(Element root, DebugReassembleConfig? reassembleConfig) {
|
||||
void reassemble(Element root) {
|
||||
if (!kReleaseMode) {
|
||||
FlutterTimeline.startSync('Preparing Hot Reload (widgets)');
|
||||
}
|
||||
try {
|
||||
assert(root._parent == null);
|
||||
assert(root.owner == this);
|
||||
root._debugReassembleConfig = reassembleConfig;
|
||||
root.reassemble();
|
||||
} finally {
|
||||
if (!kReleaseMode) {
|
||||
@ -3374,7 +3373,6 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
|
||||
}
|
||||
|
||||
Element? _parent;
|
||||
DebugReassembleConfig? _debugReassembleConfig;
|
||||
_NotificationNode? _notificationTree;
|
||||
|
||||
/// Compare two widgets for equality.
|
||||
@ -3526,15 +3524,10 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
|
||||
@mustCallSuper
|
||||
@protected
|
||||
void reassemble() {
|
||||
if (_debugShouldReassemble(_debugReassembleConfig, _widget)) {
|
||||
markNeedsBuild();
|
||||
_debugReassembleConfig = null;
|
||||
}
|
||||
markNeedsBuild();
|
||||
visitChildren((Element child) {
|
||||
child._debugReassembleConfig = _debugReassembleConfig;
|
||||
child.reassemble();
|
||||
});
|
||||
_debugReassembleConfig = null;
|
||||
}
|
||||
|
||||
bool _debugIsInScope(Element target) {
|
||||
@ -5585,9 +5578,7 @@ class StatefulElement extends ComponentElement {
|
||||
|
||||
@override
|
||||
void reassemble() {
|
||||
if (_debugShouldReassemble(_debugReassembleConfig, _widget)) {
|
||||
state.reassemble();
|
||||
}
|
||||
state.reassemble();
|
||||
super.reassemble();
|
||||
}
|
||||
|
||||
@ -6952,9 +6943,3 @@ class _NullWidget extends Widget {
|
||||
@override
|
||||
Element createElement() => throw UnimplementedError();
|
||||
}
|
||||
|
||||
// Whether a [DebugReassembleConfig] indicates that an element holding [widget] can skip
|
||||
// a reassemble.
|
||||
bool _debugShouldReassemble(DebugReassembleConfig? config, Widget? widget) {
|
||||
return config == null || config.widgetName == null || widget?.runtimeType.toString() == config.widgetName;
|
||||
}
|
||||
|
@ -970,7 +970,7 @@ mixin WidgetInspectorService {
|
||||
Future<void> forceRebuild() {
|
||||
final WidgetsBinding binding = WidgetsBinding.instance;
|
||||
if (binding.rootElement != null) {
|
||||
binding.buildOwner!.reassemble(binding.rootElement!, null);
|
||||
binding.buildOwner!.reassemble(binding.rootElement!);
|
||||
return binding.endOfFrame;
|
||||
}
|
||||
return Future<void>.value();
|
||||
|
@ -116,6 +116,7 @@ Future<Map<String, dynamic>> hasReassemble(Future<Map<String, dynamic>> pendingR
|
||||
}
|
||||
|
||||
void main() {
|
||||
final Set<String> testedExtensions = <String>{}; // Add the name of an extension to this set in the test where it is tested.
|
||||
final List<String?> console = <String?>[];
|
||||
late PipelineOwner owner;
|
||||
|
||||
@ -153,6 +154,9 @@ void main() {
|
||||
|
||||
expect(binding.frameScheduled, isFalse);
|
||||
|
||||
testedExtensions.add(WidgetsServiceExtensions.didSendFirstFrameEvent.name);
|
||||
testedExtensions.add(WidgetsServiceExtensions.didSendFirstFrameRasterizedEvent.name);
|
||||
|
||||
expect(debugPrint, equals(debugPrintThrottled));
|
||||
debugPrint = (String? message, { int? wrapWidth }) {
|
||||
console.add(message);
|
||||
@ -162,12 +166,13 @@ void main() {
|
||||
tearDownAll(() async {
|
||||
// See widget_inspector_test.dart for tests of the ext.flutter.inspector
|
||||
// service extensions included in this count.
|
||||
int widgetInspectorExtensionCount = 20;
|
||||
int widgetInspectorExtensionCount = 28;
|
||||
if (WidgetInspectorService.instance.isWidgetCreationTracked()) {
|
||||
// Some inspector extensions are only exposed if widget creation locations
|
||||
// are tracked.
|
||||
widgetInspectorExtensionCount += 2;
|
||||
}
|
||||
expect(binding.extensions.keys.where((String name) => name.startsWith('inspector.')), hasLength(widgetInspectorExtensionCount));
|
||||
|
||||
// The following service extensions are disabled in web:
|
||||
// 1. exit
|
||||
@ -175,12 +180,13 @@ void main() {
|
||||
const int disabledExtensions = kIsWeb ? 2 : 0;
|
||||
|
||||
// The expected number of registered service extensions in the Flutter
|
||||
// framework, excluding any that are for the widget inspector
|
||||
// (see widget_inspector_test.dart for tests of the ext.flutter.inspector
|
||||
// service extensions).
|
||||
const int serviceExtensionCount = 38;
|
||||
// framework, excluding any that are for the widget inspector (see
|
||||
// widget_inspector_test.dart for tests of the ext.flutter.inspector service
|
||||
// extensions). Any test counted here must be tested in this file!
|
||||
const int serviceExtensionCount = 29;
|
||||
|
||||
expect(binding.extensions.length, serviceExtensionCount + widgetInspectorExtensionCount - disabledExtensions);
|
||||
expect(testedExtensions, hasLength(serviceExtensionCount));
|
||||
|
||||
expect(console, isEmpty);
|
||||
debugPrint = debugPrintThrottled;
|
||||
@ -213,6 +219,8 @@ void main() {
|
||||
expect(result, <String, String>{'enabled': 'true'});
|
||||
expect(WidgetsApp.debugAllowBannerOverride, true);
|
||||
expect(binding.frameScheduled, isFalse);
|
||||
|
||||
testedExtensions.add(WidgetsServiceExtensions.debugAllowBanner.name);
|
||||
});
|
||||
|
||||
test('Service extensions - debugDumpApp', () async {
|
||||
@ -221,6 +229,8 @@ void main() {
|
||||
expect(result, <String, dynamic>{
|
||||
'data': matches('TestServiceExtensionsBinding - DEBUG MODE\n<no tree currently mounted>'),
|
||||
});
|
||||
|
||||
testedExtensions.add(WidgetsServiceExtensions.debugDumpApp.name);
|
||||
});
|
||||
|
||||
test('Service extensions - debugDumpFocusTree', () async {
|
||||
@ -234,6 +244,8 @@ void main() {
|
||||
r'$',
|
||||
),
|
||||
});
|
||||
|
||||
testedExtensions.add(WidgetsServiceExtensions.debugDumpFocusTree.name);
|
||||
});
|
||||
|
||||
test('Service extensions - debugDumpRenderTree', () async {
|
||||
@ -251,6 +263,8 @@ void main() {
|
||||
r'$',
|
||||
),
|
||||
});
|
||||
|
||||
testedExtensions.add(RenderingServiceExtensions.debugDumpRenderTree.name);
|
||||
});
|
||||
|
||||
test('Service extensions - debugDumpLayerTree', () async {
|
||||
@ -274,6 +288,8 @@ void main() {
|
||||
r'$',
|
||||
),
|
||||
});
|
||||
|
||||
testedExtensions.add(RenderingServiceExtensions.debugDumpLayerTree.name);
|
||||
});
|
||||
|
||||
test('Service extensions - debugDumpSemanticsTreeInTraversalOrder', () async {
|
||||
@ -288,6 +304,8 @@ void main() {
|
||||
r'To generate semantics, try turning on an assistive technology \(like VoiceOver or TalkBack\) on your device.'
|
||||
)
|
||||
});
|
||||
|
||||
testedExtensions.add(RenderingServiceExtensions.debugDumpSemanticsTreeInTraversalOrder.name);
|
||||
});
|
||||
|
||||
test('Service extensions - debugDumpSemanticsTreeInInverseHitTestOrder', () async {
|
||||
@ -302,10 +320,12 @@ void main() {
|
||||
r'To generate semantics, try turning on an assistive technology \(like VoiceOver or TalkBack\) on your device.'
|
||||
)
|
||||
});
|
||||
|
||||
testedExtensions.add(RenderingServiceExtensions.debugDumpSemanticsTreeInInverseHitTestOrder.name);
|
||||
});
|
||||
|
||||
test('Service extensions - debugPaint', () async {
|
||||
final Iterable<Map<String, dynamic>> extensionChangedEvents = binding.getServiceExtensionStateChangedEvents('ext.flutter.debugPaint');
|
||||
final Iterable<Map<String, dynamic>> extensionChangedEvents = binding.getServiceExtensionStateChangedEvents('ext.flutter.${RenderingServiceExtensions.debugPaint.name}');
|
||||
Map<String, dynamic> extensionChangedEvent;
|
||||
Map<String, dynamic> result;
|
||||
Future<Map<String, dynamic>> pendingResult;
|
||||
@ -357,6 +377,8 @@ void main() {
|
||||
expect(debugPaintSizeEnabled, false);
|
||||
expect(extensionChangedEvents.length, 2);
|
||||
expect(binding.frameScheduled, isFalse);
|
||||
|
||||
testedExtensions.add(RenderingServiceExtensions.debugPaint.name);
|
||||
});
|
||||
|
||||
test('Service extensions - debugPaintBaselinesEnabled', () async {
|
||||
@ -399,6 +421,8 @@ void main() {
|
||||
expect(result, <String, String>{'enabled': 'false'});
|
||||
expect(debugPaintBaselinesEnabled, false);
|
||||
expect(binding.frameScheduled, isFalse);
|
||||
|
||||
testedExtensions.add(RenderingServiceExtensions.debugPaintBaselinesEnabled.name);
|
||||
});
|
||||
|
||||
test('Service extensions - invertOversizedImages', () async {
|
||||
@ -445,6 +469,8 @@ void main() {
|
||||
expect(result, <String, String>{'enabled': 'false'});
|
||||
expect(debugInvertOversizedImages, false);
|
||||
expect(binding.frameScheduled, isFalse);
|
||||
|
||||
testedExtensions.add(RenderingServiceExtensions.invertOversizedImages.name);
|
||||
});
|
||||
|
||||
test('Service extensions - profileWidgetBuilds', () async {
|
||||
@ -474,6 +500,8 @@ void main() {
|
||||
expect(debugProfileBuildsEnabled, false);
|
||||
|
||||
expect(binding.frameScheduled, isFalse);
|
||||
|
||||
testedExtensions.add(WidgetsServiceExtensions.profileWidgetBuilds.name);
|
||||
});
|
||||
|
||||
test('Service extensions - profileUserWidgetBuilds', () async {
|
||||
@ -503,6 +531,8 @@ void main() {
|
||||
expect(debugProfileBuildsEnabledUserWidgets, false);
|
||||
|
||||
expect(binding.frameScheduled, isFalse);
|
||||
|
||||
testedExtensions.add(WidgetsServiceExtensions.profileUserWidgetBuilds.name);
|
||||
});
|
||||
|
||||
test('Service extensions - profileRenderObjectPaints', () async {
|
||||
@ -532,6 +562,8 @@ void main() {
|
||||
expect(debugProfilePaintsEnabled, false);
|
||||
|
||||
expect(binding.frameScheduled, isFalse);
|
||||
|
||||
testedExtensions.add(RenderingServiceExtensions.profileRenderObjectPaints.name);
|
||||
});
|
||||
|
||||
test('Service extensions - profileRenderObjectLayouts', () async {
|
||||
@ -561,6 +593,8 @@ void main() {
|
||||
expect(debugProfileLayoutsEnabled, false);
|
||||
|
||||
expect(binding.frameScheduled, isFalse);
|
||||
|
||||
testedExtensions.add(RenderingServiceExtensions.profileRenderObjectLayouts.name);
|
||||
});
|
||||
|
||||
test('Service extensions - evict', () async {
|
||||
@ -596,12 +630,16 @@ void main() {
|
||||
expect(data, isFalse);
|
||||
expect(completed, isTrue);
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMessageHandler('flutter/assets', null);
|
||||
|
||||
testedExtensions.add(ServicesServiceExtensions.evict.name);
|
||||
});
|
||||
|
||||
test('Service extensions - exit', () async {
|
||||
// no test for _calling_ 'exit', because that should terminate the process!
|
||||
// Not expecting extension to be available for web platform.
|
||||
expect(binding.extensions.containsKey(FoundationServiceExtensions.exit.name), !isBrowser);
|
||||
|
||||
testedExtensions.add(FoundationServiceExtensions.exit.name);
|
||||
});
|
||||
|
||||
test('Service extensions - platformOverride', () async {
|
||||
@ -688,6 +726,8 @@ void main() {
|
||||
expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
|
||||
expect(extensionChangedEvent['value'], 'android');
|
||||
binding.reassembled = 0;
|
||||
|
||||
testedExtensions.add(FoundationServiceExtensions.platformOverride.name);
|
||||
});
|
||||
|
||||
test('Service extensions - repaintRainbow', () async {
|
||||
@ -731,6 +771,8 @@ void main() {
|
||||
expect(result, <String, String>{'enabled': 'false'});
|
||||
expect(debugRepaintRainbowEnabled, false);
|
||||
expect(binding.frameScheduled, isFalse);
|
||||
|
||||
testedExtensions.add(RenderingServiceExtensions.repaintRainbow.name);
|
||||
});
|
||||
|
||||
test('Service extensions - debugDisableClipLayers', () async {
|
||||
@ -773,6 +815,8 @@ void main() {
|
||||
expect(result, <String, String>{'enabled': 'false'});
|
||||
expect(debugDisableClipLayers, false);
|
||||
expect(binding.frameScheduled, isFalse);
|
||||
|
||||
testedExtensions.add(RenderingServiceExtensions.debugDisableClipLayers.name);
|
||||
});
|
||||
|
||||
test('Service extensions - debugDisablePhysicalShapeLayers', () async {
|
||||
@ -815,6 +859,8 @@ void main() {
|
||||
expect(result, <String, String>{'enabled': 'false'});
|
||||
expect(debugDisablePhysicalShapeLayers, false);
|
||||
expect(binding.frameScheduled, isFalse);
|
||||
|
||||
testedExtensions.add(RenderingServiceExtensions.debugDisablePhysicalShapeLayers.name);
|
||||
});
|
||||
|
||||
test('Service extensions - debugDisableOpacityLayers', () async {
|
||||
@ -857,6 +903,8 @@ void main() {
|
||||
expect(result, <String, String>{'enabled': 'false'});
|
||||
expect(debugDisableOpacityLayers, false);
|
||||
expect(binding.frameScheduled, isFalse);
|
||||
|
||||
testedExtensions.add(RenderingServiceExtensions.debugDisableOpacityLayers.name);
|
||||
});
|
||||
|
||||
test('Service extensions - reassemble', () async {
|
||||
@ -880,6 +928,8 @@ void main() {
|
||||
expect(result, <String, String>{});
|
||||
expect(binding.reassembled, 1);
|
||||
binding.reassembled = 0;
|
||||
|
||||
testedExtensions.add(FoundationServiceExtensions.reassemble.name);
|
||||
});
|
||||
|
||||
test('Service extensions - showPerformanceOverlay', () async {
|
||||
@ -888,6 +938,7 @@ void main() {
|
||||
// The performance overlay service extension is disabled on the web.
|
||||
if (kIsWeb) {
|
||||
expect(binding.extensions.containsKey(WidgetsServiceExtensions.showPerformanceOverlay.name), isFalse);
|
||||
testedExtensions.add(WidgetsServiceExtensions.showPerformanceOverlay.name);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -909,6 +960,8 @@ void main() {
|
||||
expect(result, <String, String>{'enabled': 'false'});
|
||||
expect(WidgetsApp.showPerformanceOverlayOverride, false);
|
||||
expect(binding.frameScheduled, isFalse);
|
||||
|
||||
testedExtensions.add(WidgetsServiceExtensions.showPerformanceOverlay.name);
|
||||
});
|
||||
|
||||
test('Service extensions - timeDilation', () async {
|
||||
@ -945,6 +998,8 @@ void main() {
|
||||
expect(timeDilation, 1.0);
|
||||
expect(extensionChangedEvents.length, 2);
|
||||
expect(binding.frameScheduled, isFalse);
|
||||
|
||||
testedExtensions.add(SchedulerServiceExtensions.timeDilation.name);
|
||||
});
|
||||
|
||||
test('Service extensions - brightnessOverride', () async {
|
||||
@ -953,6 +1008,8 @@ void main() {
|
||||
final String brightnessValue = result['value'] as String;
|
||||
|
||||
expect(brightnessValue, 'Brightness.light');
|
||||
|
||||
testedExtensions.add(FoundationServiceExtensions.brightnessOverride.name);
|
||||
});
|
||||
|
||||
test('Service extensions - activeDevToolsServerAddress', () async {
|
||||
@ -966,6 +1023,8 @@ void main() {
|
||||
result = await binding.testExtension(FoundationServiceExtensions.activeDevToolsServerAddress.name, <String, String>{'value': 'http://127.0.0.1:9102'});
|
||||
serverAddress = result['value'] as String;
|
||||
expect(serverAddress, 'http://127.0.0.1:9102');
|
||||
|
||||
testedExtensions.add(FoundationServiceExtensions.activeDevToolsServerAddress.name);
|
||||
});
|
||||
|
||||
test('Service extensions - connectedVmServiceUri', () async {
|
||||
@ -979,5 +1038,7 @@ void main() {
|
||||
result = await binding.testExtension(FoundationServiceExtensions.connectedVmServiceUri.name, <String, String>{'value': 'http://127.0.0.1:54000/kMUMseKAnog=/'});
|
||||
serverAddress = result['value'] as String;
|
||||
expect(serverAddress, 'http://127.0.0.1:54000/kMUMseKAnog=/');
|
||||
|
||||
testedExtensions.add(FoundationServiceExtensions.connectedVmServiceUri.name);
|
||||
});
|
||||
}
|
||||
|
@ -1,123 +0,0 @@
|
||||
// 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/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('reassemble with a className only marks subtrees from the first matching element as dirty', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const Foo(Bar(Fizz(SizedBox())))
|
||||
);
|
||||
|
||||
expect(Foo.count, 0);
|
||||
expect(Bar.count, 0);
|
||||
expect(Fizz.count, 0);
|
||||
|
||||
DebugReassembleConfig config = DebugReassembleConfig(widgetName: 'Bar');
|
||||
WidgetsBinding.instance.buildOwner!.reassemble(WidgetsBinding.instance.rootElement!, config);
|
||||
|
||||
expect(Foo.count, 0);
|
||||
expect(Bar.count, 1);
|
||||
expect(Fizz.count, 1);
|
||||
|
||||
config = DebugReassembleConfig(widgetName: 'Fizz');
|
||||
WidgetsBinding.instance.buildOwner!.reassemble(WidgetsBinding.instance.rootElement!, config);
|
||||
|
||||
expect(Foo.count, 0);
|
||||
expect(Bar.count, 1);
|
||||
expect(Fizz.count, 2);
|
||||
|
||||
config = DebugReassembleConfig(widgetName: 'NoMatch');
|
||||
WidgetsBinding.instance.buildOwner!.reassemble(WidgetsBinding.instance.rootElement!, config);
|
||||
|
||||
expect(Foo.count, 0);
|
||||
expect(Bar.count, 1);
|
||||
expect(Fizz.count, 2);
|
||||
|
||||
config = DebugReassembleConfig();
|
||||
WidgetsBinding.instance.buildOwner!.reassemble(WidgetsBinding.instance.rootElement!, config);
|
||||
|
||||
expect(Foo.count, 1);
|
||||
expect(Bar.count, 2);
|
||||
expect(Fizz.count, 3);
|
||||
|
||||
WidgetsBinding.instance.buildOwner!.reassemble(WidgetsBinding.instance.rootElement!, null);
|
||||
|
||||
expect(Foo.count, 2);
|
||||
expect(Bar.count, 3);
|
||||
expect(Fizz.count, 4);
|
||||
});
|
||||
}
|
||||
|
||||
class Foo extends StatefulWidget {
|
||||
const Foo(this.child, {super.key});
|
||||
|
||||
final Widget child;
|
||||
static int count = 0;
|
||||
|
||||
@override
|
||||
State<Foo> createState() => _FooState();
|
||||
}
|
||||
|
||||
class _FooState extends State<Foo> {
|
||||
@override
|
||||
void reassemble() {
|
||||
Foo.count += 1;
|
||||
super.reassemble();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.child;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Bar extends StatefulWidget {
|
||||
const Bar(this.child, {super.key});
|
||||
|
||||
final Widget child;
|
||||
static int count = 0;
|
||||
|
||||
@override
|
||||
State<Bar> createState() => _BarState();
|
||||
}
|
||||
|
||||
class _BarState extends State<Bar> {
|
||||
@override
|
||||
void reassemble() {
|
||||
Bar.count += 1;
|
||||
super.reassemble();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.child;
|
||||
}
|
||||
}
|
||||
|
||||
class Fizz extends StatefulWidget {
|
||||
const Fizz(this.child, {super.key});
|
||||
|
||||
final Widget child;
|
||||
static int count = 0;
|
||||
|
||||
@override
|
||||
State<Fizz> createState() => _FizzState();
|
||||
}
|
||||
|
||||
class _FizzState extends State<Fizz> {
|
||||
@override
|
||||
void reassemble() {
|
||||
Fizz.count += 1;
|
||||
super.reassemble();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.child;
|
||||
}
|
||||
}
|
@ -115,7 +115,7 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService {
|
||||
final WidgetsBinding binding = WidgetsBinding.instance;
|
||||
|
||||
if (binding.rootElement != null) {
|
||||
binding.buildOwner!.reassemble(binding.rootElement!, null);
|
||||
binding.buildOwner!.reassemble(binding.rootElement!);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ String getDefaultCachedKernelPath({
|
||||
}) {
|
||||
final StringBuffer buffer = StringBuffer();
|
||||
final List<String> cacheFrontEndOptions = extraFrontEndOptions.toList()
|
||||
..removeWhere((String arg) => arg.startsWith('--enable-experiment=') || arg == '--flutter-widget-cache');
|
||||
..removeWhere((String arg) => arg.startsWith('--enable-experiment='));
|
||||
buffer.writeAll(dartDefines);
|
||||
buffer.writeAll(cacheFrontEndOptions);
|
||||
String buildPrefix = '';
|
||||
|
@ -401,7 +401,6 @@ class UpdateFSReport {
|
||||
bool success = false,
|
||||
int invalidatedSourcesCount = 0,
|
||||
int syncedBytes = 0,
|
||||
this.fastReassembleClassName,
|
||||
int scannedSourcesCount = 0,
|
||||
Duration compileDuration = Duration.zero,
|
||||
Duration transferDuration = Duration.zero,
|
||||
@ -423,7 +422,6 @@ class UpdateFSReport {
|
||||
Duration get findInvalidatedDuration => _findInvalidatedDuration;
|
||||
|
||||
bool _success;
|
||||
String? fastReassembleClassName;
|
||||
int _invalidatedSourcesCount;
|
||||
int _syncedBytes;
|
||||
int _scannedSourcesCount;
|
||||
@ -435,7 +433,6 @@ class UpdateFSReport {
|
||||
if (!report._success) {
|
||||
_success = false;
|
||||
}
|
||||
fastReassembleClassName ??= report.fastReassembleClassName;
|
||||
_invalidatedSourcesCount += report._invalidatedSourcesCount;
|
||||
_syncedBytes += report._syncedBytes;
|
||||
_scannedSourcesCount += report._scannedSourcesCount;
|
||||
@ -495,7 +492,6 @@ class DevFS {
|
||||
DateTime? lastCompiled;
|
||||
DateTime? _previousCompiled;
|
||||
PackageConfig? lastPackageConfig;
|
||||
File? _widgetCacheOutputFile;
|
||||
|
||||
Uri? _baseUri;
|
||||
Uri? get baseUri => _baseUri;
|
||||
@ -555,22 +551,6 @@ class DevFS {
|
||||
lastCompiled = _previousCompiled;
|
||||
}
|
||||
|
||||
|
||||
/// If the build method of a single widget was modified, return the widget name.
|
||||
///
|
||||
/// If any other changes were made, or there is an error scanning the file,
|
||||
/// return `null`.
|
||||
String? _checkIfSingleWidgetReloadApplied() {
|
||||
final File? widgetCacheOutputFile = _widgetCacheOutputFile;
|
||||
if (widgetCacheOutputFile != null && widgetCacheOutputFile.existsSync()) {
|
||||
final String widget = widgetCacheOutputFile.readAsStringSync().trim();
|
||||
if (widget.isNotEmpty) {
|
||||
return widget;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Updates files on the device.
|
||||
///
|
||||
/// Returns the number of bytes synced.
|
||||
@ -596,7 +576,6 @@ class DevFS {
|
||||
final DateTime candidateCompileTime = DateTime.now();
|
||||
didUpdateFontManifest = false;
|
||||
lastPackageConfig = packageConfig;
|
||||
_widgetCacheOutputFile = _fileSystem.file('$dillOutputPath.incremental.dill.widget_cache');
|
||||
|
||||
// Update modified files
|
||||
final Map<Uri, DevFSContent> dirtyEntries = <Uri, DevFSContent>{};
|
||||
@ -741,7 +720,6 @@ class DevFS {
|
||||
success: true,
|
||||
syncedBytes: syncedBytes,
|
||||
invalidatedSourcesCount: invalidatedFiles.length,
|
||||
fastReassembleClassName: _checkIfSingleWidgetReloadApplied(),
|
||||
compileDuration: compileTimer.elapsed,
|
||||
transferDuration: transferTimer.elapsed,
|
||||
);
|
||||
|
@ -44,9 +44,6 @@ abstract class FeatureFlags {
|
||||
/// Whether custom devices are enabled.
|
||||
bool get areCustomDevicesEnabled => false;
|
||||
|
||||
/// Whether fast single widget reloads are enabled.
|
||||
bool get isSingleWidgetReloadEnabled => false;
|
||||
|
||||
/// Whether WebAssembly compilation for Flutter Web is enabled.
|
||||
bool get isFlutterWebWasmEnabled => false;
|
||||
|
||||
@ -62,7 +59,6 @@ const List<Feature> allFeatures = <Feature>[
|
||||
flutterLinuxDesktopFeature,
|
||||
flutterMacOSDesktopFeature,
|
||||
flutterWindowsDesktopFeature,
|
||||
singleWidgetReload,
|
||||
flutterAndroidFeature,
|
||||
flutterIOSFeature,
|
||||
flutterFuchsiaFeature,
|
||||
@ -140,20 +136,6 @@ const Feature flutterCustomDevicesFeature = Feature(
|
||||
),
|
||||
);
|
||||
|
||||
/// The fast hot reload feature for https://github.com/flutter/flutter/issues/61407.
|
||||
const Feature singleWidgetReload = Feature(
|
||||
name: 'Hot reload optimization for changes to class body of a single widget',
|
||||
configSetting: 'single-widget-reload-optimization',
|
||||
environmentOverride: 'FLUTTER_SINGLE_WIDGET_RELOAD',
|
||||
master: FeatureChannelSetting(
|
||||
available: true,
|
||||
enabledByDefault: true,
|
||||
),
|
||||
beta: FeatureChannelSetting(
|
||||
available: true,
|
||||
),
|
||||
);
|
||||
|
||||
/// Enabling WebAssembly compilation from `flutter build web`
|
||||
const Feature flutterWebWasm = Feature(
|
||||
name: 'WebAssembly compilation from flutter build web',
|
||||
|
@ -44,9 +44,6 @@ class FlutterFeatureFlags implements FeatureFlags {
|
||||
@override
|
||||
bool get areCustomDevicesEnabled => isEnabled(flutterCustomDevicesFeature);
|
||||
|
||||
@override
|
||||
bool get isSingleWidgetReloadEnabled => isEnabled(singleWidgetReload);
|
||||
|
||||
@override
|
||||
bool get isFlutterWebWasmEnabled => isEnabled(flutterWebWasm);
|
||||
|
||||
|
@ -446,7 +446,6 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
|
||||
fullRestart: true,
|
||||
reason: reason,
|
||||
overallTimeInMs: elapsed.inMilliseconds,
|
||||
fastReassemble: false,
|
||||
).send();
|
||||
}
|
||||
return OperationResult.ok;
|
||||
|
@ -58,7 +58,6 @@ class CustomDimensions {
|
||||
this.commandRunAndroidEmbeddingVersion,
|
||||
this.commandPackagesAndroidEmbeddingVersion,
|
||||
this.nullSafety,
|
||||
this.fastReassemble,
|
||||
this.nullSafeMigratedLibraries,
|
||||
this.nullSafeTotalLibraries,
|
||||
this.hotEventCompileTimeInMs,
|
||||
@ -118,17 +117,17 @@ class CustomDimensions {
|
||||
final String? commandRunAndroidEmbeddingVersion; // cd45
|
||||
final String? commandPackagesAndroidEmbeddingVersion; // cd46
|
||||
final bool? nullSafety; // cd47
|
||||
final bool? fastReassemble; // cd48
|
||||
// cd48 was fastReassemble but that feature was removed
|
||||
final int? nullSafeMigratedLibraries; // cd49
|
||||
final int? nullSafeTotalLibraries; // cd50
|
||||
final int? hotEventCompileTimeInMs; // cd 51
|
||||
final int? hotEventFindInvalidatedTimeInMs; // cd 52
|
||||
final int? hotEventScannedSourcesCount; // cd 53
|
||||
final int? hotEventReassembleTimeInMs; // cd 54
|
||||
final int? hotEventReloadVMTimeInMs; // cd 55
|
||||
final bool? commandRunEnableImpeller; // cd 56
|
||||
final String? commandRunIOSInterfaceType; // cd 57
|
||||
final bool? commandRunIsTest; // cd 58
|
||||
final int? hotEventCompileTimeInMs; // cd51
|
||||
final int? hotEventFindInvalidatedTimeInMs; // cd52
|
||||
final int? hotEventScannedSourcesCount; // cd53
|
||||
final int? hotEventReassembleTimeInMs; // cd54
|
||||
final int? hotEventReloadVMTimeInMs; // cd55
|
||||
final bool? commandRunEnableImpeller; // cd56
|
||||
final String? commandRunIOSInterfaceType; // cd57
|
||||
final bool? commandRunIsTest; // cd58
|
||||
|
||||
/// Convert to a map that will be used to upload to the analytics backend.
|
||||
Map<String, String> toMap() => <String, String>{
|
||||
@ -179,7 +178,6 @@ class CustomDimensions {
|
||||
if (commandRunAndroidEmbeddingVersion != null) CustomDimensionsEnum.commandRunAndroidEmbeddingVersion.cdKey: commandRunAndroidEmbeddingVersion.toString(),
|
||||
if (commandPackagesAndroidEmbeddingVersion != null) CustomDimensionsEnum.commandPackagesAndroidEmbeddingVersion.cdKey: commandPackagesAndroidEmbeddingVersion.toString(),
|
||||
if (nullSafety != null) CustomDimensionsEnum.nullSafety.cdKey: nullSafety.toString(),
|
||||
if (fastReassemble != null) CustomDimensionsEnum.fastReassemble.cdKey: fastReassemble.toString(),
|
||||
if (nullSafeMigratedLibraries != null) CustomDimensionsEnum.nullSafeMigratedLibraries.cdKey: nullSafeMigratedLibraries.toString(),
|
||||
if (nullSafeTotalLibraries != null) CustomDimensionsEnum.nullSafeTotalLibraries.cdKey: nullSafeTotalLibraries.toString(),
|
||||
if (hotEventCompileTimeInMs != null) CustomDimensionsEnum.hotEventCompileTimeInMs.cdKey: hotEventCompileTimeInMs.toString(),
|
||||
@ -247,7 +245,6 @@ class CustomDimensions {
|
||||
commandRunAndroidEmbeddingVersion: other.commandRunAndroidEmbeddingVersion ?? commandRunAndroidEmbeddingVersion,
|
||||
commandPackagesAndroidEmbeddingVersion: other.commandPackagesAndroidEmbeddingVersion ?? commandPackagesAndroidEmbeddingVersion,
|
||||
nullSafety: other.nullSafety ?? nullSafety,
|
||||
fastReassemble: other.fastReassemble ?? fastReassemble,
|
||||
nullSafeMigratedLibraries: other.nullSafeMigratedLibraries ?? nullSafeMigratedLibraries,
|
||||
nullSafeTotalLibraries: other.nullSafeTotalLibraries ?? nullSafeTotalLibraries,
|
||||
hotEventCompileTimeInMs: other.hotEventCompileTimeInMs ?? hotEventCompileTimeInMs,
|
||||
@ -309,7 +306,6 @@ class CustomDimensions {
|
||||
commandRunAndroidEmbeddingVersion: _extractString(map, CustomDimensionsEnum.commandRunAndroidEmbeddingVersion),
|
||||
commandPackagesAndroidEmbeddingVersion: _extractString(map, CustomDimensionsEnum.commandPackagesAndroidEmbeddingVersion),
|
||||
nullSafety: _extractBool(map, CustomDimensionsEnum.nullSafety),
|
||||
fastReassemble: _extractBool(map, CustomDimensionsEnum.fastReassemble),
|
||||
nullSafeMigratedLibraries: _extractInt(map, CustomDimensionsEnum.nullSafeMigratedLibraries),
|
||||
nullSafeTotalLibraries: _extractInt(map, CustomDimensionsEnum.nullSafeTotalLibraries),
|
||||
hotEventCompileTimeInMs: _extractInt(map, CustomDimensionsEnum.hotEventCompileTimeInMs),
|
||||
@ -397,7 +393,7 @@ enum CustomDimensionsEnum {
|
||||
commandRunAndroidEmbeddingVersion, // cd45
|
||||
commandPackagesAndroidEmbeddingVersion, // cd46
|
||||
nullSafety, // cd47
|
||||
fastReassemble, // cd48
|
||||
obsolete1, // cd48 (was fastReassemble)
|
||||
nullSafeMigratedLibraries, // cd49
|
||||
nullSafeTotalLibraries, // cd50
|
||||
hotEventCompileTimeInMs, // cd51
|
||||
|
@ -39,7 +39,6 @@ class HotEvent extends UsageEvent {
|
||||
required this.sdkName,
|
||||
required this.emulator,
|
||||
required this.fullRestart,
|
||||
required this.fastReassemble,
|
||||
this.reason,
|
||||
this.finalLibraryCount,
|
||||
this.syncedLibraryCount,
|
||||
@ -63,7 +62,6 @@ class HotEvent extends UsageEvent {
|
||||
final String sdkName;
|
||||
final bool emulator;
|
||||
final bool fullRestart;
|
||||
final bool fastReassemble;
|
||||
final int? finalLibraryCount;
|
||||
final int? syncedLibraryCount;
|
||||
final int? syncedClassesCount;
|
||||
@ -94,7 +92,6 @@ class HotEvent extends UsageEvent {
|
||||
hotEventInvalidatedSourcesCount: invalidatedSourcesCount,
|
||||
hotEventTransferTimeInMs: transferTimeInMs,
|
||||
hotEventOverallTimeInMs: overallTimeInMs,
|
||||
fastReassemble: fastReassemble,
|
||||
hotEventCompileTimeInMs: compileTimeInMs,
|
||||
hotEventFindInvalidatedTimeInMs: findInvalidatedTimeInMs,
|
||||
hotEventScannedSourcesCount: scannedSourcesCount,
|
||||
|
@ -34,7 +34,6 @@ import 'compile.dart';
|
||||
import 'convert.dart';
|
||||
import 'devfs.dart';
|
||||
import 'device.dart';
|
||||
import 'features.dart';
|
||||
import 'globals.dart' as globals;
|
||||
import 'ios/application_package.dart';
|
||||
import 'ios/devices.dart';
|
||||
@ -169,11 +168,8 @@ class FlutterDevice {
|
||||
platform: platform,
|
||||
);
|
||||
} else {
|
||||
// The flutter-widget-cache feature only applies to run mode.
|
||||
List<String> extraFrontEndOptions = buildInfo.extraFrontEndOptions;
|
||||
extraFrontEndOptions = <String>[
|
||||
if (featureFlags.isSingleWidgetReloadEnabled)
|
||||
'--flutter-widget-cache',
|
||||
'--enable-experiment=alternative-invalidation-strategy',
|
||||
...extraFrontEndOptions,
|
||||
];
|
||||
|
@ -20,7 +20,6 @@ import 'convert.dart';
|
||||
import 'dart/package_map.dart';
|
||||
import 'devfs.dart';
|
||||
import 'device.dart';
|
||||
import 'features.dart';
|
||||
import 'globals.dart' as globals;
|
||||
import 'project.dart';
|
||||
import 'reporting/reporting.dart';
|
||||
@ -415,7 +414,6 @@ class HotRunner extends ResidentRunner {
|
||||
sdkName: _sdkName!,
|
||||
emulator: _emulator!,
|
||||
fullRestart: false,
|
||||
fastReassemble: false,
|
||||
overallTimeInMs: appStartedTimer.elapsed.inMilliseconds,
|
||||
compileTimeInMs: totalCompileTime.inMilliseconds,
|
||||
transferTimeInMs: totalLaunchAppTime.inMilliseconds,
|
||||
@ -802,7 +800,6 @@ class HotRunner extends ResidentRunner {
|
||||
emulator: emulator!,
|
||||
fullRestart: true,
|
||||
reason: reason,
|
||||
fastReassemble: false,
|
||||
overallTimeInMs: restartTimer.elapsed.inMilliseconds,
|
||||
syncedBytes: result.updateFSReport?.syncedBytes,
|
||||
invalidatedSourcesCount: result.updateFSReport?.invalidatedSourcesCount,
|
||||
@ -828,7 +825,6 @@ class HotRunner extends ResidentRunner {
|
||||
emulator: emulator!,
|
||||
fullRestart: true,
|
||||
reason: reason,
|
||||
fastReassemble: false,
|
||||
).send();
|
||||
}
|
||||
status?.cancel();
|
||||
@ -878,7 +874,6 @@ class HotRunner extends ResidentRunner {
|
||||
emulator: emulator!,
|
||||
fullRestart: false,
|
||||
reason: reason,
|
||||
fastReassemble: false,
|
||||
).send();
|
||||
} else {
|
||||
HotEvent('exception',
|
||||
@ -887,7 +882,6 @@ class HotRunner extends ResidentRunner {
|
||||
emulator: emulator!,
|
||||
fullRestart: false,
|
||||
reason: reason,
|
||||
fastReassemble: false,
|
||||
).send();
|
||||
}
|
||||
return OperationResult(errorCode, errorMessage, fatal: true);
|
||||
@ -971,7 +965,6 @@ class HotRunner extends ResidentRunner {
|
||||
viewCache,
|
||||
onSlow,
|
||||
reloadMessage,
|
||||
updatedDevFS.fastReassembleClassName,
|
||||
);
|
||||
shouldReportReloadTime = reassembleResult.shouldReportReloadTime;
|
||||
if (reassembleResult.reassembleViews.isEmpty) {
|
||||
@ -1005,7 +998,6 @@ class HotRunner extends ResidentRunner {
|
||||
syncedBytes: updatedDevFS.syncedBytes,
|
||||
invalidatedSourcesCount: updatedDevFS.invalidatedSourcesCount,
|
||||
transferTimeInMs: updatedDevFS.transferDuration.inMilliseconds,
|
||||
fastReassemble: featureFlags.isSingleWidgetReloadEnabled && updatedDevFS.fastReassembleClassName != null,
|
||||
compileTimeInMs: updatedDevFS.compileDuration.inMilliseconds,
|
||||
findInvalidatedTimeInMs: updatedDevFS.findInvalidatedDuration.inMilliseconds,
|
||||
scannedSourcesCount: updatedDevFS.scannedSourcesCount,
|
||||
@ -1225,7 +1217,6 @@ Future<OperationResult> defaultReloadSourcesHelper(
|
||||
emulator: emulator!,
|
||||
fullRestart: false,
|
||||
reason: reason,
|
||||
fastReassemble: false,
|
||||
usage: usage,
|
||||
).send();
|
||||
// Reset devFS lastCompileTime to ensure the file will still be marked
|
||||
@ -1288,7 +1279,6 @@ typedef ReassembleHelper = Future<ReassembleResult> Function(
|
||||
Map<FlutterDevice?, List<FlutterView>> viewCache,
|
||||
void Function(String message)? onSlow,
|
||||
String reloadMessage,
|
||||
String? fastReassembleClassName,
|
||||
);
|
||||
|
||||
Future<ReassembleResult> _defaultReassembleHelper(
|
||||
@ -1296,7 +1286,6 @@ Future<ReassembleResult> _defaultReassembleHelper(
|
||||
Map<FlutterDevice?, List<FlutterView>> viewCache,
|
||||
void Function(String message)? onSlow,
|
||||
String reloadMessage,
|
||||
String? fastReassembleClassName,
|
||||
) async {
|
||||
// Check if any isolates are paused and reassemble those that aren't.
|
||||
final Map<FlutterView, FlutterVmService?> reassembleViews = <FlutterView, FlutterVmService?>{};
|
||||
@ -1325,17 +1314,9 @@ Future<ReassembleResult> _defaultReassembleHelper(
|
||||
reassembleViews[view] = device.vmService;
|
||||
// If the tool identified a change in a single widget, do a fast instead
|
||||
// of a full reassemble.
|
||||
Future<void> reassembleWork;
|
||||
if (fastReassembleClassName != null) {
|
||||
reassembleWork = device.vmService!.flutterFastReassemble(
|
||||
isolateId: view.uiIsolate!.id!,
|
||||
className: fastReassembleClassName,
|
||||
);
|
||||
} else {
|
||||
reassembleWork = device.vmService!.flutterReassemble(
|
||||
isolateId: view.uiIsolate!.id!,
|
||||
);
|
||||
}
|
||||
final Future<void> reassembleWork = device.vmService!.flutterReassemble(
|
||||
isolateId: view.uiIsolate!.id!,
|
||||
);
|
||||
reassembleFutures.add(reassembleWork.then(
|
||||
(Object? obj) => obj,
|
||||
onError: (Object error, StackTrace stackTrace) {
|
||||
|
@ -809,19 +809,6 @@ class FlutterVmService {
|
||||
);
|
||||
}
|
||||
|
||||
Future<Map<String, Object?>?> flutterFastReassemble({
|
||||
required String isolateId,
|
||||
required String className,
|
||||
}) {
|
||||
return invokeFlutterExtensionRpcRaw(
|
||||
'ext.flutter.fastReassemble',
|
||||
isolateId: isolateId,
|
||||
args: <String, Object>{
|
||||
'className': className,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> flutterAlreadyPaintedFirstUsefulFrame({
|
||||
required String isolateId,
|
||||
}) async {
|
||||
|
@ -118,14 +118,14 @@ void main() {
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testWithoutContext('--flutter-widget-cache and --enable-experiment are removed from getDefaultCachedKernelPath hash', () {
|
||||
testWithoutContext('--enable-experiment is removed from getDefaultCachedKernelPath hash', () {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
final Config config = Config.test();
|
||||
|
||||
expect(getDefaultCachedKernelPath(
|
||||
trackWidgetCreation: true,
|
||||
dartDefines: <String>[],
|
||||
extraFrontEndOptions: <String>['--enable-experiment=foo', '--flutter-widget-cache'],
|
||||
extraFrontEndOptions: <String>['--enable-experiment=foo'],
|
||||
fileSystem: fileSystem,
|
||||
config: config,
|
||||
), 'build/cache.dill.track.dill');
|
||||
@ -133,7 +133,7 @@ void main() {
|
||||
expect(getDefaultCachedKernelPath(
|
||||
trackWidgetCreation: true,
|
||||
dartDefines: <String>['foo=bar'],
|
||||
extraFrontEndOptions: <String>['--enable-experiment=foo', '--flutter-widget-cache'],
|
||||
extraFrontEndOptions: <String>['--enable-experiment=foo'],
|
||||
fileSystem: fileSystem,
|
||||
config: config,
|
||||
), 'build/06ad47d8e64bd28de537b62ff85357c4.cache.dill.track.dill');
|
||||
@ -141,7 +141,7 @@ void main() {
|
||||
expect(getDefaultCachedKernelPath(
|
||||
trackWidgetCreation: false,
|
||||
dartDefines: <String>[],
|
||||
extraFrontEndOptions: <String>['--enable-experiment=foo', '--flutter-widget-cache'],
|
||||
extraFrontEndOptions: <String>['--enable-experiment=foo'],
|
||||
fileSystem: fileSystem,
|
||||
config: config,
|
||||
), 'build/cache.dill');
|
||||
@ -149,7 +149,7 @@ void main() {
|
||||
expect(getDefaultCachedKernelPath(
|
||||
trackWidgetCreation: true,
|
||||
dartDefines: <String>[],
|
||||
extraFrontEndOptions: <String>['--enable-experiment=foo', '--flutter-widget-cache', '--foo=bar'],
|
||||
extraFrontEndOptions: <String>['--enable-experiment=foo', '--foo=bar'],
|
||||
fileSystem: fileSystem,
|
||||
config: config,
|
||||
), 'build/95b595cca01caa5f0ca0a690339dd7f6.cache.dill.track.dill');
|
||||
|
@ -178,7 +178,6 @@ void main() {
|
||||
Map<FlutterDevice?, List<FlutterView>> viewCache,
|
||||
void Function(String message)? onSlow,
|
||||
String reloadMessage,
|
||||
String? fastReassembleClassName,
|
||||
) async => ReassembleResult(
|
||||
<FlutterView?, FlutterVmService?>{null: null},
|
||||
false,
|
||||
@ -296,7 +295,6 @@ void main() {
|
||||
hotEventSdkName: 'Tester',
|
||||
hotEventEmulator: false,
|
||||
hotEventFullRestart: true,
|
||||
fastReassemble: false,
|
||||
hotEventOverallTimeInMs: 64000,
|
||||
hotEventSyncedBytes: 4,
|
||||
hotEventInvalidatedSourcesCount: 2,
|
||||
@ -379,7 +377,6 @@ void main() {
|
||||
Map<FlutterDevice?, List<FlutterView>> viewCache,
|
||||
void Function(String message)? onSlow,
|
||||
String reloadMessage,
|
||||
String? fastReassembleClassName,
|
||||
) async => ReassembleResult(
|
||||
<FlutterView?, FlutterVmService?>{null: null},
|
||||
false,
|
||||
@ -402,7 +399,6 @@ void main() {
|
||||
hotEventSdkName: 'Tester',
|
||||
hotEventEmulator: false,
|
||||
hotEventFullRestart: false,
|
||||
fastReassemble: false,
|
||||
hotEventCompileTimeInMs: 16000,
|
||||
hotEventFindInvalidatedTimeInMs: 64000,
|
||||
hotEventScannedSourcesCount: 16,
|
||||
|
@ -25,7 +25,6 @@ import 'package:flutter_tools/src/convert.dart';
|
||||
import 'package:flutter_tools/src/devfs.dart';
|
||||
import 'package:flutter_tools/src/device.dart';
|
||||
import 'package:flutter_tools/src/device_port_forwarder.dart';
|
||||
import 'package:flutter_tools/src/features.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
@ -424,7 +423,6 @@ void main() {
|
||||
hotEventSdkName: 'Android',
|
||||
hotEventEmulator: false,
|
||||
hotEventFullRestart: false,
|
||||
fastReassemble: false,
|
||||
)),
|
||||
));
|
||||
expect(fakeVmServiceHost?.hasRemainingExpectations, false);
|
||||
@ -480,7 +478,6 @@ void main() {
|
||||
hotEventSdkName: 'Android',
|
||||
hotEventEmulator: false,
|
||||
hotEventFullRestart: false,
|
||||
fastReassemble: false,
|
||||
)),
|
||||
));
|
||||
expect(fakeVmServiceHost?.hasRemainingExpectations, false);
|
||||
@ -527,7 +524,6 @@ void main() {
|
||||
hotEventSdkName: 'Android',
|
||||
hotEventEmulator: false,
|
||||
hotEventFullRestart: false,
|
||||
fastReassemble: false,
|
||||
)),
|
||||
));
|
||||
expect(fakeVmServiceHost?.hasRemainingExpectations, false);
|
||||
@ -766,96 +762,6 @@ void main() {
|
||||
Usage: () => TestUsage(),
|
||||
}));
|
||||
|
||||
testUsingContext('ResidentRunner can perform fast reassemble', () => testbed.run(() async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
listViews,
|
||||
FakeVmServiceRequest(
|
||||
method: 'getVM',
|
||||
jsonResponse: fakeVM.toJson(),
|
||||
),
|
||||
listViews,
|
||||
listViews,
|
||||
FakeVmServiceRequest(
|
||||
method: 'getVM',
|
||||
jsonResponse: fakeVM.toJson(),
|
||||
),
|
||||
const FakeVmServiceRequest(
|
||||
method: kReloadSourcesServiceName,
|
||||
args: <String, Object>{
|
||||
'isolateId': '1',
|
||||
'pause': false,
|
||||
'rootLibUri': 'main.dart.incremental.dill',
|
||||
},
|
||||
jsonResponse: <String, Object>{
|
||||
'type': 'ReloadReport',
|
||||
'success': true,
|
||||
'details': <String, Object>{
|
||||
'loadedLibraryCount': 1,
|
||||
},
|
||||
},
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
method: 'getIsolatePauseEvent',
|
||||
args: <String, Object>{
|
||||
'isolateId': '1',
|
||||
},
|
||||
jsonResponse: fakeUnpausedEvent.toJson(),
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
method: 'ext.flutter.fastReassemble',
|
||||
args: <String, Object?>{
|
||||
'isolateId': fakeUnpausedIsolate.id,
|
||||
'className': 'FOO',
|
||||
},
|
||||
),
|
||||
]);
|
||||
final FakeDelegateFlutterDevice flutterDevice = FakeDelegateFlutterDevice(
|
||||
device,
|
||||
BuildInfo.debug,
|
||||
FakeResidentCompiler(),
|
||||
devFS,
|
||||
)..vmService = fakeVmServiceHost!.vmService;
|
||||
residentRunner = HotRunner(
|
||||
<FlutterDevice>[
|
||||
flutterDevice,
|
||||
],
|
||||
stayResident: false,
|
||||
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
||||
target: 'main.dart',
|
||||
devtoolsHandler: createNoOpHandler,
|
||||
);
|
||||
devFS.nextUpdateReport = UpdateFSReport(
|
||||
success: true,
|
||||
fastReassembleClassName: 'FOO',
|
||||
invalidatedSourcesCount: 1,
|
||||
);
|
||||
|
||||
final Completer<DebugConnectionInfo> futureConnectionInfo = Completer<DebugConnectionInfo>.sync();
|
||||
final Completer<void> futureAppStart = Completer<void>.sync();
|
||||
unawaited(residentRunner.attach(
|
||||
appStartedCompleter: futureAppStart,
|
||||
connectionInfoCompleter: futureConnectionInfo,
|
||||
enableDevTools: true,
|
||||
));
|
||||
|
||||
await futureAppStart.future;
|
||||
final OperationResult result = await residentRunner.restart();
|
||||
|
||||
expect(result.fatal, false);
|
||||
expect(result.code, 0);
|
||||
|
||||
final TestUsageEvent event = (globals.flutterUsage as TestUsage).events.first;
|
||||
expect(event.category, 'hot');
|
||||
expect(event.parameter, 'reload');
|
||||
expect(event.parameters?.fastReassemble, true);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
Platform: () => FakePlatform(),
|
||||
ProjectFileInvalidator: () => FakeProjectFileInvalidator(),
|
||||
Usage: () => TestUsage(),
|
||||
FeatureFlags: () => TestFeatureFlags(isSingleWidgetReloadEnabled: true),
|
||||
}));
|
||||
|
||||
testUsingContext('ResidentRunner reports hot reload time details', () => testbed.run(() async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
listViews,
|
||||
@ -893,10 +799,9 @@ void main() {
|
||||
jsonResponse: fakeUnpausedEvent.toJson(),
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
method: 'ext.flutter.fastReassemble',
|
||||
method: 'ext.flutter.reassemble',
|
||||
args: <String, Object?>{
|
||||
'isolateId': fakeUnpausedIsolate.id,
|
||||
'className': 'FOO',
|
||||
},
|
||||
),
|
||||
]);
|
||||
@ -917,7 +822,6 @@ void main() {
|
||||
);
|
||||
devFS.nextUpdateReport = UpdateFSReport(
|
||||
success: true,
|
||||
fastReassembleClassName: 'FOO',
|
||||
invalidatedSourcesCount: 1,
|
||||
);
|
||||
|
||||
@ -942,7 +846,6 @@ void main() {
|
||||
Platform: () => FakePlatform(),
|
||||
ProjectFileInvalidator: () => FakeProjectFileInvalidator(),
|
||||
Usage: () => TestUsage(),
|
||||
FeatureFlags: () => TestFeatureFlags(isSingleWidgetReloadEnabled: true),
|
||||
}));
|
||||
|
||||
testUsingContext('ResidentRunner can send target platform to analytics from full restart', () => testbed.run(() async {
|
||||
@ -1225,7 +1128,6 @@ void main() {
|
||||
hotEventSdkName: 'Android',
|
||||
hotEventEmulator: false,
|
||||
hotEventFullRestart: true,
|
||||
fastReassemble: false,
|
||||
)),
|
||||
));
|
||||
expect(fakeVmServiceHost?.hasRemainingExpectations, false);
|
||||
@ -1984,31 +1886,7 @@ flutter:
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('FlutterDevice passes flutter-widget-cache flag when feature is enabled', () async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
||||
final FakeDevice device = FakeDevice();
|
||||
|
||||
final DefaultResidentCompiler? residentCompiler = (await FlutterDevice.create(
|
||||
device,
|
||||
buildInfo: const BuildInfo(
|
||||
BuildMode.debug,
|
||||
'',
|
||||
treeShakeIcons: false,
|
||||
extraFrontEndOptions: <String>[],
|
||||
),
|
||||
target: null, platform: FakePlatform(),
|
||||
)).generator as DefaultResidentCompiler?;
|
||||
|
||||
expect(residentCompiler!.extraFrontEndOptions,
|
||||
contains('--flutter-widget-cache'));
|
||||
}, overrides: <Type, Generator>{
|
||||
Artifacts: () => Artifacts.test(),
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
FeatureFlags: () => TestFeatureFlags(isSingleWidgetReloadEnabled: true),
|
||||
});
|
||||
|
||||
testUsingContext('FlutterDevice passes alternative-invalidation-strategy flag', () async {
|
||||
testUsingContext('FlutterDevice passes alternative-invalidation-strategy flag', () async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
||||
final FakeDevice device = FakeDevice();
|
||||
|
||||
@ -2032,7 +1910,7 @@ flutter:
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('FlutterDevice passes initializeFromDill parameter if specified', () async {
|
||||
testUsingContext('FlutterDevice passes initializeFromDill parameter if specified', () async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
||||
final FakeDevice device = FakeDevice();
|
||||
|
||||
|
@ -1,63 +0,0 @@
|
||||
// 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 'dart:async';
|
||||
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
|
||||
import '../src/common.dart';
|
||||
import 'test_data/single_widget_reload_project.dart';
|
||||
import 'test_driver.dart';
|
||||
import 'test_utils.dart';
|
||||
|
||||
void main() {
|
||||
late Directory tempDir;
|
||||
final SingleWidgetReloadProject project = SingleWidgetReloadProject();
|
||||
late FlutterRunTestDriver flutter;
|
||||
|
||||
setUp(() async {
|
||||
tempDir = createResolvedTempDirectorySync('hot_reload_test.');
|
||||
await project.setUpIn(tempDir);
|
||||
flutter = FlutterRunTestDriver(tempDir);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await flutter.stop();
|
||||
tryToDelete(tempDir);
|
||||
});
|
||||
|
||||
testWithoutContext('newly added code executes during hot reload with single widget reloads, but only invalidated widget', () async {
|
||||
final StringBuffer stdout = StringBuffer();
|
||||
final StreamSubscription<String> subscription = flutter.stdout.listen(stdout.writeln);
|
||||
await flutter.run(singleWidgetReloads: true);
|
||||
project.uncommentHotReloadPrint();
|
||||
try {
|
||||
await flutter.hotReload();
|
||||
expect(stdout.toString(), allOf(
|
||||
contains('(((TICK 1)))'),
|
||||
contains('(((((RELOAD WORKED)))))'),
|
||||
// Does not invalidate parent widget, so second tick is not output.
|
||||
isNot(contains('(((TICK 2)))'),
|
||||
)));
|
||||
} finally {
|
||||
await subscription.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
testWithoutContext('changes outside of the class body triggers a full reload', () async {
|
||||
final StringBuffer stdout = StringBuffer();
|
||||
final StreamSubscription<String> subscription = flutter.stdout.listen(stdout.writeln);
|
||||
await flutter.run(singleWidgetReloads: true);
|
||||
project.modifyFunction();
|
||||
try {
|
||||
await flutter.hotReload();
|
||||
expect(stdout.toString(), allOf(
|
||||
contains('(((TICK 1)))'),
|
||||
contains('(((TICK 2)))'),
|
||||
));
|
||||
} finally {
|
||||
await subscription.cancel();
|
||||
}
|
||||
});
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
// 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 '../test_utils.dart';
|
||||
import 'project.dart';
|
||||
|
||||
class SingleWidgetReloadProject extends Project {
|
||||
@override
|
||||
final String pubspec = '''
|
||||
name: test
|
||||
environment:
|
||||
sdk: '>=3.0.0-0 <4.0.0'
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
''';
|
||||
|
||||
@override
|
||||
final String main = r'''
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
final ByteData message = const StringCodec().encodeMessage('AppLifecycleState.resumed')!;
|
||||
await ServicesBinding.instance!.defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', message, (_) { });
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
int count = 1;
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// PARENT WIDGET
|
||||
|
||||
print('((((TICK $count))))');
|
||||
count += 1;
|
||||
|
||||
return MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
home: SecondWidget(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SecondWidget extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Do not remove the next line, it's uncommented by a test to verify that
|
||||
// hot reloading worked:
|
||||
// printHotReloadWorked();
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
|
||||
void printHotReloadWorked() {
|
||||
// The call to this function is uncommented by a test to verify that hot
|
||||
// reloading worked.
|
||||
print('(((((RELOAD WORKED)))))');
|
||||
}
|
||||
''';
|
||||
|
||||
Uri get parentWidgetUri => mainDart;
|
||||
int get parentWidgetLine => lineContaining(main, '// PARENT WIDGET');
|
||||
|
||||
void uncommentHotReloadPrint() {
|
||||
final String newMainContents = main.replaceAll(
|
||||
'// printHotReloadWorked();',
|
||||
'printHotReloadWorked();',
|
||||
);
|
||||
writeFile(
|
||||
fileSystem.path.join(dir.path, 'lib', 'main.dart'),
|
||||
newMainContents,
|
||||
writeFutureModifiedDate: true,
|
||||
);
|
||||
}
|
||||
|
||||
void modifyFunction() {
|
||||
final String newMainContents = main.replaceAll(
|
||||
'(((((RELOAD WORKED)))))',
|
||||
'(((((RELOAD WORKED 2)))))',
|
||||
);
|
||||
writeFile(
|
||||
fileSystem.path.join(dir.path, 'lib', 'main.dart'),
|
||||
newMainContents,
|
||||
writeFutureModifiedDate: true,
|
||||
);
|
||||
}
|
||||
}
|
@ -90,7 +90,6 @@ abstract class FlutterTestDriver {
|
||||
List<String> arguments, {
|
||||
String? script,
|
||||
bool withDebugger = false,
|
||||
bool singleWidgetReloads = false,
|
||||
}) async {
|
||||
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
|
||||
if (withDebugger) {
|
||||
@ -114,8 +113,6 @@ abstract class FlutterTestDriver {
|
||||
environment: <String, String>{
|
||||
'FLUTTER_TEST': 'true',
|
||||
'FLUTTER_WEB': 'true',
|
||||
if (singleWidgetReloads)
|
||||
'FLUTTER_SINGLE_WIDGET_RELOAD': 'true',
|
||||
},
|
||||
);
|
||||
|
||||
@ -511,7 +508,6 @@ class FlutterRunTestDriver extends FlutterTestDriver {
|
||||
bool chrome = false,
|
||||
bool expressionEvaluation = true,
|
||||
bool structuredErrors = false,
|
||||
bool singleWidgetReloads = false,
|
||||
bool serveObservatory = false,
|
||||
String? script,
|
||||
List<String>? additionalCommandArgs,
|
||||
@ -542,7 +538,6 @@ class FlutterRunTestDriver extends FlutterTestDriver {
|
||||
startPaused: startPaused,
|
||||
pauseOnExceptions: pauseOnExceptions,
|
||||
script: script,
|
||||
singleWidgetReloads: singleWidgetReloads,
|
||||
);
|
||||
}
|
||||
|
||||
@ -551,7 +546,6 @@ class FlutterRunTestDriver extends FlutterTestDriver {
|
||||
bool withDebugger = false,
|
||||
bool startPaused = false,
|
||||
bool pauseOnExceptions = false,
|
||||
bool singleWidgetReloads = false,
|
||||
bool serveObservatory = false,
|
||||
List<String>? additionalCommandArgs,
|
||||
}) async {
|
||||
@ -573,7 +567,6 @@ class FlutterRunTestDriver extends FlutterTestDriver {
|
||||
withDebugger: withDebugger,
|
||||
startPaused: startPaused,
|
||||
pauseOnExceptions: pauseOnExceptions,
|
||||
singleWidgetReloads: singleWidgetReloads,
|
||||
attachPort: port,
|
||||
);
|
||||
}
|
||||
@ -585,7 +578,6 @@ class FlutterRunTestDriver extends FlutterTestDriver {
|
||||
bool withDebugger = false,
|
||||
bool startPaused = false,
|
||||
bool pauseOnExceptions = false,
|
||||
bool singleWidgetReloads = false,
|
||||
int? attachPort,
|
||||
}) async {
|
||||
assert(!startPaused || withDebugger);
|
||||
@ -593,7 +585,6 @@ class FlutterRunTestDriver extends FlutterTestDriver {
|
||||
args,
|
||||
script: script,
|
||||
withDebugger: withDebugger,
|
||||
singleWidgetReloads: singleWidgetReloads,
|
||||
);
|
||||
|
||||
final Completer<void> prematureExitGuard = Completer<void>();
|
||||
@ -806,13 +797,11 @@ class FlutterTestTestDriver extends FlutterTestDriver {
|
||||
bool withDebugger = false,
|
||||
bool pauseOnExceptions = false,
|
||||
Future<void> Function()? beforeStart,
|
||||
bool singleWidgetReloads = false,
|
||||
}) async {
|
||||
await super._setupProcess(
|
||||
args,
|
||||
script: script,
|
||||
withDebugger: withDebugger,
|
||||
singleWidgetReloads: singleWidgetReloads,
|
||||
);
|
||||
|
||||
// Stash the PID so that we can terminate the VM more reliably than using
|
||||
|
@ -443,7 +443,6 @@ class TestFeatureFlags implements FeatureFlags {
|
||||
this.isMacOSEnabled = false,
|
||||
this.isWebEnabled = false,
|
||||
this.isWindowsEnabled = false,
|
||||
this.isSingleWidgetReloadEnabled = false,
|
||||
this.isAndroidEnabled = true,
|
||||
this.isIOSEnabled = true,
|
||||
this.isFuchsiaEnabled = false,
|
||||
@ -463,9 +462,6 @@ class TestFeatureFlags implements FeatureFlags {
|
||||
@override
|
||||
final bool isWindowsEnabled;
|
||||
|
||||
@override
|
||||
final bool isSingleWidgetReloadEnabled;
|
||||
|
||||
@override
|
||||
final bool isAndroidEnabled;
|
||||
|
||||
@ -492,8 +488,6 @@ class TestFeatureFlags implements FeatureFlags {
|
||||
return isMacOSEnabled;
|
||||
case flutterWindowsDesktopFeature:
|
||||
return isWindowsEnabled;
|
||||
case singleWidgetReload:
|
||||
return isSingleWidgetReloadEnabled;
|
||||
case flutterAndroidFeature:
|
||||
return isAndroidEnabled;
|
||||
case flutterIOSFeature:
|
||||
|
Loading…
x
Reference in New Issue
Block a user