Building your first app in Flutter
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)
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.
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.
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.
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.
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
.
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
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
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).
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.
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_compass
plugin 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.
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.
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.
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);
}
}
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';
}
});
}
I also did some refactoring in the class to clean it up a bit.
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.
Next, We’ll expose the data from the service inside the controller by listening to the streams inside the class constructor.
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.
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.
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.
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.
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.
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.
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}',
),
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 theflutter_svg
package. More info — here.
After adding these vector images the UI looks like this.
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 ourpubspec.yaml
file as assets and then we can useImage.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.
Note — The
turns
parameter takes the current rotation as a number of turns, so we’ll divide thecompassHeading
by 360. Also, the??
is “if null” operator in dart, which we used to specify the turns when thecompassHeading
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.
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.
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, runflutter build macos
Note — You need to enable desktop support for Flutter by first running
flutter config --enable-windows-desktop
, andflutter 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)