diff --git a/bin/internal/engine.version b/bin/internal/engine.version index cfb0a70587c24..c7bcfc8780041 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -6cca1bfd575a9a611f095e1ce2943d6170bc94af +f6e7fd521c595feebd8a9025681e14863691c7a1 diff --git a/dev/bots/docs.sh b/dev/bots/docs.sh index 4463b9f0cde1f..3312402aabb17 100755 --- a/dev/bots/docs.sh +++ b/dev/bots/docs.sh @@ -20,7 +20,7 @@ function generate_docs() { # Install and activate dartdoc. # NOTE: When updating to a new dartdoc version, please also update # `dartdoc_options.yaml` to include newly introduced error and warning types. - "$DART" pub global activate dartdoc 4.1.0 + "$DART" pub global activate dartdoc 5.0.1 # Install and activate the snippets tool, which resides in the # assets-for-api-docs repo: diff --git a/dev/tracing_tests/test/inflate_widget_update_test.dart b/dev/tracing_tests/test/inflate_widget_update_test.dart new file mode 100644 index 0000000000000..acfd014082f30 --- /dev/null +++ b/dev/tracing_tests/test/inflate_widget_update_test.dart @@ -0,0 +1,77 @@ +// 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/scheduler.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'common.dart'; + +void main() { + WidgetsFlutterBinding.ensureInitialized(); + initTimelineTests(); + test('Widgets with updated keys produce well formed timelines', () async { + await runFrame(() { runApp(const TestRoot()); }); + await SchedulerBinding.instance.endOfFrame; + + debugProfileBuildsEnabled = true; + + await runFrame(() { + TestRoot.state.updateKey(); + }); + + int buildCount = 0; + for (final TimelineEvent event in await fetchTimelineEvents()) { + if (event.json!['name'] == 'BUILD') { + final String ph = event.json!['ph'] as String; + if (ph == 'B') { + buildCount++; + } else if (ph == 'E') { + buildCount--; + } + } + } + expect(buildCount, 0); + + debugProfileBuildsEnabled = false; + }, skip: isBrowser); // [intended] uses dart:isolate and io. +} + +class TestRoot extends StatefulWidget { + const TestRoot({super.key}); + + static late TestRootState state; + + @override + State createState() => TestRootState(); +} + +class TestRootState extends State { + final Key _globalKey = GlobalKey(); + Key _localKey = UniqueKey(); + + @override + void initState() { + super.initState(); + TestRoot.state = this; + } + + void updateKey() { + setState(() { + _localKey = UniqueKey(); + }); + } + + @override + Widget build(BuildContext context) { + return Center( + key: _localKey, + child: SizedBox( + key: _globalKey, + width: 100, + height: 100, + ), + ); + } +} diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart index 5bc0ecb272c47..f1e03b4a0e323 100644 --- a/packages/flutter/lib/src/widgets/framework.dart +++ b/packages/flutter/lib/src/widgets/framework.dart @@ -3792,33 +3792,35 @@ abstract class Element extends DiagnosticableTree implements BuildContext { ); } - final Key? key = newWidget.key; - if (key is GlobalKey) { - final Element? newChild = _retakeInactiveElement(key, newWidget); - if (newChild != null) { - assert(newChild._parent == null); - assert(() { - _debugCheckForCycles(newChild); - return true; - }()); - newChild._activateWithParent(this, newSlot); - final Element? updatedChild = updateChild(newChild, newWidget, newSlot); - assert(newChild == updatedChild); - return updatedChild!; + try { + final Key? key = newWidget.key; + if (key is GlobalKey) { + final Element? newChild = _retakeInactiveElement(key, newWidget); + if (newChild != null) { + assert(newChild._parent == null); + assert(() { + _debugCheckForCycles(newChild); + return true; + }()); + newChild._activateWithParent(this, newSlot); + final Element? updatedChild = updateChild(newChild, newWidget, newSlot); + assert(newChild == updatedChild); + return updatedChild!; + } } - } - final Element newChild = newWidget.createElement(); - assert(() { - _debugCheckForCycles(newChild); - return true; - }()); - newChild.mount(this, newSlot); - assert(newChild._lifecycleState == _ElementLifecycle.active); - - if (isTimelineTracked) - Timeline.finishSync(); + final Element newChild = newWidget.createElement(); + assert(() { + _debugCheckForCycles(newChild); + return true; + }()); + newChild.mount(this, newSlot); + assert(newChild._lifecycleState == _ElementLifecycle.active); - return newChild; + return newChild; + } finally { + if (isTimelineTracked) + Timeline.finishSync(); + } } void _debugCheckForCycles(Element newChild) {