Flutter on the front while Django got the back: Full Stack Development for Mobile, Web and Desktop from single codebase

This post will be continuously updated…

This chapter is a part of series From Life to Life: Compilation of Skills that pays (Vol. 1). You can find the book cover here and List of Contents here.

Flutter is Google’s UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase. Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. We will use Flutter to build the frontend for Mobile, Desktop and Web and Django to build the backend. Flutter uses Dart programming language while Django uses Python.

Content

Minimal Installation to start development in Flutter and Django
• Hello World in Flutter
• Simple Login Page in Flutter
• Sending HTTP POST request from Flutter
• Handling HTTP POST request in Django
• Testing the Server through Postman
• Handling CRUD operation through REST API in Django
• Database Management admin panel in Django
• Using an API, Login with Phone OTP using Twilio API
• Form Validation in Login Page in Flutter
• Using Device Features: Read Contacts, Send SMS & Make Calls
• Encrypting and Securing Communication between Flutter and Django

What is my Teaching Philosophy : I will teach only those things which I believe will stay and dominate for a long time. (So that you can earn and even teach it to your kids). I will teach everything to the point, it may not be entertaining but it will be very concise and easy to grasp. (Do take a break while reading this)

How this chapter will get you paid : You can add Flutter and Django to your skill-set and work as a freelancer or for a company.

How to learn effectively from this chapter : Don’t just simply read it, follow it — learn it by doing and experimenting and googling stuff that you don’t understand and be curious.

Minimal Installation to start development in Flutter and Django

To keep things short, I am assuming you are on windows from now on. If not, refer here to install Flutter and here to install Django on your computer.

The right way to install Flutter and Django is by cloning the github repository. For that, first you will need git installed. So, download and install git from here.

Now open Git Bash from the start menu and enter the following commands one by one.

cd c:
git clone https://github.com/flutter/flutter.git -b stable
cd flutter
flutter channel beta
flutter upgrade
flutter config — enable-web

Now add flutter/bin to your system’s environment variables so that you can access flutter from any directory through any shell. Open Edit the system environment variables from start/control panel and set it as shown below in the following screenshot and click OK.

Now download and install Visual Studio Code Editor from here. If you haven’t installed Chrome already (or any chromium based browser which supports debugging like Microsoft Edge) then download and install it from here.

Now, let us install Django but for that we will require Python and Pip installed. So, Download and install python (pip is included in the package) from here but while installing it make sure you have these things checked as shown below in the 2 screenshots.

Now open Git Bash from the start menu and enter the following commands one by one.

cd c:
git clone https://github.com/django/django.git
python -m pip install -e django/

Note: we have now successfully installed Flutter and Django which is just enough to start development. With this, you can develop and debug apps for the web and since the same code (as it is) can be used to develop apps for mobile and desktop all you need to do is go through further installation steps to compile apps for mobile and desktop. This is optional for learning purposes and can be done later when you want to build the app.

For compiling mobile apps download and install Android Studio with Flutter extension, refer here. For compiling desktop apps download and install Visual Studio Community (Not Visual Studio Code) with Desktop development with C++ package, refer here. We are not covering these installation procedures in detail.

Hello World in Flutter

Things will get difficult here slowly but it is worth learning.

Open Visual Studio Code and then select the folder where you want to create the project from File => Open Folder. I have created a folder named Learn and selected it. Now, open a new terminal by Pressing Ctrl+Shift+` or from Terminal => New Terminal. Confirm that your flutter installation has been successful by running.

flutter doctor

Now, Enter the following command to create a new flutter project named myapp.

flutter create myapp

Now, let us switch our selected directory to the myapp. Open File => Open Folder, choose the folder named myapp and click select folder.

Now, let us open a new terminal again by Pressing Ctrl+Shift+` or from Terminal => New Terminal and check whether you have connected to the devices on which you want to run this flutter app by entering the below command.

flutter devices

For me there is a chromium based browser name Edge (web) in the list, for you it might be Chrome (web). I will be using Edge browser to run and debug the app since it comes preinstalled with windows. Let us, run the following command to execute the flutter app.

flutter run -d Edge -v

You will see the boilerplate app running as shown in the below screenshot.

You can click on the + floating button to increase the count and if you want to reload the app press ‘r’ in the terminal.

The main code for this app is present in the myapp => lib => main.dart. Here are the contents of this file.

Everything in a Flutter app is implemented through widgets. For example, from a simple application like showing text on the screen via Text widget to a complex application like routing to a different page via Navigator widget, everything is done using widgets.

Let us understand the code of this demo app.

import ‘package:flutter/material.dart’;

