Flutter Driver is a neat way of testing Flutter applications perfoming integration tests. It allows you to run your Flutter application in an emulator and to control the app from your test code, performing taps and asserting that the app works as expected.
One problem I faced when using Flutter Driver is that the app won’t restart between tests.
For example, I would run a test in which the user performs a login, then a test in which the user performs a registration, but after the login test, the test left the app logged in. An alternative would be to always logout and leave the app after every single test, but sometimes we just want to clear everything up and go back to the start.
Restarting a Flutter app
Restarting Flutter applications can be confusing for newcomers but the solution is indeed simple and smart: We need to recreate the root Widget.
In my case, my root widget is a StatefulWidget
called App
, which accepts a Key
as parameter like all other Widgets:
class App extends StatefulWidget {
App({
Key key,
}) : super(key: key);
@override
_AppState createState() => _AppState();
}
When we recreate this Widget with a different Key
, Flutter will completely rebuild the subtree, throwing away the existing Widget tree and creating a new one from scratch.
For example, you can create the App
Widget passing a UniqueKey
:
runApp(
App(
key: UniqueKey(),
),
);
But this is a bit unpractical, because we can’t call to runApp
from within our application. That’s what packages like flutter_phoenix do for you:
runApp(
Phoenix(
child: App(),
),
);
Then you can call to Phoenix.rebirth(context)
to force the Phoenix
Widget to be recreated.
Restarting a Flutter app from Flutter Driver tests
The same concept can be applied from Flutter Driver. For it, you need to implement a handler to communicate from the Flutter Driver to the application, to force recreating the root Widget.
You can do that by passing a handler
to the enableFlutterDriverExtension
method when starting your tests:
enableFlutterDriverExtension(handler: (command) async {
switch (command) {
case 'restart':
_startApp();
return 'ok';
}
throw Exception('Unknown command');
});
This allows you to call await driver.requestData('restart')
from your tests, running the _startApp()
method again.
In _startApp()
, you need to call to runApp
like in the previous example, and like before, pass a UniqueKey
:
void _startApp() {
runApp(
App(
key: UniqueKey(),
),
);
}
Then, in your tests, when you want to restart your application, call to the requestData
method with the restart
command, this will call runApp
again, but because App
will have a different key
, the App
Widget will be recreated, allowing you to test different parts of the app like if it was run for the first time.