Check that State.initState and State.didUpdateWidget don't return Futures (#15183)
* add a debug check that the returned Type of State.initState and State.didUpdateWidget are not Futures * add test to verify that async lifecycles throw FlutterErrors * address some feedback, match implementation of setState check * address feedback from hixie * fix odd spacing in test
This commit is contained in:
parent
f8ac23cd86
commit
419fef3f8b
@ -3731,7 +3731,18 @@ class StatefulElement extends ComponentElement {
|
|||||||
assert(_state._debugLifecycleState == _StateLifecycle.created);
|
assert(_state._debugLifecycleState == _StateLifecycle.created);
|
||||||
try {
|
try {
|
||||||
_debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
|
_debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
|
||||||
_state.initState();
|
final dynamic debugCheckForReturnedFuture = _state.initState() as dynamic;
|
||||||
|
assert(() {
|
||||||
|
if (debugCheckForReturnedFuture is Future) {
|
||||||
|
throw new FlutterError(
|
||||||
|
'${_state.runtimeType}.initState() returned a Future.\n'
|
||||||
|
'State.initState() must be a void method without an `async` keyword.\n'
|
||||||
|
'Rather than awaiting on asynchronous work directly inside of initState,\n'
|
||||||
|
'call a separate method to do this work without awaiting it.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}());
|
||||||
} finally {
|
} finally {
|
||||||
_debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
|
_debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
|
||||||
}
|
}
|
||||||
@ -3753,7 +3764,18 @@ class StatefulElement extends ComponentElement {
|
|||||||
_state._widget = widget;
|
_state._widget = widget;
|
||||||
try {
|
try {
|
||||||
_debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
|
_debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
|
||||||
_state.didUpdateWidget(oldWidget);
|
final dynamic debugCheckForReturnedFuture = _state.didUpdateWidget(oldWidget) as dynamic;
|
||||||
|
assert(() {
|
||||||
|
if (debugCheckForReturnedFuture is Future) {
|
||||||
|
throw new FlutterError(
|
||||||
|
'${_state.runtimeType}.didUpdateWidget() returned a Future.\n'
|
||||||
|
'State.didUpdateWidget() must be a void method without an `async` keyword.\n'
|
||||||
|
'Rather than awaiting on asynchronous work directly inside of didUpdateWidget,\n'
|
||||||
|
'call a separate method to do this work without awaiting it.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}());
|
||||||
} finally {
|
} finally {
|
||||||
_debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
|
_debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
|
||||||
}
|
}
|
||||||
|
57
packages/flutter/test/widgets/async_lifecycle_test.dart
Normal file
57
packages/flutter/test/widgets/async_lifecycle_test.dart
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
class InvalidOnInitLifecycleWidget extends StatefulWidget {
|
||||||
|
const InvalidOnInitLifecycleWidget({Key key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
InvalidOnInitLifecycleWidgetState createState() => new InvalidOnInitLifecycleWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class InvalidOnInitLifecycleWidgetState extends State<InvalidOnInitLifecycleWidget> {
|
||||||
|
@override
|
||||||
|
void initState() async {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return new Container();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InvalidDidUpdateWidgetLifecycleWidget extends StatefulWidget {
|
||||||
|
const InvalidDidUpdateWidgetLifecycleWidget({Key key, this.id}) : super(key: key);
|
||||||
|
|
||||||
|
final int id;
|
||||||
|
|
||||||
|
@override
|
||||||
|
InvalidDidUpdateWidgetLifecycleWidgetState createState() => new InvalidDidUpdateWidgetLifecycleWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class InvalidDidUpdateWidgetLifecycleWidgetState extends State<InvalidDidUpdateWidgetLifecycleWidget> {
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(InvalidDidUpdateWidgetLifecycleWidget oldWidget) async {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return new Container();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('async onInit throws FlutterError', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(const InvalidOnInitLifecycleWidget());
|
||||||
|
|
||||||
|
expect(tester.takeException(), isFlutterError);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('async didUpdateWidget throws FlutterError', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(const InvalidDidUpdateWidgetLifecycleWidget(id: 1));
|
||||||
|
await tester.pumpWidget(const InvalidDidUpdateWidgetLifecycleWidget(id: 2));
|
||||||
|
|
||||||
|
expect(tester.takeException(), isFlutterError);
|
||||||
|
});
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user