Home Tutorials Training Consulting Products Books Company Donate Contact us









Get more...

Training Events

This tutorial gives a brief introduction to the Flutter SDK

1. Flutter SDK

The Flutter SDK allows you to build beautiful and fast native apps for both iOS and Android with a single codebase. It was introduced by Google in 2017 and has reached v1.0 on December 4th, 2018.

It is written in C, C++ and Dart. The SDK utilizes the Skia Graphics Engine to render on iOS and Android.

This tutorial will use mainly Visual Studio Code for its development but the usage of the command line is also demonstrated.

2. Installation of Flutter

To develop Flutter applications you need:

  • the Flutter SDK

  • the Android SDK for developing Android applications

  • XCode for development iOS applications

To install the Flutter SDK follow the instructions on the Flutter installation page for your operating system. To install the Flutter SDK follow the instructions Android Studio installation guide To install XCode follow the instructions xCode installation guide

iOS apps can only be developed on macOS devices.

2.1. Android SDK licenses

The Android SDK requires you to sign SDK package licenses. This is relevant for publishing the app, but $ flutter doctor will nag you about this. You can run the command $ flutter doctor --android-licenses to view and accept the licenses.

If you get a Java exception when running the above command, make sure the shell you’re currently in has Java 8 available. The command will only work with Java 8 (not 9 or higher).

2.2. Test the Flutter installation

To test your installation run the $ flutter doctor command.

The output should look like this:

