mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Remove "disqualified" concept
Allows a non-initialised stateful component to be used as a source of settings more than once. Instead of asserting that it was only being used once, we assert that once you are stateful you don't get used as a bag of settings, which is the real thing we were trying to avoid. A lot of code ends up using StatefulComponents as a source multiple times in a row, and before this would fail. Patch by Ian Hickson
This commit is contained in:
parent
79980b7eed
commit
aba0593069
@ -879,25 +879,13 @@ abstract class StatefulComponent extends Component {
|
||||
|
||||
StatefulComponent({ Key key }) : super(key: key);
|
||||
|
||||
bool _disqualifiedFromEverAppearingAgain = false;
|
||||
bool _isStateInitialized = false;
|
||||
|
||||
void didMount() {
|
||||
assert(!_disqualifiedFromEverAppearingAgain);
|
||||
super.didMount();
|
||||
}
|
||||
|
||||
void _buildIfDirty() {
|
||||
assert(!_disqualifiedFromEverAppearingAgain);
|
||||
super._buildIfDirty();
|
||||
}
|
||||
|
||||
bool retainStatefulNodeIfPossible(StatefulComponent newNode) {
|
||||
assert(!_disqualifiedFromEverAppearingAgain);
|
||||
assert(newNode != null);
|
||||
assert(!newNode._isStateInitialized);
|
||||
assert(_canSync(this, newNode));
|
||||
assert(_child != null);
|
||||
newNode._disqualifiedFromEverAppearingAgain = true;
|
||||
|
||||
newNode._child = _child;
|
||||
_child = null;
|
||||
@ -910,14 +898,17 @@ abstract class StatefulComponent extends Component {
|
||||
// when _sync is called, our 'old' is actually the new instance that
|
||||
// we are to copy state from.
|
||||
void _sync(Widget old, dynamic slot) {
|
||||
assert(!_disqualifiedFromEverAppearingAgain);
|
||||
// TODO(ianh): _sync should only be called once when old == null
|
||||
if (old == null && !_isStateInitialized) {
|
||||
initState();
|
||||
_isStateInitialized = true;
|
||||
if (old == null) {
|
||||
if (!_isStateInitialized) {
|
||||
initState();
|
||||
_isStateInitialized = true;
|
||||
}
|
||||
}
|
||||
if (old != null)
|
||||
if (old != null) {
|
||||
assert(_isStateInitialized);
|
||||
assert(!old._isStateInitialized);
|
||||
syncConstructorArguments(old);
|
||||
}
|
||||
super._sync(old, slot);
|
||||
}
|
||||
|
||||
@ -930,25 +921,21 @@ abstract class StatefulComponent extends Component {
|
||||
// method to update `this` to account for the new values the parent
|
||||
// passed to `source`. Make sure to call super.syncConstructorArguments(source)
|
||||
// unless you are extending StatefulComponent directly.
|
||||
// A given source can be used multiple times as a source.
|
||||
// The source must not be mutated.
|
||||
void syncConstructorArguments(Component source);
|
||||
|
||||
Widget syncChild(Widget node, Widget oldNode, dynamic slot) {
|
||||
assert(!_disqualifiedFromEverAppearingAgain);
|
||||
return super.syncChild(node, oldNode, slot);
|
||||
}
|
||||
|
||||
// Calls function fn immediately and then schedules another build
|
||||
// for this Component.
|
||||
void setState(void fn()) {
|
||||
assert(!_disqualifiedFromEverAppearingAgain);
|
||||
fn();
|
||||
_scheduleBuild();
|
||||
}
|
||||
|
||||
String toStringName() {
|
||||
if (_disqualifiedFromEverAppearingAgain)
|
||||
return '[[DISQUALIFIED]] ${super.toStringName()}';
|
||||
return super.toStringName();
|
||||
if (_isStateInitialized)
|
||||
return 'Stateful ${super.toStringName()}';
|
||||
return 'Stateless ${super.toStringName()}';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
66
sky/unit/test/widget/stateful_components_test.dart
Normal file
66
sky/unit/test/widget/stateful_components_test.dart
Normal file
@ -0,0 +1,66 @@
|
||||
import 'package:sky/animation.dart';
|
||||
import 'package:sky/widgets.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'widget_tester.dart';
|
||||
|
||||
class InnerComponent extends StatefulComponent {
|
||||
InnerComponent();
|
||||
|
||||
bool _didInitState = false;
|
||||
|
||||
void initState() {
|
||||
_didInitState = true;
|
||||
}
|
||||
|
||||
void syncConstructorArguments(InnerComponent source) {
|
||||
}
|
||||
|
||||
Widget build() {
|
||||
return new Container();
|
||||
}
|
||||
}
|
||||
|
||||
class OutterContainer extends StatefulComponent {
|
||||
OutterContainer({ this.child });
|
||||
|
||||
InnerComponent child;
|
||||
|
||||
void syncConstructorArguments(OutterContainer source) {
|
||||
child = source.child;
|
||||
}
|
||||
|
||||
Widget build() {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
test('resync stateful widget', () {
|
||||
|
||||
WidgetTester tester = new WidgetTester();
|
||||
|
||||
InnerComponent inner;
|
||||
OutterContainer outter;
|
||||
|
||||
tester.pumpFrame(() {
|
||||
return new OutterContainer(child: new InnerComponent());
|
||||
});
|
||||
|
||||
tester.pumpFrame(() {
|
||||
inner = new InnerComponent();
|
||||
outter = new OutterContainer(child: inner);
|
||||
return outter;
|
||||
});
|
||||
|
||||
expect(inner._didInitState, isFalse);
|
||||
expect(inner.parent, isNull);
|
||||
|
||||
outter.setState(() {});
|
||||
scheduler.beginFrame(0.0);
|
||||
|
||||
expect(inner._didInitState, isFalse);
|
||||
expect(inner.parent, isNull);
|
||||
|
||||
});
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user