How To Create A Mobile App Using Flutter For An Existing Web Application

The steps below use Flutter's WebView to create a mobile app from an existing web application.

1. System requirements

Flutter depends on one of these tools being available in your OS environment.

2. Install Flutter

1. Download flutter here: https://flutter.dev/docs/get-started/install

2. Extract the zip file and place the contained flutter directory iton the desired installation location for the Flutter SDK (for example, C:\flutter – do not install Flutter in a directory like C:\Program Files\ that requires elevated privileges).

3. Add Flutter to the PATH environment variable:
From the Start search bar, enter env and select Edit environment variables for your account. Under User variables check if there is an entry called Path:

  • If the entry exists, append (add to the end) the full path to flutter\bin using ; as a separator from existing values (for example, C:\flutter\flutter\bin).
  • If the entry doesn’t exist, create a new user variable named Path with the full path to flutter\bin as its value.

Important: Note that you have to close and reopen any existing console windows for these changes to take effect.

3. Set up an editor

Android Studio System Requirements (Windows)

  • 64-bit Microsoft® Windows® 8/10
  • x86_64 CPU architecture; 2nd generation Intel Core or newer, or AMD CPU with support for a Windows Hypervisor
  • 8 GB RAM or more
  • 8 GB of available disk space minimum (IDE + Android SDK + Android Emulator)
  • 1280 x 800 minimum screen resolution

Android Studio System Requirements (Linux)

  • Any 64-bit Linux distribution that supports Gnome, KDE, or Unity DE; GNU C Library (glibc) 2.31 or later.
  • x86_64 CPU architecture; 2nd generation Intel Core or newer, or AMD processor with support for AMD Virtualization (AMD-V) and SSSE3
  • 8 GB RAM or more
  • 8 GB of available disk space minimum (IDE + Android SDK + Android Emulator)
  • 1280 x 800 minimum screen resolution

1. To install Android Studio (recommended), go here: https://developer.android.com/studio

Alternatively, you can install Visual Studio Code here: https://code.visualstudio.com/download

In Android Studio, install the Flutter and Dart plugins for Linux or Windows:
a. Open plugin preferences (File > Settings > Plugins).
b. Select Marketplace, select the Flutter plugin and click Install.
c. Select the Dart plugin and click Install.

2. Run Flutter Doctor

From a console window that has the Flutter directory in the path (see above), run the following command to see if there are any platform dependencies you need to complete the setup:

$ cd C:\flutter
$ flutter doctor

You should see output similar to (the output below assumes that you installed Android Studio and VS Code):

Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 2.5.3, on Microsoft Windows [Version 6.3.9600], locale en-US)
[√] Android toolchain - develop for Android devices (Android SDK version 31.0.0)
[√] Chrome - develop for the web
[√] Android Studio (version 2020.3)
[√] VS Code (version 1.61.2)
[√] Connected device (1 available)

• No issues found!

You should see output with No issues found!:

If there are any issues, go here: https://flutter.dev/docs/get-started/install/windows

4. Create the mobile app in Android Studio

1. Open Android Studio and select New Flutter Project.

2. Verify the Flutter SDK path is set to the correct directory you set up when installing Flutter (ex: C:\flutte\flutter) and click Next

3. Enter the Project name and Project location (defaults to ...\AndroidStudioProjects\projectname). The Project type should be set to Application and fill in the Organization field (ex com.yourdomain). You can leave the defaults for the other settings (Android language is set to Kotlin and Platforms are set to Android and iOS). Click Finish.

Note: When creating a new Flutter app, Android Studio asks for a company domain name in reverse order, something like com.example. The company domain name and project name are used together as the package name for Android (the Bundle ID for iOS) when the app is released. If you think that the app might be released, it’s better to specify the package name now. The package name can’t be changed once the app is released, so make the name unique.

5. Adding WebView for Flutter

1. Open the pubspec.yaml file (double-click) located in the project root. Under dependencies > flutter, add the webview_flutter line of code like below (pay attention to the formatting):

dependencies:
  flutter:
    sdk: flutter
    
  webview_flutter: ^2.3.0

Then, click Pub get located at the upper right hand side of the screen.

