Building a HyperLocal App to fight COVID-19 : Part I

With the COVID19 expanding at a unprecedented rate, a lot of small businesses are affected, especially those that rely on a brick and mortar model for sales. During the quarantine I decided to explore the idea of making a hyperlocal ecommerce app that can help small business with sales during this period.

The Tech Stack

The goal is to keep a minimal tech stack that reduces the cost of the application, while keeps the spirit of a simple clone. We don’t need to implement features that large providers like Amazon or Grofers need for their operations. I decided to go ahead with Flutter, as it was one of my goals for this year and allows the same codebase to run on iOS and Android, something that is highly desirable. I had heard a lot of praise around Firebase, so decided to give Firebase a shot for this application, especially that fabric is now integrated with firebase.

For back-end, I will use python. Its simple enough for a app like this as we don’t have scale factor to consider.

The Architecture

I decided to break all the services into simple components. I came up with the following basic services that I need:

  1. User: The basic user model. Should contain details like user’s email, phone and saved addresses.
  2. Authentication: The authentication service is going be a child class of Auth that implements Firebase Authentication.
  3. Cart: The cart model. At basic level, this is a map of items and quantity, but should support features like tracking payment, finding total price and conversion of a paid cart to an order that should go out for delivery.
  4. Geo Location: The geolocation services should allow to find the user’s geo location and identify the nearest store and fetch inventory. It should also provide a place picker for the user to select a custom location.
  5. Inventory: This fetches the inventory for a store from the backend, and is responsible to handling the available quantity and stock count of items.
  6. Push Notifications: The wrapper around the firebase cloud messaging functionality, allowing to push notifications to end user for events like sales or stock outage.
  7. Store: The store model contains the store details like store name, store location, payment IDs for the payment gateway
  8. Catalog: This is a WIP. Ideally the catalog should be loaded from every store that should be used to build the feed and manage cart entities. I have not been able to implement it as of now, but its on my road map.
How it all stiches together

Layout

Welcome and Sign Up Page

I wanted to greet the user with a personalized experience. This again can be customized, but provides the user with basic information around the application. I used the flutter library liquid_swipe to achieve this. However I found liquid swipe to be slow on certain occasions for me, so had to tweak some code to get it working smoothly.

A swipe animation to add spice to the welcome page

The Home Screen

I looked at the home screen of some established apps like Grofers, Amazon and BigBasket and decided that following are essential for a great landing page experience:

  1. Product promotions that catch people as soon as they login
  2. A product feed that is well organized to find products in 2–3 taps
  3. All app functions easily accessible

However the app model that we have has following challenges:

  1. The feed and discounts vary from store to store
  2. We need the geolocation before we can show the feed

To solve this, I used the flutter geolocator library. In the Home Page’s init state, we call the function getLocationInBackground. This guarantees that when the home screen Widget is rendered, the location is already available for all network calls.

void getLocationInBackground() {
new Future.delayed(Duration.zero,() async {
var _store = Provider.of<FranchiseStore>(context, listen: false);
Position _position = await Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
print(">>>Location: "+_position.toString());
_store.getStoreDetails(_postion);
});
}

We find the current location of the user device, and pass it to the instance of store class to load the nearest store’s payment details and location.

We also want the user to be able to update his location, so we use the google_map_location_picker to select a geolocation. and get its lat long and address via google maps API. This location is then used to update the current store model, and all necessary widgets rebuild as it refreshes the app state.

void showPlacePicker(FranchiseStore store) async {
LocationResult result = await showLocationPicker(context, "<Google Maps API Key>",hintText: "Enter delivery address",layersButtonEnabled: true);
store.getStoreDetails(result);
}

Once we get the store, we simply use a FutureBuilder to parse the json response of inventory and render it into widgets. We fetch all inventory by category which allows to render the inventory by category. This entire operation is cached by the Store model, so we perform this expensive network call just once for every store.

A very basic cache implementation can be something like

class CacheInterceptor {
var _cache = Map<String, String>();

get cache => _cache;
Future<String> getResponseWitchCache(String category) async{
if(_cache[category] == null) {
print("Fresh network request");
var response = await http.Client().get('https://ourawesomeapp.com/category/'+category);
_cache[category] = response.body;
return _cache[category];
} else {
print("Cache hit");
return _cache[category];
}
}
}

The app utilizes a much more complex caching that is backed by Aerospike in the backend which is also used for search and suggestions, but this is a simple cache implementation for people looking for a bare minimum way to cache network calls for a user session.

This is how the end app looks like with all this functionality baked in

Notice that store location is immediately updated on Home Screen Update. Clicking on the location opens the location selector popup that allows to edit the location

This event is also recorded at Firebase Analytics and the data can be extracted. We can see this event in the User Engagement tab in the MainActivity section

This blog is first of the 4 series blog covering the app I am working on to help small businesses fight COVID 19. In the next part I will cover how the cart model is implemented and how we manage cart state across the consumer and delivery app.

Programmer. Likes Problem Solving.