Flutter

Gradle Exception and setting the Flutter SDK path

Flutter

When setting up Firebase to use with a Flutter app you will need to find your apps application id and get an SHA1 certificate. During this process you will need to open the build.gradle file of your Flutter project in Android Studio and you may need to find and set the flutter SDK path. You may encounter some problems if you haven’t done this before, and so I will cover a few of those issues, and how to deal with them in this post.

If you haven’t done this before open your app project in Android Studio. If you see a yellow message across the top of the main windows about installing the Flutter Extension, then go ahead and install it, you’ll also need to restart Android Studio.

Once you’ve opened your Gradle file you may notice a Gradle Exception in it saying something like “Gradle exception flutter SDK not found”. To fix this, read on.

Android Studio settings

  1. Press Ctrl + Alt + S when you are in Android Studio, and the Settings Window will open
  2. Select Languages & Frameworks > Flutter and enter the Flutter SDK path. Read below to find out how to find out the Flutter SDK path.
setting the flutter sdk path in Android Studio

How to find the Flutter SDK path using Flutter doctor

This is the place that you unzipped the Flutter SDK download zip to. The best way to find it is to open your Terminal / Command / Git Bash window and type:

 flutter doctor - v

In the first few lines of the output returned will be a line starting Flutter version x.x.x and then a folder path – that is the path you want to copy, and then enter in the Flutter SDK path textbox in the Settings Window above in Android Studio. Click OK to save that and the Settings window will close – that’s how you change the Flutter SDK path in Android Studio.

If you can’t see anything called Tasks in the Gradle window, open settings in Android Studio again, but this time select Experimental Features from the left hand list and then uncheck Do not build Gradle task list during Gradle sync. Click OK and then sync the project.

The exceptions should disappear and if you open Android Studio settings the SDK path should now be visible as shown in this screenshot, with the Flutter SDK version appearing after a moment.

change flutter sdk path in android studio

Now that you have finished setting up the SDK path, and if you are new to Flutter you may find this getting started with Flutter post handy.

Flutter bottom navigation bar example

Flutter

After searching google for examples of Flutter navigation, more specifically the bottom navigation bar I was a bit stumped. Several of the examples I found would show the bottom navigation bar on one page but not persist it across all pages, and that wasn’t what I was after. Eventually I stumbled upon this excellent article by Andrea Bizzotto not only showing me how to persist the bottom navigation bar on every page, but also how to combine this with a stack navigator on each page – fantastic!

After working through his bottom navigation example and getting it working for me locally, I started to try and work out how to adapt this to build an app I had in mind (hopefully I’ll be posting about the launch of that soon!). It would need a bottom navigation bar, a stack navigator on at least one of those pages, but then a single page on at least one of the others. So I adapted Andrea’s code to do just that and you can find it here on github.

If you want to understand the changes I had to make then here are the code changes step by step – I started by forking his repo but found it wouldn’t run and I had to add this to pubspec.yaml:

environment:  sdk: ">=2.7.0 <3.0.0"

In order to update this code to handle some pages with a stack and some as a standalone page I made the following changes.

Combine a tab navigator with the bottom navigation bar

First of all a new property needed to be added to the TabNavigator class in tab_navigator.dart, so that as well as the navigatorKey and the tabItem properties, there’s a new one called stacked, which is a boolean. So the top of the TabNavigator class now looked like this:

class TabNavigator extends StatelessWidget {
  TabNavigator({this.navigatorKey, this.tabItem, this.stacked});
  final GlobalKey<NavigatorState> navigatorKey;
  final TabItem tabItem;
  final bool stacked;

  void _push(BuildContext context, {int materialIndex: 500}) {
    var routeBuilders = _routeBuilders(context, materialIndex: materialIndex);
    ....
    ....

The build method in tab_navigator.dart also needed to be updated so that if a page is a ‘stacked’ page, the routeBuilders method is still called, but if not, the SinglePage is returned.

@override
Widget build(BuildContext context) {
    final routeBuilders = _routeBuilders(context);
    return Navigator(
        key: navigatorKey,
        initialRoute: TabNavigatorRoutes.root,
        onGenerateRoute: (routeSettings) {
          if (stacked) {
            return MaterialPageRoute(
              builder: (context) {
                return routeBuilders[routeSettings.name](context);
              },
            );
          }

          return MaterialPageRoute(
            builder: (context) => SinglePage(
              color: activeTabColor[tabItem],
              title: tabName[tabItem],
              materialIndex: 0,
            ),
          );
        });
  }

Add new file single_page.dart containing this code:

import 'package:flutter/material.dart';

class SinglePage extends StatelessWidget {
  SinglePage({this.color, this.title, this.materialIndex: 500});
  final MaterialColor color;
  final String title;
  final int materialIndex;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: color,
        title: Text(
          '$title[$materialIndex]',
        ),
      ),
      body: Container(
        color: color[materialIndex],
        child: Center(
            child: Text(
          "I am a single page.",
          style: TextStyle(fontWeight: FontWeight.bold, fontSize: 28),
        )),
      ),
    );
  }
}

Finally add a reference to that page in tab_navigator.dart:

import 'package:nested_navigation_demo_flutter/single_page.dart';

Next I needed to modify app.dart, to identify which page would be a single page, and which would be stacked.

Add new property to the _buildOffstageNavigator method, so that we can pass in whether each page has a stack or not.

Widget _buildOffstageNavigator(TabItem tabItem, bool isStacked) {
    return Offstage(
      offstage: _currentTab != tabItem,
      child: TabNavigator(
        navigatorKey: _navigatorKeys[tabItem],
        tabItem: tabItem,
        stacked: isStacked,
      ),
    );
  }

Once that change had been made I had to change the code where the _buildOffstageNavigator method is called to include the new boolean property. This is in the build method of Appstate.dart

....
child: Scaffold(
        body: Stack(children: <Widget>[
          _buildOffstageNavigator(TabItem.red, true),
          _buildOffstageNavigator(TabItem.green, false),
          _buildOffstageNavigator(TabItem.blue, true),
        ]),
        bottomNavigationBar: BottomNavigation(
          currentTab: _currentTab,
          onSelectTab: _selectTab,
        ),
      ),
        
        .......

Once all of those changes are made you should have an app that works like you can see in this video, with a Flutter bottom navigation bar on every page, with two of the main pages showing stack navigation and one just a single page.

So that’s a fully working Flutter bottom navigation bar example. You may also be interested in a blog post I wrote recently about my experience installing and setting up Flutter on my PC, be sure to read it if you are just starting out.

Getting started with the Flutter app example on Windows

Apps, Flutter

I started developing mobile apps with Xamarin, and since then have also built mobile apps using React Native and Expo. I’ve been hearing more and more about Flutter and recently had a use case while building an app that couldn’t be done in Expo without ejecting to bare React Native, and also couldn’t easily be achieved in Xamarin. With that in mind I decided to get started with Flutter, and got stuck into building the Flutter app example provided on the main Flutter website, and learning about Flutter Doctor. I develop primarily on a PC so this post is focused on installing and using Flutter on a Windows PC. Up until now I really only use my Mac when needing to test an app on an iOS simulator.

My first impression is that the documentation for Flutter is excellent, certainly as far as Flutter install and setup goes, I started here – https://flutter.dev/docs/get-started/install/windows and that covered everything to get me started quickly. I’m using Visual Studio Code as I’m already used to it, but you can also use Android Studio or EMACS, just bear in mind that whichever one you use you will always have to have Android Studio installed as it’s that that provides the Android platform dependencies.

Flutter doctor Android licenses

I followed the instructions to install Flutter, the only glitch I experienced was that I had to accept a load of Android agreements to complete the installation, but that process was handled seamlessly in the terminal window. It asked me to run this command, which I did and then all was ok:

Run flutter doctor --android-licenses to accept the SDK licenses

If you get a Java error :

Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/annotation/XmlSchema
         at com.android.repository.api.SchemaModule$SchemaModuleVersion.(SchemaModule.java:156)
         at com.android.repository.api.SchemaModule.(SchemaModule.java:75)
         at com.android.sdklib.repository.AndroidSdkHandler.(AndroidSdkHandler.java:81)
         at com.android.sdklib.tool.sdkmanager.SdkManagerCli.main(SdkManagerCli.java:73)
         at com.android.sdklib.tool.sdkmanager.SdkManagerCli.main(SdkManagerCli.java:48)
 Caused by: java.lang.ClassNotFoundException: javax.xml.bind.annotation.XmlSchema
         at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
         at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
         at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522) 

… after running this command then install the Android SDK command line tools from the Android SDK manager in Android Studio – details are in this thread.

However I also installed Flutter on another Windows PC and had a few more issues. This PC had an old install of Android Studio so I was starting from a different set up. Flutter Doctor kept telling me that Android Studio wasn’t installed even though it was. To fix this problem I ran these two commands in Git bash, the first one set the android studio path to nothing, the second one set it to the correct path::

$ flutter config --android-studio-dir=""
$ flutter config --android-studio-dir="your-path-to-android-studio"

That resolved that issue but then after running Flutter Doctor it looked like everything was ok as it asked me to run another command to view Android licenses. However when I ran that command I got this error:

“could not find or load main class flutter doctor”

I did some Googling and discovered a few people saying that a space in the path could be the problem, so deleted my Android SDK install, and reinstalled it from the SDK Manager in Android Studio to a path with no spaces. Then ran this command:

$ flutter config --android-sdk "your-new-path-to-android-sdk"

Overall my experience of installing Flutter and setting it up was very positive. A very smooth process and a tool (Flutter Doctor) which is on hand from the command line to check your install and help you correct any issues, and there’s an example Flutter app which you can create and run from the command line getting you up and running with a simple app in less than an hour – https://flutter.dev/docs/get-started/codelab.

Android Emulators – adb.exe

My Android emulators had been set up on my PC a while back though and it seems like something needed to be updated as every time I tried to start one I saw this message:

The ADB binary found at C:\Users\echristy\AppData\Local\Android\sdk\platform-tools\adb.exe is obsolete and has serious performance problems with the Android Emulator. Please update to a newer version to get significantly faster app/file transfer.

To solve this problem I followed these steps and it resolved the problem for me:

  1. Open Android Studio
  2. Open SDK Manager
  3. Click the middle Tab which is labelled SDK Tools
  4. Check the Show Package Details checkbox which is in the lower right hand corner
  5. Click on the SDK Build Tools item to expand it.
  6. Check the newest version (highest number) that does not contain the string ‘rc’ after it’s name
  7. Uncheck all older versions (lower numbers)
  8. Click Apply and you’ll see a dialog like this:
  1. Click OK
  2. Once it’s finished, close your emulator and reopen it.
  3. Now try running your app again and the error message should have gone.

Building the Flutter App example

I continued following the Get Started guide on the Flutter website and built another app, and again the process seems very quick and easy. However one thing I’ve noticed straight away is my lack of opportunity to run this app on an iOS device. Having started with Xamarin years ago I could view my app on my physical iOS device as long as my Mac was on the same network as my PC, and set up correctly. More recently I’ve built React Native Expo apps on my PC, and using Expo, and the Expo Go client app on my iPhone, I could scan a QR code and view my app on the phone. So far on Flutter I feel that restriction of only being able to view my app on an Android emulator or device. I believe I have to jump on to my Mac entirely to test on a device. So for now testing my Flutter app on my iPhone looks tricky – which is the only negative I’ve found so far, but for me, a significant one.

A couple of real basics it’s worth mentioning – the main source code files of a Flutter app, or at least the one’s you’ll want to start editing first, are all in the lib folder – main.dart is the file you’ll want to find to start editing in the app that is created after running the Create New Flutter Project command in the IDE. Secondly you don’t need to worry about having any emulators set up to start with as any Flutter mobile app will also run in Chrome.

Overall though I really like Flutter so far – I’ve also written a post about building a persistent bottom navigation bar in a Flutter app.

Why not try some Flutter training

On Udemy there are courses starting from $19.99.