In flutter, everything is a widget. The above code imports Material design widgets in the project. Material is an adaptable system of guidelines, components, and tools that support the best practices of user interface design. Backed by open-source code, Material streamlines collaboration between designers and developers and helps teams quickly build beautiful products.

void main()

main() is the function in flutter from where the execution of the program starts.

runApp(MyApp());

The function runApp inflates the given widget and attach it to the screen. MyApp is a user-defined function it can have any name.

class MyApp extends StatelessWidget

Class MyApp inherits from interface StatelessWidget which has a member function named build. Let us understand the concept of Widgets and their types.

Widgets are the building blocks of a flutter application. Widgets describe what their view should look like given their current configuration and state. It includes a text widget, row widget, column widget, container widget, and many more. And the structure of the code of an app is a tree of widgets.

Based on the functionality, Flutter widgets can be divided into the following categories :

  • Basic Widgets: These are the widgets that are necessary for the development of any flutter application. These may include Appbar, Scaffold, Column, Row, Container, Text, Image, Icon, FlutterLogo, etc.
  • Layout Widgets: These widgets help in placing the other widgets on the screen as needed. These can be widgets that allow single-child or multi-child. Single child layout widget may include Align, Container, Center, Padding, SizedBox, AspectRatio, FittedBox, FractionallySizedBox, etc. Multi child layout widget may include Column, Row, GridView, ListView, Stack, etc.
  • Assets Widgets: These widgets take charge of assets such as display images and icons. These may include Image, Icon, etc.
  • Text Widgets: These widgets display text. These may include Text, RichText, etc.
  • Input Widgets: These widgets provide input functionality in a flutter application. These may include Form, FormField, etc.
  • Scrolling Widgets: These widgets provide scrollability to their children widgets. These may include GridView, ListView, Scrollable, Scrollbar, etc.
  • Styling Widgets: These widgets manage the theme of your app, make your app responsive to screen sizes, or add padding. These may include Theme, Padding, etc.
  • Async Widgets: These provide async functionality in the flutter application. This may include FutureBuilder, StreamBuilder, etc.
  • Painting and Effect Widgets: These widgets apply visual effects to the children without changing their layout, size, or position. These may include Opacity, RotatedBox, Transform, DecoratedBox, etc.
  • Animation and Motion Widgets: These widgets add animation to other widgets.
  • Accessibility Widgets: These widgets help make flutter app more accessible.
  • Interaction model Widgets: These widgets respond to touch events and route users to different views. It can be further classified into 2 categories — Touch Interaction Widgets and Routing Widgets. Touch Interaction Widgets may include Draggable, Scrollable, LongPressDraggable, GestureDetector, etc. Routing Widgets includes Navigator.
  • Material Components Widgets: These are visual, behavioral, and motion-rich widgets implementing the Material Design guidelines. These may include — App Structure Widgets, Buttons Widgets, Input Widgets, Selections Widgets, Alerts and Dialogs Widgets, Information displays Widgets, etc.
  • Cupertino (iOS-style) Widgets: These include beautiful and high-fidelity widgets for the current iOS design language.

Based on the state, Flutter widgets can be divided into the following categories :

  • Stateless Widgets: The widgets whose state can not be altered once they are built are called stateless widgets. These widgets are immutable once they are built i.e any amount of change in the variables, icons, buttons, or retrieving data can not change the state of the app.
    Examples: Icon, IconButton, and Text are examples of stateless widgets. To create a Stateless widget, we have to override the build() method from StatelessWidget Interface.
  • Stateful Widgets: The widgets whose state can be altered once they are built are called stateful widgets. Their states are mutable and can be changed multiple times in their lifetime. This simply means that the state of an app can change multiple times with different sets of variables, inputs, data.
    Examples : Checkbox, Radio Button, Slider, and TextField are examples of Stateful widgets. To create a Stateful widget, we have to override the createState() method from StatefulWidget Interface.

Let us return to the next line of the demo app code.

@override

This is a type of Annotation. Annotations do not affect on the meaning of a Dart program. This annotation is recognized by the Dart analyzer, and it allows the analyzer to provide hints or warnings for some potential problems of an otherwise valid program. As such, the meaning of this annotation is defined by the Dart analyzer.

The @override annotation expresses the intent that a declaration should override an interface method, something which is not visible from the declaration itself. This extra information allows the analyzer to provide a warning when that intent is not satisfied, where a member is intended to override a superclass member or implement an interface member but fails to do so. Such a situation can arise if a member name is mistyped, or if the superclass renames the member.

Widget build(BuildContext context)

This is overriding of a member function of interface StatelessWidget. The framework replaces the subtree below this widget with the widget returned by this method.

