Unverified Commit 419fef3f authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

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
parent f8ac23cd
......@@ -3731,7 +3731,18 @@ class StatefulElement extends ComponentElement {
assert(_state._debugLifecycleState == _StateLifecycle.created);
try {
_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 {
_debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
}
......@@ -3753,7 +3764,18 @@ class StatefulElement extends ComponentElement {
_state._widget = widget;
try {
_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 {
_debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
}
......
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);
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment