Building your first app in Flutter

Abhay Maurya
12 min readDec 13, 2021

--

and understanding why Flutter is so awesome!

In this blog, we will learn how to use Flutter to create your first real-world cross-platform compass app. So let’s dive into it! 🚀

Code for this blog — LiquidatorCoder/nord (github.com)

Our final compass app demo

Introduction to Flutter

Flutter is an open-source framework by Google for building beautiful, natively compiled, multi-platform applications from a single codebase. It is quite easy to set up and get running.

flutter. dev

To start let’s visit Install | Flutter and download Flutter for our operating system. I am using a Windows PC so I’ll select Windows and then download the latest Flutter SDK by clicking the blue button on the next page.

Select your OS
Download the latest Flutter SDK

Next, we follow the steps in the installation guide and update our environment path variables. Then, we run flutter doctor in the terminal to verify that the installation process of Flutter has been completed.

P.S.- I am not using the latest Flutter SDK at the moment 😅

Next, we install Android Studio by following the steps given here. This should only take a few minutes.

After that, we are finally ready to build our awesome compass app in Flutter.

Get inspiration

Before we get into the actual programming with Flutter, let’s take a look at some compass apps available online, shall we?

So, I quickly googled compass app UI and found some awesome frontend designs for our app.

Note — As this is a beginner tutorial, I would recommend you to start by following other people’s UIs as UI design is out of the scope of this tutorial.

Ok, this looks cool! -> Link
🤯 -> Link

Although, the second one is dope as hell (kudos to Offriginal) yet I decided to go with the first one as it is a little bit simpler and minimal (credits to Marc Merle).

Let’s Code

So, let’s start by creating a Flutter project using the Flutter create command. I am going to name this app nord as it sounds cool. Feel free to name it anything you like.

flutter create --org com.abhay nord

Also, a quick bonus tip -> I added the — org com.abhay parameter to the create command to add the organisation name in our starter app. This helps in generating a unique application Id for our app, as in this case com.abhay.nord .

Flutter is creating the project for us.

Next, we’ll open the project inside our IDE of choice, which in my case is VS Code. If you want to set up VS Code for your Flutter projects you may follow the steps mentioned here. We will also initialize git inside our project as it helps in maintaining the project.

cd nord
git init .
code . ; exit
Our project inside VS Code

Initial steps

Before we start coding, let’s take a look at the project directory structure.

├───.gitignore
├───.metadata
├───.packages
├───analysis_options.yaml
├───nord.iml
├───pubspec.lock
├───pubspec.yaml
├───README.md


├───android/

├───ios/

├───lib
│ main.dart

├───test
│ widget_test.dart

├───web/

└───windows/

By default, all our Dart code (which is, by the way, the programming language we’ll use while developing this app 😊) is inside the lib directory. The main starting point of our app is the main.dart file.

Since Flutter creates apps for Android, iOS, Web, Windows & more, the directories with the same name as the platforms contain the configuration code for the latter. For example, the android directory contains Kotlin code for the main activity, Gradle files, app icons and other app resources.

Now let’s run this starter project. Right now I am using a physical device (in this case my Android phone) to build and debug the app, but you can also use a virtual emulator. Connect your device to your PC and then use the Flutter run command to create a debug version of the app.

flutter run
Initial starter app

Next, we’ll remove all the comments inside the main.dart file. We’ll also remove the MyHomePage class and replace its instance inside the MyApp class with a Container (a basic widget provided by the Flutter framework).

Remove unnecessary code

Your app should just show a black screen now. Don’t worry, we will fix this soon.

But before that, we need to create some directories in our lib directory.

New directories inside lib directory.

These will help us separate our business logic from the UI of our app.

Now we can start working on our compass data service and controller, which will provide us with real-time compass data in our views.

Service & Controller

Let’s start by adding the flutter_compassplugin to our project which will help us get the compass data from the device sensors.