The next few lines of code is returning a Widget. It’s impossible to cover all the widgets here. We can learn all the widgets and how to use them from here. Just choose a widget you want to learn and see its example code. Here is a screenshot of this website.

class MyHomePage extends StatefulWidget

Here, the class MyHomePage inherits the interface StatefulWidget and implements its member function createState. Fields in this widget class are always marked final like the code final String title;

State is an information that (1) can be read synchronously when the widget is built and (2) might change during the lifetime of the widget. It is the responsibility of the widget implementer to ensure that the [State] is promptly notified when such state changes, using [State.setState].

StatefulWidget is a widget that has a mutable state. Stateful widgets are useful when the part of the user interface you are describing can change dynamically, e.g. due to having an internal clock-driven state, or depending on some system state.

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

This is a parameterized constructor saying that you can call MyHomePage with the title of the homepage in its parameter like it was done previously in the code — MyHomePage(title: ‘Flutter Demo Home Page’)

final String title;

As described above the fields in MyHomePage class are always marked final. Here is the difference between usage of const and final.

If the value you have is computed at runtime (new DateTime.now(), for example), you can not use a const for it. However, if the value is known at compile time (const a = 1;), then you should use const over final. There are 2 other large differences between const and final. Firstly, if you're using const, you have to declare it as static const rather than just const. Secondly, if you have a const collection, everything inside of that is in const. If you have a final collection, everything inside of that is not final. final should be used over const if you don't know the value at compile time, and it will be calculated/grabbed at runtime. If you want an HTTP response that can't be changed, if you want to get something from a database, or if you want to read from a local file, use final. Anything that isn't known at compile time should be final over const. With all of that being said, both const and final cannot be reassigned, but fields in a final object, as long as they aren't const or final themselves, can be reassigned (unlike const).

_MyHomePageState createState() => _MyHomePageState();

This creates the mutable state for this widget at a given location in the tree.

class _MyHomePageState extends State<MyHomePage>

This is the class where the build function of the child widget MyHomePage (of the root widget MyApp) is implemented. This build function again returns a widget to display on the app.

Simple Login Page in Flutter

Here is a program for a simple login app in Flutter. If you enter “I Love My India” in the password field then you will see the notification that says “Correct Password! Login Successful…” if not then it says “Incorrect Password! Please Retry…”. Let us, go step by step and make this program more advanced and feature-rich.

Replace the file content of man.dart with the below code, save it and execute the flutter app with the command — flutter run -d Edge -v . If this command is already running press ‘r’ in terminal to reload.

Sending HTTP POST request from Flutter

To send HTTP requests like POST requests we need to use a public library named http. There are several flutter libraries available at https://pub.dev/ including http. You can explore many libraries here. Let us include the http library in our project. To do so add http under the dependency section in the file myapp => pubspec.yaml as shown below.

dependencies:
http: ^0.12.2

Now, run the below command in the terminal to install this library.

flutter pub get

Now, we can import this library into our code.

import ‘package:http/http.dart’ as http;

Now, add the below function in the class _MyHomePageState.

This function assumes that there is a server running on http://127.0.0.1:8000/api/loginMember/ which checks if the password is correct and if the password is correct the server will return a string. The body of this POST request contains the phone number and the password. Soon we will build a server in Django which will handle this POST request and return “Member Authenticated” if the password is correct.

Also, update the function which is executed on pressing the Login Button (the onPressed property of TextButton Widget) as shown below.

Let us execute/reload the app and check if it is working. When the app has started in the debug mode in Edge — Right Click anywhere on the webpage and select Inspect, Now go to the Network tab and see what happens if you press the Login button in the app. If you can see something similar to that shown below then your app is working.

Here is more about Asynchronous Programming in Flutter. A synchronous operation blocks other operations from executing until it completes but an Asynchronous operation lets your program complete the work while waiting for another operation to finish. Some of the common asynchronous operations are fetching data over a network, Reading/Writing from/to a database, etc. To perform asynchronous operations in Dart, you can use the Future class and the async and await keywords.

A future (lower case “f”) is an instance of the Future (capitalized “F”) class. A future represents the result of an asynchronous operation and can have two states: uncompleted or completed. The async and await keywords provide a declarative way to define asynchronous functions and use their results. You can use the async keyword before a function’s body to mark it as asynchronous. You can use the await keyword to get the completed result of an asynchronous expression. The await keyword only works within an async function. Future is usually the return type of an asynchronous function.

Handling HTTP POST request in Django

The installation procedure of Django was already covered in the first topic of this chapter. So, now let us create a project in Django. Enter the following command in the terminal.

django-admin startproject server

