Most Android apps make use of startActivityForResult at one point or another. But, how do you do this in Flutter?
The official doc
The very helpful Flutter For Android Developers page on the official Flutter website demonstrates a way to do it using Navigator.of(context).pushNamed.
However, in the official doc about Navigation, using MaterialPageRoute is recommended for Material Design apps. So, how do you combine the two?
Adapting the official doc to using MaterialPageRoute
My first attempt provided a crash, as below.
1 2 3 4 5 |
I/flutter (23315): ??? EXCEPTION CAUGHT BY GESTURE ???????????????????????????????????????????????????????????????????? I/flutter (23315): The following assertion was thrown while handling a gesture: I/flutter (23315): type '_InternalLinkedHashMap' is not a subtype of type 'Null' of 'result' where I/flutter (23315): _InternalLinkedHashMap is from dart:collection I/flutter (23315): Null is from dart:core |
Turned out it was due to using new MaterialPageRoute<Null>, which I had copied from the Navigation page. The solution: replace Null with dynamic.
Code tutorial
For this code tutorial, we will create an app with 2 screens. Tapping a button on the first screen will open the second screen. The second screen has 2 buttons. When tapping either of them, the screen is closed, and the first screen displays the text of the button that was tapped.
App setup
To follow the code tutorial, create a new app as follows.
1 |
flutter create startactivityforresultexample |
If you’re unsure how to set up a Flutter app, check out Getting started with Flutter official tutorial.
Firstly, let’s set up main.dart to launch a MaterialApp.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import 'package:flutter/material.dart'; import 'home/home_page.dart'; void main() { runApp(new MyApp()); } class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return new MaterialApp( title: 'StartActivityForResult Example', theme: new ThemeData( primaryColor: const Color(0xFF43a047), accentColor: const Color(0xFFffcc00), primaryColorBrightness: Brightness.dark, ), home: new HomePage(), ); } } |
Secondly, we need to add HomePage to be able to build the project. We create a new folder home and, in it, home_page.dart.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
import 'package:flutter/material.dart'; class HomePage extends StatefulWidget { HomePage({Key key}) : super(key: key); @override _HomePageState createState() => new _HomePageState(); } class _HomePageState extends State<HomePage> { String _selection; @override Widget build(BuildContext context) { Widget buttonWidget = new FlatButton( textColor: Colors.blueGrey, color: Colors.white, child: new Text('To Selection Screen'), onPressed: _buttonTapped, ); List<Widget> widgets = new List<Widget>(); widgets.add(buttonWidget); if (_selection != null) { Widget textWidget = new Text(_selection); widgets.add(textWidget); } return new Scaffold( appBar: new AppBar( title: new Text("Home Page, with selection"), ), body: new Padding( padding: new EdgeInsets.symmetric(vertical: 0.0, horizontal: 4.0), child: new Column( children: widgets, ), ), ); } void _buttonTapped() { // TODO } } |
For HomePage, we use a stateful widget, with the variable _selection tracking the selection made in the other screen. If that variable is null, the screen shows only one button. Otherwise, it shows its value below the button. When tapping the button in HomePage, the method _buttonTapped() is fired (this is where we will launch the second screen from).
Thirdly, we add the second screen, SelectionPage, as a stateless widget. Let’s add a folder named selection and, in it, create selection_page.dart. It contains 2 buttons, which call _selectItem when tapped (this is where we will close this screen and pass on the value back to HomePage).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
import 'package:flutter/material.dart'; const String ITEM_1 = 'Item 1'; const String ITEM_2 = 'Item 2'; class SelectionPage extends StatelessWidget { SelectionPage({Key key}) : super(key: key); @override Widget build(BuildContext context) { Widget buttons = new Container( child: new Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ new FlatButton( textColor: Colors.blueGrey, color: Colors.white, child: new Text(ITEM_1), onPressed: () => _selectItem(ITEM_1, context), ), new FlatButton( textColor: Colors.blueGrey, color: Colors.white, child: new Text(ITEM_2), onPressed: () => _selectItem(ITEM_2, context), ), ], ), ); return new Scaffold( appBar: new AppBar( title: new Text("Selection page"), ), body: new Padding( padding: new EdgeInsets.symmetric(vertical: 0.0, horizontal: 4.0), child: buttons, ), ); } void _selectItem(String value, BuildContext context) { // TODO } } |
Start Activity For Result implementation
We can now implement the Flutter equivalent of Android SDK’s startActivityForResult.
The code to launch SelectionPage when tapping button on HomePage is as below.
1 2 3 4 5 6 7 8 9 |
import '../selection/selection_page.dart'; void _buttonTapped() { Navigator.of(context).push(new MaterialPageRoute<dynamic>( builder: (BuildContext context) { return new SelectionPage(); }, )); } |
This is the equivalent of Android SDK’s startActivity.
Now, let’s implement closing SelectionPage screen and passing some data when tapping the button in SelectionPage.
1 2 3 |
void _selectItem(String value, BuildContext context) { Navigator.of(context).pop({'selection':value}); } |
And let’s read the data in HomePage (as you would do in Android SDK’s onActivityResult ). Note that instead of using a callback system as in Android SDK, we read the data sent back by SelectionPage as a Future of the code launching SelectionPage.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import 'dart:async' show Future; Future _buttonTapped() async { Map results = await Navigator.of(context).push(new MaterialPageRoute<dynamic>( builder: (BuildContext context) { return new SelectionPage(); }, )); if (results != null && results.containsKey('selection')) { setState(() { _selection = results['selection']; }); } } |
Voila!

What next?
Bookmark Flutter for Android Developers as it is full of great tricks that will save you time. Then, if you haven’t done so already, read How to write an integration test in Flutter and write a UI test for this code tutorial.

Mobile app developer with 12 years experience. Started with the Android SDK in 2010 and switched to Flutter in 2017. Past apps range from start ups to large tech (Google) and non tech (British Airways) companies, in various sectors (transport, commercial operations, e-commerce).
very thanks my friend
where is java part?
The article isn’t about starting a new Android activity from Flutter, it is about starting a new Flutter screen/route and listen to results, as you would do in Android with
startActivityForResult
(the article was targeted at native Android developers trying to figure out how to do simple things in Flutter). However, you should refer to more recent articles on other blogs for routing in Flutter, as routing in Flutter has evolved a lot since this was written..