[✓] Flutter (Channel stable, v1.2.1, on Linux, locale en_US.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
[!] Android Studio (version 3.3)
    ✗ Flutter plugin not installed; this adds Flutter specific functionality. (1)
    ✗ Dart plugin not installed; this adds Dart specific functionality. (1)
[!] VS Code (version 1.32.3)
    ✗ Flutter extension not installed; install from
      https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter (2)
[!] Connected device
    ! No devices available (3)
1 If you’re not using Android Studio for development you can ignore these errors
2 Configuration of the editor will be covered later
3 Configuring devices for debugging will be covered later

2.3. Special note for Linux users

If you are on Linux (Ubuntu, Fedora, Arch, etc.) you might need to install an additional package. The output of $ flutter doctor will show you the following message:

    ✗ Downloaded executables cannot execute on host.
      See https://github.com/flutter/flutter/issues/6207 for more information
      On Debian/Ubuntu/Mint: sudo apt-get install lib32stdc++6
      On Fedora: dnf install libstdc++.i686
      On Arch: pacman -S lib32-libstdc++5 (you need to enable multilib:
      https://wiki.archlinux.org/index.php/Official_repositories#multilib)

3. Setup of the Android Emulator

The emulator can be a powerful replacement of a physical Android device. However if you plan on using a physical device as your development device you can skip this step.

Before setting it up it is advisable to enable VM Acceleration on your development machine. This can sometimes drastically improve performance of the emulator.

Currently, emulators are only creatable in Android Studio. This is because the AVD Manager (AVD is an abbreviation for Android Virtual Device) is tightly integrated into it. But don’t worry, you will only need to create an emulator once. After that the Flutter CLI tools can start the emulator for you.

If you already have a project in Android Studio, click on Tools  AVD Manager.

Once open, click on + Create Virtual Device.

You will be presented with a list of devices available for emulation. These cover most of what is available. For this purpose it is not important which device you choose. Just keep in mind, that the devices have different aspect-ratios and resolutions. As a result your layout might look different to what is presented in this series.

This tutorial will contain screenshots from the Pixel 3

The next step is to choose an Android version. At the time of writing this is Android Q, but for Flutter apps there is not much of a difference. For more production-ready apps however, you should check your app for inconsistencies across your supported versions.

This tutorial will be done using Android Q

On the next page you can name your device. It is advisable to choose a name you recognize, as you will have to choose the correct device to run your Flutter apps on.

You can close the AVD Manager now.

To check if the emulator is available, run the $ flutter doctor command again. There should be 1 connected device:

[✓] Connected device (1 available)

3.1. Setup of ADB on a physical device

Flutter allows to use your physical android device as a debug target. To enable this functionality you need to enable ADB debugging on the device. This setting can be found in the developer options of your phone.

Open the settings app on the phone and got to About Phone and tap Build Number seven times. This enables a sub menu in the settings app.

Go to Developer options at the bottom and enable USB-Debugging.

On some devices the Developer Options can be located or activated differently. If unsure search for the instructions for your specific device.

Connect your smartphone to your PC via USB.

Now run $ flutter doctor again and there should be 1 connected device:

[✓] Connected device (1 available)

On some devices you will first have accept the connection by your PC.

4. Configuration of the development environment

Flutter can be developed with the usage of a text editor and the command line.

More advanced support is delivered by the following tools:

We currently recommend Visual Studio Code as development tools, as it is fast and lightweight.

4.1. Visual Studio Code

Visual Studio Code is a lightweight text editor with support for a wide-array of plugins.

Follow the instructions on https://code.visualstudio.com/ for your platform. 551324 For Flutter development there are two plugins required:

By clicking each link above Visual Studio Code will open the installation page of each plugin. Install both and reload the program if prompted.

Alternatively you can click on the extension icon on the left sidebar and search for Dart and Flutter and install the extensions manually.

flutter install10

4.2. Eclipse IDE

The Flutter support for the Eclipse IDE is currently being worked on. It reuses the existing Visual Studio Code components. Please star or comment on eclipse/dartboard#109 if you would like to see this functionality implemented.

4.3. Android Studio

Currently Android Studio is required for the setup of Flutter development, so it must be installed.

To add support for Dart and Flutter there are two plugins required. Install them by navigating to File  Settings…​  Plugins and search for 'Flutter'. Click on Install to install the plugin. You will be asked to also install the Dart plugin.

After the installation has finished restart the IDE.

5. Exercise: Creating a new project via the command line

5.1. Creating the project

On the command line create a new folder and switch to this folder.

To create a new Flutter project use the following commands.

flutter create first_app

By default Flutter uses Kotlin for the Android native code. Use -a java to use Java.

flutter create -i swift -a java testing

5.2. Run the project

cd first_app
flutter run
This requires that you have already created and started a virtual or physical device.

5.3. Review the code and change the application logic

Open the main.dart file located in the log folder via a text editor or via Visual Studio Code. Change the application logic to increase the counter by 2 instead of 1 if you click the button.

6. Exercise: Creating a new project via Visual Studio Code

6.1. Creating the project

Open VSCode and invoke the command palette via (View  Command Palette…​ or Ctrl + Shift + P).

  1. Select Flutter: New Project.

  2. Use the hello_world as name for your project.

  3. Select a folder where the project should be saved.

Note: that the text hint in VSCode is sometimes hard to distinguish from an input value.

VSCode will now restart and after it restarted it will run the actual creation of the template code. This will create the hello_world project.

Your explorer on the left should look similar to this:

Hello World - Project Explorer
If the project is not automatically created, make sure the Dart and Flutter extensions are enabled.

6.2. Ensure you have a device available for deployment

On the right side of the status bar at the bottom of VSCode you see multiple information about your development environment. One of these indicators shows the current status of the target device (or emulator):

If no device is currently available you can click on the No Devices button.

Hello World - Status Bar

A pop up opens that let you chose which device you want to develop on. The list contains the emulators as well as any real device you have configured for development.

Choose one of the listed devices. If the target device is an emulator wait for it to fully start.

6.3. Start the application

Press F5 to build, deploy and start the application.

Depending on your Internet connection and host device speed the initial build could take several minutes. You can see the current step/progress in the builtin DEBUG CONSOLE of VSCode. If it did not open automatically you can open it using the command Debug: Focus on Debug Console View

After the build process finished the app appears on your target device:

Hello World - Running Application

Tap 3 times on the + in the bottom right.

Now search for the primarySwatch color setting in the source code.

        primarySwatch: Colors.blue,

Replace it with the following.

        primarySwatch: Colors.red,

When saving the change should automatically show up in the running app. Notice how the number of times you clicked the button stayed the same. This a feature of the Flutter toolkit that allows state to be persisted between hot reloads.

You can trigger a hot reload manually by using the respective button in the debug toolkit at the top of you VSCode instance:

Debug Indicator

7. Flutter applications

The main entry point for Flutter apps is the lib/main.dart file. This file contains a main() method that can be used to call the runApp(…​) method of the Flutter framework.

As everything in Flutter the App base class of your app is just a widget that returns a MaterialApp from it’s build(BuildContext context) method. This class can be used to set routes, dark theme preferences and various other default settings. It is the root of a Flutter app.

The follwing listing shows a main.dart file:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:github_viewer/page/homepage.dart'; (1)

void main() => runApp(App()); (1)


class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: SimpleWidget(title: 'Flutter Demo Home Page'), (2)
    );
  }
}