Remember we created an incomplete login page in flutter where we sent a phone number and a password to a server at http://127.0.0.1:8000/api/loginMember/ through HTTP POST request and expected a string “Member Authenticated” if the password sent was “I Love My India”. Let us design the backend for this but for that, we will need a library named djangorestframework installed. Enter the following command in the terminal.

pip install djangorestframework

Now, let us import this library into our project by adding it to server => settings.py inside INSTALLED_APPS array as shown below.

INSTALLED_APPS = [
‘rest_framework’,
‘django.contrib.admin’,
‘django.contrib.auth’,
‘django.contrib.contenttypes’,
‘django.contrib.sessions’,
‘django.contrib.messages’,
‘django.contrib.staticfiles’,
]

Now, let us create an app named api inside our project. Enter the following commands in the terminal one by one.

cd server
python manage.py startapp api

This will create an app named api in the project server. What’s the difference between a project and an app? An app is a Web application that does something — e.g., a Weblog system, a database of public records, or a small poll app. A project is a collection of configuration and apps for a particular website. A project can contain multiple apps. An app can be in multiple projects.

The first thing we have to do just after creating an app is to include it in the project. So, the file server => settings.py should be updated like this.

INSTALLED_APPS = [
‘api.apps.ApiConfig’,
‘rest_framework’,
‘django.contrib.admin’,
‘django.contrib.auth’,
‘django.contrib.contenttypes’,
‘django.contrib.sessions’,
‘django.contrib.messages’,
‘django.contrib.staticfiles’,
]

The second thing we have to do just after creating the app is import the app’s URLs to the project’s URL. So, the file server => urls.py should be updated like this.

from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path(‘admin/’, admin.site.urls),
path(‘api/’, include(‘api.urls’)),
]

For some reason these 2 steps are not automatically done by the framework during app creation.

Now, we can write the program to handle the incoming POST requests. Go to, server => api => views.py and update it so that it contains the logic to check the password followed by sending an appropriate response.

Now you can include all the URLs that need to be available to receive HTTP requests in the file server => api => urls.py. Remember that we expect a POST request at http://127.0.0.1:8000/api/loginMember/. So, we will create a file server => api => urls.py with the below content.

from django.urls import path
from . import views
urlpatterns = [
path('loginMember/', views.loginMember, name="loginMember"),
]

Finally, let us run the app server and test it through Postman. Finalize and start the server with the following commands.

python manage.py makemigrations
python manage.py migrate
python manage.py runserver 8000

Testing the Server through Postman

Now that your server is up and running. Let us test it on Postman but for that, you will need Postman Installed. Download and Install Postman from here.

Once installed, Create a new Request and Send a POST request at http://127.0.0.1:8000/api/loginMember/ with the body of the request containing parameters you want to test. Refer the screenshot below.

Handling CRUD operation through REST API

Now, we will learn how to do create, read, update and delete operations from the database through Django server and test it through Postman. We will later verify the changes through a graphical admin panel provided by Django. We will handle — creation of a new member, reading the name of an existing member, updating the name of an existing member and deleting an existing member. We don’t need to install any database in our computer because Django ships with an inbuilt SQL database.

Let us update the file server => api => models.py file to include a class named Member. We will be storing/retrieving data as an object of this class to/from the database.

from django.db import modelsclass Member(models.Model):
phone = models.IntegerField(primary_key=True ,default="9999999999")
name = models.TextField(default="anonymous")
password = models.TextField(default="")

Let us update the code in server => api => views.py file as shown below.

Now, let us add the URLs where we want to receive the incoming HTTP requests by updating file server => api => urls.py

from django.urls import path
from . import views
urlpatterns = [
path('loginMember/', views.loginMember, name="loginMember"),
path('createMember/', views.createMember, name="createMember"),
path('readMember/', views.readMember, name="readMember"),
path('updateMember/', views.updateMember, name="updateMember"),
path('deleteMember/', views.deleteMember, name="deleteMember"),
]

Finally, Let us finalize and start the server. Run the following commands.

python manage.py makemigrations
python manage.py migrate
python manage.py runserver 8000

Now, Let us test the APIs through Postman, Here is a screenshot to add a member named Pankaj Kumar with phone number 1111111111 and password “I Love My India”. This is done by sending a POST request on http://127.0.0.1:8000/api/createMember/.

Similarly, Here is a screenshot to add another member named Alisha Jajoria with phone number 2222222222 and password “I Dont Want To Marry”. This is done by sending a POST request on http://127.0.0.1:8000/api/createMember/.

Here is a screenshot to read a member’s name whose phone number is 2222222222. This is done by sending a POST request on http://127.0.0.1:8000/api/readMember/.

