This commit is contained in:
parent
c873c2100a
commit
2b9beda0d0
@ -1016,9 +1016,6 @@ class _NullElement extends Element {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool get debugDoingBuild => throw UnimplementedError();
|
bool get debugDoingBuild => throw UnimplementedError();
|
||||||
|
|
||||||
@override
|
|
||||||
void performRebuild() { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _NullWidget extends Widget {
|
class _NullWidget extends Widget {
|
||||||
|
@ -4507,6 +4507,10 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the element has been marked as needing rebuilding.
|
/// Returns true if the element has been marked as needing rebuilding.
|
||||||
|
///
|
||||||
|
/// The flag is true when the element is first created and after
|
||||||
|
/// [markNeedsBuild] has been called. The flag is reset to false in the
|
||||||
|
/// [performRebuild] implementation.
|
||||||
bool get dirty => _dirty;
|
bool get dirty => _dirty;
|
||||||
bool _dirty = true;
|
bool _dirty = true;
|
||||||
|
|
||||||
@ -4580,10 +4584,14 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
|
|||||||
/// Called by the [BuildOwner] when [BuildOwner.scheduleBuildFor] has been
|
/// Called by the [BuildOwner] when [BuildOwner.scheduleBuildFor] has been
|
||||||
/// called to mark this element dirty, by [mount] when the element is first
|
/// called to mark this element dirty, by [mount] when the element is first
|
||||||
/// built, and by [update] when the widget has changed.
|
/// built, and by [update] when the widget has changed.
|
||||||
|
///
|
||||||
|
/// The method will only rebuild if [dirty] is true. To rebuild irregardless
|
||||||
|
/// of the [dirty] flag, set `force` to true. Forcing a rebuild is convenient
|
||||||
|
/// from [update], during which [dirty] is false.
|
||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
void rebuild() {
|
void rebuild({bool force = false}) {
|
||||||
assert(_lifecycleState != _ElementLifecycle.initial);
|
assert(_lifecycleState != _ElementLifecycle.initial);
|
||||||
if (_lifecycleState != _ElementLifecycle.active || !_dirty) {
|
if (_lifecycleState != _ElementLifecycle.active || (!_dirty && !force)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert(() {
|
assert(() {
|
||||||
@ -4618,8 +4626,13 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
|
|||||||
/// Cause the widget to update itself.
|
/// Cause the widget to update itself.
|
||||||
///
|
///
|
||||||
/// Called by [rebuild] after the appropriate checks have been made.
|
/// Called by [rebuild] after the appropriate checks have been made.
|
||||||
|
///
|
||||||
|
/// The base implementation only clears the [dirty] flag.
|
||||||
@protected
|
@protected
|
||||||
void performRebuild();
|
@mustCallSuper
|
||||||
|
void performRebuild() {
|
||||||
|
_dirty = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ElementDiagnosticableTreeNode extends DiagnosticableTreeNode {
|
class _ElementDiagnosticableTreeNode extends DiagnosticableTreeNode {
|
||||||
@ -4901,7 +4914,7 @@ abstract class ComponentElement extends Element {
|
|||||||
} finally {
|
} finally {
|
||||||
// We delay marking the element as clean until after calling build() so
|
// We delay marking the element as clean until after calling build() so
|
||||||
// that attempts to markNeedsBuild() during build() will be ignored.
|
// that attempts to markNeedsBuild() during build() will be ignored.
|
||||||
_dirty = false;
|
super.performRebuild(); // clears the "dirty" flag
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
_child = updateChild(_child, built, slot);
|
_child = updateChild(_child, built, slot);
|
||||||
@ -4955,8 +4968,7 @@ class StatelessElement extends ComponentElement {
|
|||||||
void update(StatelessWidget newWidget) {
|
void update(StatelessWidget newWidget) {
|
||||||
super.update(newWidget);
|
super.update(newWidget);
|
||||||
assert(widget == newWidget);
|
assert(widget == newWidget);
|
||||||
_dirty = true;
|
rebuild(force: true);
|
||||||
rebuild();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5053,10 +5065,6 @@ class StatefulElement extends ComponentElement {
|
|||||||
super.update(newWidget);
|
super.update(newWidget);
|
||||||
assert(widget == newWidget);
|
assert(widget == newWidget);
|
||||||
final StatefulWidget oldWidget = state._widget!;
|
final StatefulWidget oldWidget = state._widget!;
|
||||||
// We mark ourselves as dirty before calling didUpdateWidget to
|
|
||||||
// let authors call setState from within didUpdateWidget without triggering
|
|
||||||
// asserts.
|
|
||||||
_dirty = true;
|
|
||||||
state._widget = widget as StatefulWidget;
|
state._widget = widget as StatefulWidget;
|
||||||
final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
|
final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
|
||||||
assert(() {
|
assert(() {
|
||||||
@ -5072,7 +5080,7 @@ class StatefulElement extends ComponentElement {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}());
|
}());
|
||||||
rebuild();
|
rebuild(force: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -5217,8 +5225,7 @@ abstract class ProxyElement extends ComponentElement {
|
|||||||
super.update(newWidget);
|
super.update(newWidget);
|
||||||
assert(widget == newWidget);
|
assert(widget == newWidget);
|
||||||
updated(oldWidget);
|
updated(oldWidget);
|
||||||
_dirty = true;
|
rebuild(force: true);
|
||||||
rebuild();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called during build when the [widget] has changed.
|
/// Called during build when the [widget] has changed.
|
||||||
@ -5746,7 +5753,7 @@ abstract class RenderObjectElement extends Element {
|
|||||||
}());
|
}());
|
||||||
assert(_slot == newSlot);
|
assert(_slot == newSlot);
|
||||||
attachRenderObject(newSlot);
|
attachRenderObject(newSlot);
|
||||||
_dirty = false;
|
super.performRebuild(); // clears the "dirty" flag
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -5768,7 +5775,7 @@ abstract class RenderObjectElement extends Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void performRebuild() {
|
void performRebuild() { // ignore: must_call_super, _performRebuild calls super.
|
||||||
_performRebuild(); // calls widget.updateRenderObject()
|
_performRebuild(); // calls widget.updateRenderObject()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5783,7 +5790,7 @@ abstract class RenderObjectElement extends Element {
|
|||||||
_debugDoingBuild = false;
|
_debugDoingBuild = false;
|
||||||
return true;
|
return true;
|
||||||
}());
|
}());
|
||||||
_dirty = false;
|
super.performRebuild(); // clears the "dirty" flag
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the children of this element to use new widgets.
|
/// Updates the children of this element to use new widgets.
|
||||||
@ -6539,9 +6546,6 @@ class _NullElement extends Element {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool get debugDoingBuild => throw UnimplementedError();
|
bool get debugDoingBuild => throw UnimplementedError();
|
||||||
|
|
||||||
@override
|
|
||||||
void performRebuild() => throw UnimplementedError();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _NullWidget extends Widget {
|
class _NullWidget extends Widget {
|
||||||
|
@ -591,9 +591,6 @@ class _NullElement extends Element {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool get debugDoingBuild => throw UnimplementedError();
|
bool get debugDoingBuild => throw UnimplementedError();
|
||||||
|
|
||||||
@override
|
|
||||||
void performRebuild() { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _NullWidget extends Widget {
|
class _NullWidget extends Widget {
|
||||||
|
@ -220,11 +220,6 @@ void main() {
|
|||||||
class _TestElement extends Element {
|
class _TestElement extends Element {
|
||||||
_TestElement() : super(const Placeholder());
|
_TestElement() : super(const Placeholder());
|
||||||
|
|
||||||
@override
|
|
||||||
void performRebuild() {
|
|
||||||
// Intentionally left empty.
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get debugDoingBuild => throw UnimplementedError();
|
bool get debugDoingBuild => throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
70
packages/flutter/test/widgets/did_update_widget_test.dart
Normal file
70
packages/flutter/test/widgets/did_update_widget_test.dart
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// 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/widgets.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('Can call setState from didUpdateWidget', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(const Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: WidgetUnderTest(text: 'hello'),
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(find.text('hello'), findsOneWidget);
|
||||||
|
expect(find.text('world'), findsNothing);
|
||||||
|
final _WidgetUnderTestState state = tester.state<_WidgetUnderTestState>(find.byType(WidgetUnderTest));
|
||||||
|
expect(state.setStateCalled, 0);
|
||||||
|
expect(state.didUpdateWidgetCalled, 0);
|
||||||
|
|
||||||
|
await tester.pumpWidget(const Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: WidgetUnderTest(text: 'world'),
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(find.text('world'), findsOneWidget);
|
||||||
|
expect(find.text('hello'), findsNothing);
|
||||||
|
expect(state.setStateCalled, 1);
|
||||||
|
expect(state.didUpdateWidgetCalled, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class WidgetUnderTest extends StatefulWidget {
|
||||||
|
const WidgetUnderTest({super.key, required this.text});
|
||||||
|
|
||||||
|
final String text;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<WidgetUnderTest> createState() => _WidgetUnderTestState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _WidgetUnderTestState extends State<WidgetUnderTest> {
|
||||||
|
late String text = widget.text;
|
||||||
|
|
||||||
|
int setStateCalled = 0;
|
||||||
|
int didUpdateWidgetCalled = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(WidgetUnderTest oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
didUpdateWidgetCalled += 1;
|
||||||
|
if (oldWidget.text != widget.text) {
|
||||||
|
// This setState is load bearing for the test.
|
||||||
|
setState(() {
|
||||||
|
text = widget.text;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setState(VoidCallback fn) {
|
||||||
|
super.setState(fn);
|
||||||
|
setStateCalled += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Text(text);
|
||||||
|
}
|
||||||
|
}
|
@ -1861,9 +1861,6 @@ class DirtyElementWithCustomBuildOwner extends Element {
|
|||||||
|
|
||||||
final BuildOwner _owner;
|
final BuildOwner _owner;
|
||||||
|
|
||||||
@override
|
|
||||||
void performRebuild() {}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
BuildOwner get owner => _owner;
|
BuildOwner get owner => _owner;
|
||||||
|
|
||||||
@ -1968,9 +1965,9 @@ class StatefulElementSpy extends StatefulElement {
|
|||||||
_Stateful get _statefulWidget => widget as _Stateful;
|
_Stateful get _statefulWidget => widget as _Stateful;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void rebuild() {
|
void rebuild({bool force = false}) {
|
||||||
_statefulWidget.onElementRebuild?.call(this);
|
_statefulWidget.onElementRebuild?.call(this);
|
||||||
super.rebuild();
|
super.rebuild(force: force);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2115,9 +2112,6 @@ class _EmptyElement extends Element {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool get debugDoingBuild => false;
|
bool get debugDoingBuild => false;
|
||||||
|
|
||||||
@override
|
|
||||||
void performRebuild() {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TestLeaderLayerWidget extends SingleChildRenderObjectWidget {
|
class _TestLeaderLayerWidget extends SingleChildRenderObjectWidget {
|
||||||
|
@ -701,11 +701,11 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void rebuild() {
|
void rebuild({bool force = false}) {
|
||||||
if (_throwOnRebuild) {
|
if (_throwOnRebuild) {
|
||||||
throw 'rebuild';
|
throw 'rebuild';
|
||||||
}
|
}
|
||||||
super.rebuild();
|
super.rebuild(force: force);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
''',
|
''',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user