2. Open main.dart located at lib > main.dart and replace all the code with the code below. Modify the following lines:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'dart:io';

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(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        colorScheme: ColorScheme(
          brightness: Brightness.light,
          surface: Colors.white,
          onSurface: Colors.blue,
          // Colors that are not relevant to AppBar in DARK mode:
          primary: Colors.blue,
          onPrimary: Colors.white,
          primaryVariant: Colors.grey,
          secondary: Colors.grey,
          secondaryVariant: Colors.grey,
          onSecondary: Colors.grey,
          background: Colors.grey,
          onBackground: Colors.grey,
          error: Colors.grey,
          onError: Colors.grey,
        ),
      ),
      home: Body(),
    );
  }
}

class Body extends StatefulWidget {
  @override
  _BodyState createState() => _BodyState();
}

class _BodyState extends State<Body> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Top Navigation Text'),
      ),
      body: WebView(
        initialUrl: 'https://yourdomain.com',
        javascriptMode: JavascriptMode.unrestricted,
      ),
    );
  }
}

3. Add the code below to the bottom of AndroidManifest.xml located at android > app > src > main > AndroidManifest.xml – After </application> and before the last line </manifest>:

...
    </application>
    <uses-permission android:name="android.permission.INTERNET" />
</manifest>

4. Modify the minSdkVersion (WebView requires an Android SKD version of at least 19). Open build.gradle located at android > app > build.gradle, scroll down to the defaulConfig section and change the minSdkVersion to 19 (line 47)

defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.yourdomain.yourdomain"
        minSdkVersion 19
        targetSdkVersion 30
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }

6. Run the app