If you are using VS Code, then you can use the Command Palette to add the dependencies. To access the Command Palette press Ctrl+Shift+P on your keyboard. Alternatively, you can access it from the View menu.

Command Palette in VS Code

Here, we will use the Dart: Add Dependecy command to add the flutter_compass dependency.

After adding this we can see that our pubspec.yaml file got updated.

flutter_compass dependency in pubspec.yaml

To complete this plugin installation we need to add some permissions inside our AndroidManifest.xml file inside the main directory in our android project, as mentioned here.

Note — We will need to rebuild our project after this step so that these permissions can be added inside our app.

Added permissions

Next, we create a service for the compass data. To do this, we create a file called compass_service.dart in the services directory. Inside this file, we create a class called CompassService. This class will serve as the main data service for our compass data in our app.

We will also create a getter to retrieve the current compass data stream. If you are not familiar with getters and setters in Dart, you should check out this blog.

import 'package:flutter_compass/flutter_compass.dart';class CompassService {
// Getter to get current heading data stream
Stream<double?>? get compassHeadingStream {
return FlutterCompass.events?.map((event) => event.heading);
}
}
CompassService Class

Next, we will add a getter for the compass direction, which tells us the direction the compass is pointing.

// Getter to get current heading direction stream
Stream<String>? get compassDirectionStream {
return FlutterCompass.events?.map((event) {
final heading = event.heading ?? 0;
if (heading >= 0 && heading <= 22.5 && heading >= 337.5) {
return 'N';
} else if (heading > 22.5 && heading <= 67.5) {
return 'NE';
} else if (heading > 67.5 && heading <= 112.5) {
return 'E';
} else if (heading > 112.5 && heading <= 157.5) {
return 'SE';
} else if (heading > 157.5 && heading <= 202.5) {
return 'S';
} else if (heading > 202.5 && heading <= 247.5) {
return 'SW';
} else if (heading > 247.5 && heading <= 292.5) {
return 'W';
} else if (heading > 292.5 && heading <= 337.5) {
return 'NW';
} else {
return 'N';
}
});
}
CompassService class with all getters

I also did some refactoring in the class to clean it up a bit.

Final CompassService class

Now we will create a controller that will provide the data to the views. To create this controller, we create a file called compass_controller.dart in the controllers directory. In this file, we create a class called CompassController. We also add the ChangeNotifier mixin to our class with the keyword “with”. To learn more about mixins and the “with” keyword, visit this page. We added the ChangeNotifier mixin so we can use the notifiyListeners method to update our view when data changes.

Don’t worry if you do not know much about these terms or state management, but remember that this not only helps keep our code clean and maintainable, but also reduces the risk of unnecessary builds or memory leaks.

CompassController class

Next, We’ll expose the data from the service inside the controller by listening to the streams inside the class constructor.

Exposing data

We also add a dispose method and cancel these stream subscriptions to free the used resources after we finish listening to the stream.

This dispose method also overrides the method of the same name from the ChangeNotifier mixin.

Final CompassController class

Finally, we can start working on the views (screens) of our app.

Views

Note — Since further steps would require a significant amount of code, I recommend opening the code files of this project on GitHub from here and following the code commit by commit.

So, first of all, let’s create a homepage. Create a new file home_page.dart inside pages directory and then add a stateless widget HomePage in it.

For more information on Widgets — Stateless or Stateful visit this page.

HomePage Widget

In the build method of this widget, we will return a scaffold widget for now. Next, we will add this widget to the build method of the MyApp Stateless widget so we can start working on the home page.

Adding HomePage in main.dart

Let us start by adding a background to our scaffold. We will add a radial gradient whose colours we have chosen from the UI design we selected earlier.

Code for the background gradient

Next, we want to show our compass readings in the centre of the screen.

For this, we add the provider package to use the values from the controller to update the view. If you want to learn more about provider, I recommend watching this amazing video from Code with Andrea.

Add the provider package the same way we added the flutter_compass plugin.