class SimpleWidget extends StatelessWidget {
SimpleWidget({Key key, this.title}) : super(key: key);final String title;
 @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body:
        Text('Hello')
    );
  }
}
1 The main method that will be called on startup of the app.
2 The widget initially shown

7.1. Widgets

In Flutter every UI Element is a Widget. This means that every element is reusable and can be (re-)used in most places.

There are plenty of default Widgets provided by the Flutter library. These range from Buttons to more complex Widgets that handle scrolling behavior for you or a TabBar. They are designed by the creators of Flutter and are supposed to work by default, but can be customized if needed.

There is a very comprehensive list of widgets available on the widgets overview page.

7.2. Stateful- vs. StatelessWidgets

Flutter differentiates between StatelessWidgets and StatefulWidgets Widgets.

  • StatelessWidgets - should be used when there is no mutable state present

  • StatefulWidgets - has it’s own state, that can be modified at runtime

7.2.1. StatelessWidget

The StatelessWidget should be used when your widget does naot have any mutable state. Hence every field in it should be marked as final.

StatelessWidgets can be used directly and do not require a special sub-class.

Here is an example.

import 'package:flutter/material.dart';

void main() => runApp(App());

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Homepage(count: 10),
    );
  }
}

class Homepage extends StatelessWidget {
  final int count; (1)

  Homepage({this.count});(2)

  @override
  Widget build(BuildContext context) {
    return Text('Pressed: $count'); (3)
  }
}
1 As you can see the count field was now marked as final and the Dart compiler does not allow any changes to it anymore
2 count is defined as optional parameter
3 As we can’t change the value of count anymore there is no need for the button and we just show the text.

7.2.2. StatefulWidget

A widget’s state consists of properties of its class.

Flutter takes care of a lot of the state related operations for you. In other words: You don’t have to care about which (nested) widget needs to be updated or repainted, etc.

There are libraries and patterns that allow for more complex state management.

The use of StatefulWidgets requires you to also create a special class that contains the state of the widget. This state class extends State<T> (with T being your StatefulWidget). The created state class is exclusive to T and can’t be used in other StatefulWidget implementation.

You have to use a special function that tells Flutter that you updated your state. The setState(…​) function is available in all State<T> classes.

setState(…​) takes a function (VoidCallback) as an argument. In this VoidCallback you can modify the widget’s properties. Flutter will then re-render all impacted widgets.

Consider this simple example:

import 'package:flutter/material.dart';

void main() => runApp(App());

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Homepage(),
    );
  }
}

class Homepage extends StatefulWidget {
  @override
  _HomepageState createState() => _HomepageState();
}

class _HomepageState extends State<Homepage> {
  int count = 0; (1)

  @override
  Widget build(BuildContext context) {
    return MaterialButton(
      child: Text('Pressed: $count'),
      onPressed: () => setState(() => count++), (2)
    );
  }
}
1 This is the state of the widget
2 In setState(…​) we update the value of the count field. Flutter then calculates what widget’s need to be updated.

In this example you can also see that StatelessWidget's can contain StatefulWidgets and vice-versa.

7.2.3. Use a StatelessWidget instead of extending existing widgets

Sometimes it might seem intuitive to just extend a Button widget if you want to generalize a often used configuration. But in this case you should rather use a StatelessWidget and return a Button with the desired configuration.

8. Building User Interfaces

8.1. Important Widgets

This is an overview of some commonly used widgets and how to use them.

8.1.1. Scaffold

The scaffold is a good starting point for a new page. It sets various defaults (like theming), provides direct properties for a host of different child widgets and much more.

An example of a Scaffold could look like this:

Scaffold(
    appBar: AppBar(
        title: Text('App title')
    ),
    body: Text('A body'),
    floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add)
    ),
    bottomNavigationBar: BottomNavigationBar(
        items: []
    )
)
Table 1. Scaffold properties
Property Type Description

appBar

AppBar

An AppBar at the top of the screen

body

Widget

The content of your page

floatingActionButton

Widget

A round button, commonly located near the bottom right of the screen. Typically used as a contextual action button for frequently performed actions. It is often referred to as a FAB.

bottomNavigationBar

Widget

A bar of actions at the bottom of the screen. Typically, this is an omnipresent navigation bar that provides access to other pages of the app.

There are quite a few more settings related to the positioning, theming and other behaviour, which will be covered more in depth later.