On the Android Studio toolbar, select an Android device for running the app in the Target selector (https://flutter.dev/docs/get-started/test-drive). To create and manage virtual devices, see https://developer.android.com/studio/run/managing-avds. Make sure that main.dart is displayed in the Config selector, and click the run icon (or menu itme Run > Run.

Once the Android simulator pops up you should see the URL of the web app/website you configured above. When I first tried to find a simple way to do this I came across a constant stream of errors in Android Studio that I had to find solutions too. Once Android Studio and especially gradle were set correctly, the code ran smoothly. So good luck and see the bottom of this page for some errors and solutions.

To build a test apk to view on your mobile device, from the menu select Build > Flutter > Build APK. When complete, the APK will be located in your project folder inside build > app > outputs > flutter-apk.

7. Add a splash screen

1. Open the pubspec.yaml file located in the project root.

2. Under dependencies > flutter, add the flutter_native_splash line of code like below (pay attention to the formatting):

dependencies:
  flutter:
    sdk: flutter
    
  webview_flutter: ^2.3.0
  flutter_native_splash: ^1.3.1

3. Next, inside the same pubspec.yaml file, add this code to the bottom:

...

flutter_native_splash:
  # color or background_image is the only required parameter.  Use color to set the background
  # of your splash screen to a solid color.  Use background_image to set the background of your
  # splash screen to a png image.  This is useful for gradients. The image will be stretch to the
  # size of the app. Only one parameter can be used, color and background_image cannot both be set.
  color: "#1e73be"
  #background_image: "assets/background.png"

  # Optional parameters are listed below.  To enable a parameter, uncomment the line by removing
  # the leading # character.

  # The image parameter allows you to specify an image used in the splash screen.  It must be a
  # png file and should be sized for 4x pixel density.
  image: assets/splash.png

  # The color_dark, background_image_dark, and image_dark are parameters that set the background
  # and image when the device is in dark mode. If they are not specified, the app will use the
  # parameters from above. If the image_dark parameter is specified, color_dark or
  # background_image_dark must be specified.  color_dark and background_image_dark cannot both be
  # set.
  #color_dark: "#042a49"
  #background_image_dark: "assets/dark-background.png"
  #image_dark: assets/splash-invert.png

To customize the splash screen, see the comments located in the code. I found that a splash screen image is best displayed when using the image parameter rather than the background_image parameter as it fits the image to the screen better. This also means that you can use both the background color parameter and the image parameter. Note that when using a splash screen image, you must create a folder in the root of the project named assets and place the image(s) (.png) in it.

After adding your settings, run the following commands in the Android Studio terminal (located at the bottom left hand corner):

flutter pub get
flutter pub run flutter_native_splash:create

When the package finishes running, your splash screen is ready.

Note: The splash screen may not appear when you launch the app from Android Studio in the simulator. However, it should appear when you launch by clicking on the launch icon in your Android device. For more info and a recommendation on why and how to add a secondary splash screen, see https://pub.dev/packages/flutter_native_splash.

8. Changing the app's launcher icon

1. Open the pubspec.yaml file and add the code after the code you already added:

...

dependencies:
  flutter:
    sdk: flutter

  webview_flutter: ^2.3.0
  flutter_native_splash: ^1.3.1
  flutter_launcher_icons: "^0.9.2"

flutter_icons:
  android: "launcher_icon"
  ios: true
  image_path: "assets/icon/icon.png"
  
  ...

2. Create a directory named icon and add an image (a .png with a recommended size of 1024×1024)

3. Run the commands below in the Android Studio terminal:

flutter pub get
flutter pub run flutter_launcher_icons:main

In the above configuration, the package is setup to replace the existing launcher icons in both the Android and iOS project with the icon located in the image path specified above and given the name “launcher_icon” in the Android project and “Example-Icon” in the iOS project. For more attribues that can be configured, see https://pub.dev/packages/flutter_launcher_icons.

9. Deploying the app

1. Create an upload keystore. Modify the location where the upload-keystore.jks file will be created.

Run the following command (substituting yourkeystorename for whatever name you want):

keytool -genkey -v -keystore yourkeystorename.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias upload

When prompted, enter a keystore, key password and other information requested. This command stores the .jks file in your home directory (c:\Users\USER_NAME\). Copy this .jks file to your project folder inside /android/app/. Keep the keystore file private; don’t check it into public source control!

If you get the following output bash: keytool: command not found when trying to run the command above then the keytool command might not be in your path -it’s part of Java, which is installed as part of Android Studio. To get the path, run flutter doctor -v from the command line and locate the path printed after Java binary at: (example, Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java).

Next, from the Start search bar, enter env and select Edit the system environment variables. Then click Environmental Variables at the bottom of the prompt. Under System variables, locate the Variable Path and click Edit. Append the full path to the Java binary we found above using ; as a separator from existing values without \java at the end (example, ;C:\Program Files\Android\Android Studio\jre\bin).

Close and reopen any existing console windows for these changes to take effect and the rerun the keytool command from above.

2. Create a file inside the android directory of your project folder named key.properties that contains a reference to your keystore. Add the code below filling in the necessary information (leave keyAlias=upload as it is):

storePassword=<password from previous step>
keyPassword=<password from previous step>
keyAlias=upload
storeFile=yourkeystorename.jks

Warning: Keep the key.properties file private; don’t check it into public source control.

3. Configure gradle to use your upload key when building your app in release mode by editing the /android/app/build.gradle file.

Add the keystore information from your properties file before the android block:

def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
   }

4. Find the buildTypes block:

buildTypes {
    release {
        // TODO: Add your own signing config for the release build.
        // Signing with the debug keys for now,
        // so `flutter run --release` works.
        signingConfig signingConfigs.debug
    }
}

and replace with:

signingConfigs {
   release {
	   keyAlias keystoreProperties['keyAlias']
	   keyPassword keystoreProperties['keyPassword']
	   storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
	   storePassword keystoreProperties['storePassword']
   }
}
buildTypes {
   release {
	   signingConfig signingConfigs.release
   }
}

5. Reviewing the app manifest

Review the default App Manifest file, AndroidManifest.xml, located in /android/app/src/main and verify that the values are correct, especially the following:

application
Edit the android:label in the application tag to reflect the final name of the app.

6. Reviewing the build configuration

Review the default Gradle build file, build.gradle, located in /android/app and verify the values are correct, especially the following values in the defaultConfig block:

applicationId
Specify the final, unique (Application Id) appid

versionCode & versionName
Specify the internal app version number, and the version number display string. You can do this by setting the version property in the pubspec.yaml file.

7. Building the app for release

You have two possible release formats when publishing to the Play Store.

  • App bundle (The Google Play Store prefers the app bundle format)
  • APK

From the Android Studio menu, select Build > Flutter > Build App Bundle