Provider package in pubspec.yaml

Now we add the MultiProvider widget in the runApp method of the main.dart file. This ensures that the controller is available in our widget tree.

Multiprovider widget with ChangeNotifierProvider

Next, we will use this controller to get our data in home_page.dart. For this, we will first get an instance of CompassController .

For that, add the following line inside the build method of the HomePage widget.

final CompassController _compassController = context.watch<CompassController>();

Then, we can use this _compassController to get data values and we can display them using a Text widget.

Text('${_compassController.compassHeading} ${_compassController.compassDirection}',
),
Build method for HomePage widget
Our app till now

Woohoo! 🎉 We now have a working app!

But it doesn’t look good 🤔 . Let’s fix that.

First, let us add the compass body to our page. To do this, I added a bunch of SVG images (link) and a background using CustomPainter , which is (you guessed it) a widget. I will not go into detail about this CustomPainter widget as it’s beyond the scope of this tutorial, but you can read more about CustomPainter here and also check out the source code for this project on GitHub.

Note — To use the SVG files, I added the flutter_svg package and added these SVG files as constant strings code, which can be interpreted by the flutter_svg package. More info — here.

After adding these vector images the UI looks like this.

Our app after adding images

Update — I swapped these SVGs with PNGs, as some of them required SVG Filters, which are currently not supported by flutter_svg . To add PNG images, we first need to add them in our pubspec.yaml file as assets and then we can use Image.asset widget to use them in our app. More info — here.

Ok, this looks good, but the app still does not feel interactive, as currently only the directional text is updated. I think the dial should also rotate and react to the change in the compass direction. Let us add this cool interaction with a bonus animation.

For this, we’ll add AnimatedRotation widget as a parent to our compass dial. This widget takes turns and duration as required parameters. turns allow us to specify the current rotation and duration takes the amount of duration the animation should last.

AnimatedRotation widget

Note — The turns parameter takes the current rotation as a number of turns, so we’ll divide the compassHeading by 360. Also, the ?? is “if null” operator in dart, which we used to specify the turns when the compassHeading is null. The _screenHeight and _screenWidth are just used to specify the width and height as a fraction of device screen size. More info — here.

Finished App! 🎉

Voilà! Our app is ready!

Building release apps

To create a final android app (that we can run on our android devices), run the following command.

flutter build apk --split-per-abi

This generates multiple apk files for our app. Read more here.

Generating final apk files

We can also build a “fat apk” that supports all Android architectures but is significantly large in size by running —

flutter build apk

To build an android appbundle run the following command.

flutter build appbundle

To build iOS and macOS apps we need to have flutter installed on a mac device.

To build for iOS, run —

flutter build ipa

Note — As the flutter_compass plugin only supports Android and iOS, the builds for other platforms won’t run properly and wouldn’t show the compass reading.

To create a windows app run flutter build windows
To build for macOS, run flutter build macos

Note — You need to enable desktop support for Flutter by first running flutter config --enable-windows-desktop , and flutter config --enable-macos-desktop then only the above commands would run. Read more here.

Code of this app on GitHub — https://github.com/LiquidatorCoder/nord

Why Flutter is so awesome?

So finally we know it. Flutter is awesome because we were able to build a cross-platform, real-world app with a maintainable and scalable structure within a day, if not hours. We were able to add several features to our app with the help of some great “pub.dev” packages from the Flutter community. The app runs with near-native performance and we do not have to worry about long load times.

That’s a wrap!

That’s it! Thanks for reading. Don’t forget to follow me on Twitter or GitHub for more awesome Flutter tips and tricks.

See ya! 👋🏻

Twitter — Abhay Maurya 💙 (@LiquidatorAB) / Twitter
GitHub — LiquidatorCoder (Abhay Maurya) (github.com)

--

--

Abhay Maurya
Abhay Maurya

Written by Abhay Maurya

Building better apps using Flutter & Dart.

No responses yet