As the scaffold shows the app bar and other structurally important parts of the app you typically want to do any dynamic loading inside of the body property instead of wrapping the scaffold directly.

8.1.2. Container

The container widget is a simple container that can receive one child. It can also have a background color and can be styled using its decoration property. As its height and width can also be set directly this can be used if you are certain about the constrains of the element.

Container(
    margin: EdgeInsets.all(10.0),
    child: Text('Text'),
    width: 150.0,
    height: 150.0,
    color: Colors.green
)

This would render a 150 by 150 pixel big, green box with the text "Text" displayed in it.

8.1.3. RaisedButton

Button with a background color. child contains the Widget for this button, frequently a Text widget. `onPressed: takes a method reference.

8.1.4. Flex, Column, Row

The widgets of the Flex family are widgets that allow putting multiple widgets next to each other on the same axis. The Column and Row widgets are just shorthands with the direction attribute of the Flex constructor set to Axis.vertical and Axis.horizontal respectively. So if you know in what direction the widget will layout its children, consider using the Column or Row directly as it is less verbose.

All of these widgets also take a <Widget>[…​] (list of Widget) as a constructor parameter. In here you can add all the widgets that should be displayed.

Column(
    children: <Widget>[
        Text('I will be above'),
        Text('I will be below'),
    ]
)

Or for the Row widget:

Row(
    children: <Widget>[
        Text('I will be left'),
        Text('I will be right'),
    ]
)
You can control the actual direction of the list by setting the mainAxisAlignment property.

8.1.5. ListView and ListTile

A ListView allows to show multiple widgets in a list. It takes care of overflowing widgets by automatically enabling scrolling if its children extend over the screen edge.

One drawback to keep in mind is that you have to know the height of a widget beforehand or else you will get exceptions as the ListView does not know how much space it should calculate. There are however solutions to this, as some specialized widgets take care of this for you.

8.2. Flavor specific widgets

Most components are available in both Cupertino (iOS) and Material (Android) styles. But of course it is up to you, which flavor you use and not dependent on the host OS. However, it is important to keep in mind, that users of the Android platform might expect Material style apps and iOS users Cupertino styles.

Conditional styling is possible, but it increases complexity in the app and depending on the size of the app might make UI-testing a lot harder. You can access the currently running host OS using Platform.isIOS and Platform.isAndroid.

9. Exercise: Build a simple interactive Flutter application

In this exercise you create a very simple Flutter app which can be extended later.

You will learn:

  • How to include another dart file into one dart file

  • How to implement a button and how to show a dialog

You will add a button to the application and show a dialog once this button is pressed.

For this create a new Flutter app, called helloflutter.

Open the 'lib/main.art' file and change it to the following.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Welcome to Flutter'),
        ),
        body: const Center(
          child: const Text('Hello World'),
        ),
      ),
    );
  }
}

Run this application and ensure that the text is displayed on the emulator.

In the 'lib' folder create a new file called 'lib.dart' with the following content.

import 'package:flutter/material.dart';

class MyHomePage extends StatelessWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

 @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            RaisedButton(
              child: Text('Press me'),
              onPressed: () {_ackAlert(context);} ),
          ],
        ),
      ),
    );
  }
}


Future<void> _ackAlert(BuildContext context) {
  return showDialog<void>(
    context: context,
    builder: (BuildContext context) {
      return AlertDialog(
        title: Text('Not in stock'),
        content: const Text('This item is no longer available'),
        actions: <Widget>[
          FlatButton(
            child: Text('Ok'),
            onPressed: () {
              Navigator.of(context).pop();
            },
          ),
        ],
      );
    },
  );
}

Now change the main.dart file to use your new StatelessWidget.

import 'package:flutter/material.dart';
import 'package:helloflutter/lib.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

9.1. Extend the application

Add an Image to the widgets in the body argument of MyHomePage.

To provide a new icon with your application, create an icons folder in your app. Search for an icon in the Internet and add it to this folder. You have to define such icons in your pubspec.yaml. Search for assets: in this file and declare you image.

 flutter:

  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  assets:
    - images/yourimagename.png

Afterwards assign it your your Icon widget via Image.assert('fullpathtothecion').

10. Exercise: Create the basic structure of a Flutter app

10.1. Create the application

Create a new Flutter application github_viewer. This application will be extended in later exercises and will be called the Github Viewer application.

10.2. Create the Homepage

The Widget defined via the home in your material app is the page the user sees when they start the app. Generally it should contain all necessary navigation elements to let the user navigate to other screens.

Create a new file page/homepage.dart in the lib directory. (Also create the page directory if it does not exist yet.)

Create the following class inside this new file.

import 'package:flutter/material.dart';

class Homepage extends StatefulWidget {
  @override
  _HomepageState createState() => _HomepageState();
}

class _HomepageState extends State<Homepage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('GitHub Issue Viewer'),
        actions: <Widget>[
          IconButton(
            onPressed: () {
              print('AppBar action pressed'); (1)
            }, (1)
            icon: Icon(Icons.settings),
          )
        ],
      ),
      body: Text('Welcome to the homepage'), (1)
    );
  }
}
1 These are placeholders which will be replaced in a later exercise

10.3. Adjust the main.dart file

Navigate back to the main.dart file and change it to the following.

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:github_viewer/page/homepage.dart';

void main() => runApp(App());

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Homepage(),
    );
  }
}

10.4. Start the application

To test this, start the app (if it’s not started). It should display the text Welcome to the homepage and have a blue app bar on top displaying GitHub Issue Viewer. On the right there should be a little "settings cog" and if pressed it should display the text AppBar action pressed in the console.

11. Exercise: Building your own Widget

We are going to continue to work on your Github Viewer application.

In this exercise you are going to build a reusable issue tile that represents an entry in the overview of issues in a repository. It builds upon previous exercises.

By now you should have the following file structure:

lib/
  page/
    homepage.dart
  main.dart

To make the development easier create the data model for the issue data. This is just a basic data classes that we will later use to fill it with data returned by the GitHub API.

Open the page/homepage.dart file and add the following class at the bottom of the file:

// ...
class Issue { (1)
  int number = 1;
  String state = 'open';
  String title = 'Issue title';
  String body = 'Issue body';
  String userName = 'userName';
  DateTime createdAt = DateTime.now();
}

class _IssueCard extends StatelessWidget {
  final Issue issue;

  _IssueCard(this.issue);

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.symmetric(vertical: 8.0),
        child: ListTile(
          title: Text('${issue.title} #${issue.number}'),
          subtitle: Text(
              '${issue.userName} opened on ${issue.createdAt}'),
          trailing: Icon(Icons.arrow_forward),
          onTap: () {
            print('Issue tile tapped');
          },
        ),
      ),
    );
  }
}
1 For now the data model is initialized with default data

To use the class go back to the _HomepageState class definition. Replace the body: Text(…​) line with the following content:

      body: ListView.builder((1)
        itemCount: 4,
        itemBuilder: (context, index) {
          return _IssueCard(Issue());
        },
      ),
1 A ListView.builder is a special constructor in the ListView class that calls the method that is passed to its itemBuilder parameter for the number off times passed to its itemCount parameter. In this case it will call the method 4 times. The current index is the second parameter to the method.

The _HomepageState class should now look like this:

class _HomepageState extends State<Homepage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('GitHub Issue Viewer'),
        actions: <Widget>[
          IconButton(
            onPressed: () {
              print('AppBar action pressed');
            },
            icon: Icon(Icons.settings),
          )
        ],
      ),
      body: ListView.builder(
        itemCount: 4,
        itemBuilder: (context, index) {
          return _IssueCard(Issue());
        },
      ),
    );
  }
}

12. Exercise build a list view

Perform the basis tutorial from Google to learn how to build a list with Flutter.

Follow the Google introduction tutorial located at https://codelabs.developers.google.com/codelabs/first-flutter-app-pt1/#0

13. Routing

Flutter supports navigation between different pages. This helps separating concerns, encourages encapsulation of code and thus makes the code base easier to read and maintain.

As everything in Flutter, a page is just a widget. It can either be a StatelessWidget or a StatefulWidget.

Navigation happens via the Navigator class. It has different functions available that allow for either named routes or just showing a page directly.

This works as follows:

Navigator.push(
        context,
        MaterialPageRoute(builder: (context) => Route()),
    );

With named routes, you pass a map of all routes to the routes parameter of the MaterialApp constructor. The keys of the map should be a string with the name of the route (typically these start with a /, similar to HTTP paths). The value of each key is a function that takes the context of the app as an argument and returns an instance of the widget of the page.

For a larger code base it is recommended to use a central map of all named routes. This way you can always change the class that is shown on a route in one single place instead of searching the code base for occurrences of the route. However sometimes it might be useful to just show a widget as a page.

Named routes may be pushed like this:

Navigator.pushNamed(context, '/issue')

Keep in mind though, that you have to register each route during instantiation of the app.

This could look like this:

MaterialApp(
        // ...
        routes: {
            '/': (context) => Homepage(),
            '/second': (context) => SecondPage(),
            // etc.
        },
        initialRoute: '/', (1)
    ),
1 The initial route is the first page the app shows when opening the app
As the app grows larger this part could become very crowded. One simple way to do this is, storing these routes and their builder functions in a separate file and expose them via a map. This is very common and you might see this in existing Flutter apps.

13.1. Passing data to a route

Sometimes you might want to pass data to a route. This could be a user id for showing their details or just something a user entered on the first screen.

For this, Navigator.pushNamed(…​) accepts an optional named argument called arguments. You may pass any Dart object to it but is is advisable to create a "wrapper" object that has the fields you want to pass for type safety.

Creating a wrapper object might look like this:

class RouteArguments {
    final int userId;
    final String message;

    RouteArguments(this.userId, this.message);
}

Passing the argument to the route:

Navigator.pushNamed(context, '/second', arguments: RouteArguments(8, 'This is a message'));

To access this data in a route, you will need to add it to the constructor of the widget.

@override
Widget build(BuildContext context) {
    // ...
    final RouteArguments args = ModalRoute.of(context).settings.arguments;
    // ...
    // And later you may acces its values directly
    int id = args.userId;
}
You can only receive these arguments where you have a BuildContext instance.

Dart automatically assigns the object to your RouteArguments type. But keep in mind that it can’t do so, if the object passed in was not of type RouteArguments. In this case it would throw an exception.

13.2. Handling unknown routes

In some cases you might want to generate the routes dynamically or you want to be safe and have an unknown route screen.

Luckily there is a onUnknownRoute handler in the MaterialApp constructor. This takes a function that returns a Route (e.g. a MaterialPageRoute or CuptertinoPageRoute). Its builder should return an instance of your page.

MaterialApp(
        // ...
        routes: ...,
        onUnknownRoute: (settings) {
            return MaterialPageRoute(builder: (context) => NotFoundPage());
        },
        initialRoute: '/',
    ),

14. Calling native code

The communication with Flutter and native code works via Channels.

Both the native components as well as the Flutter components have to use the same key for the channels to address them.

The channels needs to be registered on the Android activity.

private static final String BATTERY_CHANNEL = "samples.flutter.dev/battery";
// more code

//This goes to the end of the onCreate method

new MethodChannel(getFlutterView(), BATTERY_CHANNEL).setMethodCallHandler(
        new MethodCallHandler() {
          @Override
          public void onMethodCall(MethodCall call, Result result) {
            if (call.method.equals("getBatteryLevel")) {
              int batteryLevel = getBatteryLevel();

              if (batteryLevel != -1) {
                result.success(batteryLevel);
              } else {
                result.error("UNAVAILABLE", "Battery level not available.", null);
              }
            } else {
              result.notImplemented();
            }
          }
        }
    );
     +

And Flutter needs to call it.

class _MyHomePageState extends State<MyHomePage> {
  final logger = Logger();
  static const platform = const MethodChannel('samples.flutter.dev/battery');

  // Get battery level.
  // Get battery level.
  String _batteryLevel = 'Unknown battery level.';

  Future<void> _getBatteryLevel() async {
    String batteryLevel;
    try {
      final int result = await platform.invokeMethod('getBatteryLevel');
      batteryLevel = 'Battery level at $result % .';
    } on PlatformException catch (e) {
      batteryLevel = "Failed to get battery level: '${e.message}'.";
    }

    setState(() {
       logger.e("Called");
      _batteryLevel = batteryLevel;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Material(
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            RaisedButton(
              child: Text('Get Battery Level'),
              onPressed: _getBatteryLevel,
            ),
            Text(_batteryLevel),
          ],
        ),
      ),
    );
  }
}

15. Exercise: Calling native Android code

Execute the exercise from Google for integrating native code. For this exercise it is easier to do this step in Android Studio, as you are modifying also Java code and Java code assists is helpful for this step.

16. Links and Literature

17. vogella training and consulting support

Copyright © 2012-2019 vogella GmbH. Free use of the software examples is granted under the terms of the Eclipse Public License 2.0. This tutorial is published under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Germany license.