or from the command line, change into your project folder and build:

cd yourprojectfolder
flutter build appbundle

(Running flutter build defaults to a release build.)

The release bundle for your app is created at /build/app/outputs/bundle/release/app-release.aab.

By default, the app bundle contains your Dart code and the Flutter runtime compiled for armeabi-v7a (ARM 32-bit), arm64-v8a (ARM 64-bit), and x86-64 (x86 64-bit)

To build the APK (where you quickly download and test on your mobile device), from the Android Studio menu select Build > Flutter > Build APK. The APK for your app is created at /build/app/outputs/flutter-apk/app-release.apk.

8. Test the app bundle

An app bundle can be tested in multiple ways – this section describes two.

A. Offline using the bundle tool

B. Online using Google Play

  • Upload your bundle to Google Play to test it. You can use the internal test track, or the alpha or beta channels to test the bundle before releasing it in production.
  • Follow these steps to upload your bundle to the Play Store

IMPORTANT: When you upload the app bundle to Google Play, you may see the error:

Warning
This App Bundle contains native code, and you've not uploaded debug symbols. We recommend you upload a symbol file to make your crashes and ANRs easier to analyze and debug.

1. Install the NDK and CMake in Android Studio:

  • With a project open, click Tools > SDK Manager.
  • Click the SDK Tools tab.
  • Select the NDK (Side by side) and CMake checkboxes and click OK.

2. Once they both are installed, open the file build.gradle (android/app/build.gradle) and add this line to the bottom:

...
android.buildTypes.release.ndk.debugSymbolLevel = 'FULL'

3. Open the file local.properties and add the path to NDK – making sure to verify the ndk version (substitute USER_NAME in both sdk.dir and ndk.dir as necessary):

sdk.dir=C:\\Users\\USER_NAME\\AppData\\Local\\Android\\sdk
flutter.sdk=C:\\flutter\\flutter
flutter.buildMode=release
flutter.versionName=1.0.0
flutter.versionCode=1
ndk.dir=C:\\Users\\USER_NAME\\AppData\\Local\\Android\\sdk\\ndk\\23.1.7779620

Now build and upload the app bundle again and the error message is gone.

REFERENCES

https://flutter.dev/docs/get-started/install/windows
https://flutter.dev/docs/get-started/install
https://flutter.dev/docs/get-started/editor
https://flutter.dev/docs/get-started/test-drive
https://pub.dev/packages/webview_flutter/
https://pub.dev/documentation/webview_flutter/latest/webview_flutter/WebView-class.html
https://developer.android.com/studio
https://developer.android.com/guide/webapps
https://developer.android.com/reference/android/webkit/WebView
https://blog.logrocket.com/render-webpages-using-flutter-webview/
https://github.com/flutter-prjs/webviewprj
https://stackoverflow.com/questions/51740339/how-can-we-change-appbar-background-color-in-flutter
https://pub.dev/packages/flutter_native_splash
https://stackoverflow.com/questions/43879103/adding-a-splash-screen-to-flutter-apps
https://pub.dev/packages/flutter_launcher_icons
https://github.com/fluttercommunity/flutter_launcher_icons
https://flutter.dev/docs/deployment/android
https://developer.android.com/studio/publish/app-signing#sign-apk
https://codewithandrea.com/articles/keytool-command-not-found-how-to-fix-windows-macos/
https://developer.android.com/studio/build/shrink-code#native-crash-support
https://github.com/flutter/flutter/issues/60240
https://stackoverflow.com/questions/62568757/playstore-error-app-bundle-contains-native-code-and-youve-not-uploaded-debug
https://stackoverflow.com/questions/63373245/how-to-add-debug-symbols-to-build-gradle/63436935#63436935
https://developer.android.com/studio/projects/install-ndk

2 thoughts on “How To Create A Mobile App Using Flutter For An Existing Web Application

  1. Good day,

    Can you help me with the following errors
    I don’t know what they mean or how to correct them.

    The named parameter ‘primaryVariant’ isn’t defined.
    The named parameter ‘secondaryVariant’ isn’t defined.
    The constructor being called isn’t a const constructor.

    Thank you in advance for your help.

    Kind regards

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.