Here is a screenshot to update a member’s name from Alisha Jajoria to Shweta Jassi whose phone number is 2222222222. This is done by sending a POST request on http://127.0.0.1:8000/api/updateMember/.

Here is a screenshot to delete a member whose phone number is 1111111111. This is done by sending a POST request on http://127.0.0.1:8000/api/deleteMember/.

Database Management admin panel in Django

Generating admin sites for your staff or clients to add, change, and delete content is tedious work that doesn’t require much creativity. For that reason, Django entirely automates the creation of admin interfaces for models. Django solves the problem of creating a unified interface for site administrators to edit content. The admin isn’t intended to be used by site visitors. It’s for site managers.

First, we’ll need to create a user who can login to the admin site. Enter the following command in the terminal.

python manage.py createsuperuser

Enter your desired username and press enter. You will then be prompted for your desired email address. The final step is to enter your password. You will be asked to enter your password twice, the second time as a confirmation of the first.

Now, we need to include the models that we want to access from the admin panel. So, update the file server => api => admin.py as shown below.

from django.contrib import admin
from .models import Member
admin.site.register(Member)

Finally, we can start the server with the following command and access the admin panel at http://127.0.0.1:8000/admin/

python manage.py runserver 8000

Using an API, Login with Phone OTP using Twilio API

Let us create a frontend in Flutter and backend in Django having login with phone OTP feature.

First, we will create the frontend with the following features:

  • Home Page shows a TextField to enter the phone number and a TextButton to send an HTTP POST request (with the body of the request containing the phone number) to the server to receive an OTP on this phone number.
  • On Pressing the TextButton, if we receive a String “OTP Sent” as a Response from the server it will route us to the next page having a TextField to enter the 4 digit OTP and a Login TextButton to send an HTTP POST request (with the body of the request containing the OTP) to the server to check if the OTP is correct.
  • If the server responds with the String “Member Authenticated” we will get successfully logged in and the notification will say “Correct Password! Login Successful…”

Then we will create the backend with the following features:

  • The server on receiving the incoming POST request will generate a random 4 digit number and save it to the database corresponding to the appropriate phone number received from the body of the POST request.
  • The server will then use the Twilio API to send an SMS having the OTP we just generated to the appropriate phone number.
  • The server will also handle the second incoming POST request in which we will compare the phone number and the OTP from the body of the incoming POST request to the values present in the database. If the OTP matches the server responds back with the String “Member Authenticated”.

Note that this is a project for learning purpose, this project is vulnerable to Man In the Middle Attack. Anyone can manipulate the second incoming response from the server and add “User Authenticated” String to it and successfully login regardless of the correctness of the OTP. This will be fixed in the section Encrypting and Securing Communication between Flutter and Django. Anyway, here is a glimpse of how you can secure the app from Man In the Middle Attack by encryption. It will be covered in detail in the appropriate section.

Let us update the frontend, the file myapp => lib => main.dart as shown below.

Let us now start the flutter app with the following command and test its functionality on Network Tab of the Edge Browser like we did before.

cd..
flutter run -d Edge -v

If you can see the app sending two POST requests as shown below then your app is working properly.

Now, Let us update the backend, the file server => api => views.py as shown below. To use the Twilio API go ahead and sign up on their website to get your own API Auth Code.

Now, Let us update the URLs where we are expecting the incoming HTTP requests from the frontend. Update the file server => api => urls.py as shown below.

from django.urls import path
from . import views
urlpatterns = [
path('requestOTP/', views.requestOTP, name="requestOTP"),
path('loginMember/', views.loginMember, name="loginMember"),
path('createMember/', views.createMember, name="createMember"),
path('readMember/', views.readMember, name="readMember"),
path('updateMember/', views.updateMember, name="updateMember"),
path('deleteMember/', views.deleteMember, name="deleteMember"),
]

Now, Let us relax the CORS policy. To do this we need to install a library named django-cors-headers. Run the following command in the terminal.

pip install django-cors-headers

Now, Let us include and configure this library in our project. Update the file server => settings.py to add the library in the INSTALLED_APPS and MIDDLEWARE array as follows.

INSTALLED_APPS = [
'corsheaders',
'rest_framework',
'api.apps.ApiConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
CORS_ORIGIN_ALLOW_ALL = True

Finally, We can run the django server and the flutter frontend by running the following two commands in separate terminals.

Terminal 1: from the directory server

python manage.py runserver 8000

Terminal 2 : from the directory myapp

flutter run -d Edge -v

If you are getting an output like the one shown below then, your program is working properly.

I am writing useful things I have learned in my life which eventually paid me or helped me…

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store