Crio Projects - A Hyperlocal Ecommerce platform | Crio.Do | Project-Based Learning Platform for Developers

Objective

You will be making a hyperlocal e-commerce platform using flutter and dart, while keeping the data base on firestore and further using cloud functions to implement few other functionalities of the application like an online payment gateway. Further you'll be covering most of the parts of an android development course.

Project Context

With each day, the hold of bulky e-commerce industries like Amazon, Flipkart etc is increasing. This is bringing a hard time for the small-scale local retailers and businesses. The increasing ease of buying products online makes it harder for the local businesses to compete in the market.


Hyperlocal businesses involve building a local ecosystem that enables customers to buy anything from their neighborhood stores. Hyperlocal has two major dimensions: geography and time. Its content is targeted people or entities that are located within a well-defined area, generally on the scale of a street, neighbourhood, community, or city.


The hyperlocal eCommerce system helps the customers to connect with local stores of particular geographical regions and gives customers a better experience of shopping by providing them fast delivery of the purchased products. Thus Hyperlocal Marketplace is the next generation in transforming e-commerce for both sellers and customers.


This project is a challenging project for beginners, the perfect project idea for intermediates and a routine project for professionals. This project uses an intermediate-advanced level flutter-dart concepts and moderate knowledge of Firebase databases.

project_context

Product Stages

We can divide the application architecture in the following :

  • Flutter - Application’s frontend and core-functionalities
  • Firebase - Integrating the database on firestore with the frontend.
  • Cloud functions - Writing few functions to implement few features like push notifications and payment gateway.
  • GitHub - To publish your work

product_stages

High-Level Approach

  • Setting up the UI (User Interface) part of the application, making all the different pages and enhancing the UX (User Experience); this will gradually help you understand how the final widget tree is going to look like. We will be using a few different Flutter packages to add that extra premium touch to our UI.
  • Integrating the firestore database to populate data on the frontend.
  • Implementing authentication using firebase auth method.
  • Slightly bending the widget tree to enable our application in querying location based documents. This will help us with the hyperlocal part of the application.
  • Creating extra features like push notifications and Paytm payment gateway.
  • Publishing to GitHub.

The desired end result of this project is like this.

Primary goals

Build an e-commerce app from scratch using Flutter and secure the app with proper authentication using Firestore as the database.

Objective

You will be making a hyperlocal e-commerce platform using flutter and dart, while keeping the data base on firestore and further using cloud functions to implement few other functionalities of the application like an online payment gateway. Further you'll be covering most of the parts of an android development course.

Project Context

With each day, the hold of bulky e-commerce industries like Amazon, Flipkart etc is increasing. This is bringing a hard time for the small-scale local retailers and businesses. The increasing ease of buying products online makes it harder for the local businesses to compete in the market.


Hyperlocal businesses involve building a local ecosystem that enables customers to buy anything from their neighborhood stores. Hyperlocal has two major dimensions: geography and time. Its content is targeted people or entities that are located within a well-defined area, generally on the scale of a street, neighbourhood, community, or city.


The hyperlocal eCommerce system helps the customers to connect with local stores of particular geographical regions and gives customers a better experience of shopping by providing them fast delivery of the purchased products. Thus Hyperlocal Marketplace is the next generation in transforming e-commerce for both sellers and customers.


This project is a challenging project for beginners, the perfect project idea for intermediates and a routine project for professionals. This project uses an intermediate-advanced level flutter-dart concepts and moderate knowledge of Firebase databases.

project_context

Product Stages

We can divide the application architecture in the following :

  • Flutter - Application’s frontend and core-functionalities
  • Firebase - Integrating the database on firestore with the frontend.
  • Cloud functions - Writing few functions to implement few features like push notifications and payment gateway.
  • GitHub - To publish your work

product_stages

High-Level Approach

  • Setting up the UI (User Interface) part of the application, making all the different pages and enhancing the UX (User Experience); this will gradually help you understand how the final widget tree is going to look like. We will be using a few different Flutter packages to add that extra premium touch to our UI.
  • Integrating the firestore database to populate data on the frontend.
  • Implementing authentication using firebase auth method.
  • Slightly bending the widget tree to enable our application in querying location based documents. This will help us with the hyperlocal part of the application.
  • Creating extra features like push notifications and Paytm payment gateway.
  • Publishing to GitHub.

The desired end result of this project is like this.

Primary goals

Build an e-commerce app from scratch using Flutter and secure the app with proper authentication using Firestore as the database.

Initial setups

I recommend installing VSCode along with the Flutter and Dart plugins to start developing flutter-based apps.

Requirements

  • If you're using the Flutter framework for the first time, I'd suggest you to go online and get a basic idea of what Flutter is and how it works. It'll help you connect better with the work that you're going to be doing.

  • We will set up the basic skeleton of the app first. If you are making a Flutter app for the first time, refer this page. After successfully setting up your new flutter project, we will now make two directories:

    • UI - this will store the dart files of all the screens and pages.
    • Functionalities - this will store all the other dart files like firestore_service.dart which will have all the CRUD functions for the firestore, auth.dart which will have all the firebase authentication related code and similarly other dart files which simply just run in the background at all time and is further needed by other dart files in the UI directory.

    The file structure for the completed app would be similar to the image below.

    product_structure

  • We're also going to need a few flutter packages available at pub.dev. For which we need to add few dependencies in pubspec.yaml file, which should then look like -

dependencies:
  flutter:
    sdk: flutter
  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  english_words: ^3.1.0
  carousel_pro: ^1.0.0
  firebase_auth: ^0.16.0
  shared_preferences: ^0.5.7
  firebase_core: ^0.4.4+3
  firebase_analytics: ^5.0.15
  cloud_firestore: ^0.13.5
  firebase_storage: ^3.1.6
  firebase_messaging: ^6.0.16
  google_sign_in: ^4.4.4
  photo_view: ^0.9.2
  cached_network_image: ^2.2.0+1
  flutter_spinkit: "^4.1.2"
  fluttertoast: ^4.0.0
  location: ^3.0.2
  google_maps_flutter: ^0.5.27+3
  geocoder: ^0.2.1
  provider: ^4.1.2
  geolocator: ^5.3.1
  geoflutterfire: ^2.1.0
  flutter_speed_dial: ^1.2.5
  url_launcher: ^5.4.10
  image_picker: ^0.6.7
  webview_flutter: ^0.3.22+1
  badges: ^1.1.1
  flutter_signin_button: ^1.0.0
  curved_navigation_bar: ^0.3.2
  streaming_shared_preferences: ^1.0.1
  line_awesome_icons: 
    git:
      url: git://github.com/mateotomasgomez/line_awesome_icons_flutter.git
      ref: master
 
  • After setting up the project, we'll set up the firestore project and make the skeleton database structure:

    1. If you're using firebase for the first time, I'd recommend you to check this page out.

    2. Make a clean structure of how your database should look like. Decide what all collections should be incorporated and then further decide the structure and fields of the documents in each collection. You should atleast have these discrete collections -

      • users - store all the user documents
      • products - store all the product documents
      • shops - store all the shop data
      • orders - store all the orders placed by all the customers

      we'll discuss more on how the documents in each collection should look like in the later sections.

    3. Once you've made the skeleton of the database, it's time to connect your database with the application. Check this page out for a detailed walk-through.

  • Search for different basic flutter packages and start using them for your own testing purpose.

Bring it On!

  • Try to figure out the document structure of each collection.
  • Create all the custom CRUD functions that you think you'll be needing in the firestore_service.dart file.
  • Check the security rules of your firestore project.

Expected outcome:

  • You should have the project setup complete along with the firebase project connected to it.

Making pages and UI and integrating data from firestore

An important thing to note while making the screens and using the variables that power your app, is state management.


State management

There are different options you can take to manage state in your application. I'll list a few of them below:


Streams (BLoC)

The BLoC principle can be used to provide fast, reactive state management. The changes in variables are reflected almost instantly. Check widgets like stream builders etc.


To handle asynchronous data, I'd recommend using widgets like futures and future builders. It however, requires some initial set-up and boilerplate code to get it running. Research articles on how to build reactive apps on Flutter and this might help you get started on it.


Constructors

You can transfer variables between different screens using their constructors. It is the easiest way to achieve state management.


It however gets a bit complex with increasing state variables and might lead to complications, further into development.

Requirements

  • Research about good design practices. Learn how you can make your app more accessible and user friendly.

  • Evaluate your choice of widgets to use; fancier ones are appealing but keep utility in mind while making the choice. Check here for some cool widgets.

  • Start with making the Homescreen, which should look like the image below. Use Material.io for many designing and development resources. That navigation bar can be made using the curved navigation bar package available at pub.dev. Location icon shows the current location of the device, check this page out for a detailed walk through. I recommend placing all the location service related code in a separate dart file in the functionalities directory.The horizontal scrollable list under "Top stores nearby" shows all the stores nearby. We'll later discuss how to query stores based on the current location.

    homescreen_image

  • Build the discover page. It's a very basic page with a tab-bar in it, the categories which will show products of that particular category, and the Shops tab which will show the products of that particular shop. Showing products of a particular shop or catagory or both can be tackled with simple firestore queries.

    discover_page

  • Build the Orders page, which again is a simple task. All you need to do is query the orders collection based on the user data, then display using a list view.

    orders_page

  • Making a wishlist page. I'd recommend storing the wishlist data of a particular user in a separate field of the user document. Which can simply be a list of the product ids of all the products in the wishlist. Now you just have to show those products with simple querying in a way similar to the image below.

    wishlist_page

  • Making the product listview page, which basically shows different query results of the products collection, like search query, shop specific query, section specific query, discount specific query etc.

    products_page

  • Product description page. Whenever we tap on a product from the query result, we are navigated to another page which displays the complete product description. Descriptions include the full size images, price, discount, sizes, variants etc. This also contains shop details which is offering that specific product.

    description_page

  • To build the cart page, I'd recommend the same to have a separate field in the user document (in users collection). This will have all the cart related information, like product ids of the products in the cart and their quantities. The cart should look like the image below. The card layout can be achieved using the card widget and the stack widget (which gives that effect to the delete button on the top right).

    cart_page

  • On tapping the place order in the cart, you should be navigated to the review cart page. This will give you a summary of your order, address details, payment details etc. Again basic querying from the firestore should do most of the work. Adding a new address or edit phone number will require you to update the user document.

    cart_review_page

  • To implement features like "Place Order", basic CRUD functions can be used. Store the order data in the orders collection. Make sure to include fields like payment detailed, address, userId etc.

Tip

  • Make good use of the SliverList widget.
  • Instead of making an UI element from scratch, see if a similar package is available at pub.dev or not. This will cut down your time requirements and these packages are usually very light and easy to use.
  • Make use of different providers for making the user document available throughout the widget tree.
  • Use proper indentation while writing the code, this will help you navigate better in your code.

Bring it On!

  • Try to make the UI as good looking as possible with the use of different tools and packages available.
  • Write the code for search bar (Hint: simple substring based query).
  • Try to implement the sort and filter feature.
  • Try to incorporate a loading spinner wherever needed.
  • Write the code for the map button in the product description page, which will show the shop's location in google maps.
  • Write the code for the call button in the product description page, which will automatically dial the shop's contact number.
  • Try different transitions animation like hero animations etc.
  • Make an image viewer for displaying the product image, you should also be able to zoom in or out or rotate the image.

User authentication with Firebase

When you open the application for the first time or after logging out you're taken to a login page. If you're a new user, you can choose to register. Further, complete its form validation and authentication error handling by enabling registration of users using email or Gmail through Firebase authentication. The login/register pages should look like these:

login

signup

Requirements

  • Create basic (UI) forms similar to the image shown above.
  • To add functionality to it, register users with email and Gmail and authenticate the user into the database with the help of packages like firebase_auth and google_sign_in.
  • Again, keeping the authentication related code/files in the functionalities folder will be a good practice.
  • If the user is registering for the first time, you need to make a skeleton document in the users collection for the user. Although there are many ways to do that, one of which is using firebase cloud functions. If you're new in using firebase cloud functions, start with this page. After successfully writing and deploying your cloud function, you should see something similar to the snippet shown below.
exports.onCreate = functions.auth.user().onCreate((user) => {
  console.log('New user at', user.email, user.uid);
  const email = user.email;
  const uid = user.uid;
  name = '';
  photoUrl = 'https://firebasestorage.googleapis.com/v0/b/porsio-3829b.appspot.com/o/user_profile%2Fdefaultt.png?alt=media&token=7f088463-bdf8-4dcf-a526-09fac66003d5';
  if (user.photoURL !== null)
    photoUrl = user.photoURL;
  if (user.displayName !== null)
    name = user.displayName;
  console.log(user.photoURL);
  console.log(user.displayName);
  let c = {};
  return admin.firestore().collection('users').doc(uid).set({
    email: user.email,
    location: new admin.firestore.GeoPoint(0, 0),
    displayPic: photoUrl,
    mobileNo: '',
    name: name,
    isProfileComplete: false,
    cart: c,
    address: [],
    wishlist: [],
    orderHistory: [],
    since: admin.firestore.FieldValue.serverTimestamp(),
    tokens: [],
  });
});
  • After registration/login page, users should be redirected to the homepage. In case of error in any of the credentials, the appropriate error must be displayed.

  • The following figure is what your user's document in firestore should look like (with respect to the parameters assigned to each user like name, address, wishlist, cart etc).

    user_document

Note

  • You must not store the user password in the user document. That will be handled by the firebase authentication.

Bring it On!

  • Implement forgot password functionality too - refer this for help.
  • Try to implement the "Stay logged in" feature by saving user credentials locally. You can use shared preferences for this.
  • Implement a logout feature as well. You should delete all the user data stored locally, when the user logs out.

Expected Outcome

At this point, the login/register pages must be made along with its error handling. Also the user must be redirected to the home screen page upon successful login. Check for all sorts of errors (email, password, password confirmation error) in the forms and give appropriate errors for each. Your cloud function should be able to make the skeleton document of the user upon registration.


Try making different permutations of errors possible to test your app’s functionality.

Modification for a hyper-local model application

By now, you should be comfortable with handling location services. I'd recommend you to store this location data in a widget called streaming_shared_preferences, which is similar to shared preferences but with subscription to the data. I'd also recommend you to use some provider widgets to make this variable available down the widget tree, this will come in handy while accessing location data in different pages.


Make sure your documents in the shop collection have a field for storing the location data of the shop. The data structure that we'll be using here is a map, with two keys:

  • geohash : a unique hash code for the area.
  • geopoint : coordinates in Latitude and longitude.
      
    
    location field

Requirements

  • Although what we want can be achieved through many different ways, but what we'll be doing is quite simple and much reliable. For querying stores based on the location, we'll be using a package called GeoFlutterFire. It allows you to store and query a set of keys based on their geographic location. GeoflutterFire is an easy to use tool which will make querying easy while keeping the application light and breezy.
  • Querying through GeoFlutterFire gives you a list of document snapshots as a result. So, we'll be writing a simple function like what's shown below, to get a list of document snapshots of all the shops that are nearby.
Stream<List<DocumentSnapshot>> getNearbyStores(LatLng location) {
    Geoflutterfire geo = Geoflutterfire();
    GeoFirePoint center =
        geo.point(latitude: location.latitude, longitude: location.longitude);
    var collectionRef = db.collection('shops');
    var geoRef = geo.collection(collectionRef: collectionRef).within(
        center: center, radius: 25, field: 'location', strictMode: false);
    return geoRef;
  }
  • Now with the help of providers, we'll make this list available throughout the widget tree.
  • Now slightly modify the firebase querying functions, by adding an extra condition. A condition (whereIn) to check whether the shop data in the product's document matches with any entry of the nearby shops list. For example:
 Stream getProducts(List<DocumentReference> nearByShopsReferences) {
    Stream<QuerySnapshot> prods = db
        .collection('products')
        .where('shop', whereIn: nearByShopsReferences)
        .snapshots();
    return prods;
  }

References


Note

  • The stream of geoflutterfire query is slightly different from a regular query. It returns a list of document snapshots instead of a QuerySnapshot.

Bring it On!

  • Fully explore the GeoFlutterFire package and experiment with different radius for queries.

  • Implement small features like locking the place order button if some of the products in the cart are not available nearby.

    not cart

  • We're just using the current user location for querying, see if you can change the centre of the query by passing a different geo data.

  • Display the address from the geo data collected.

Expected Outcome

Now the application will only show nearby stores or products offered by nearby stores. This includes search results, filters, sorts etc.

Adding Paytm payment gateway

By now you should have a basic application ready without the payment system. Although you can still have an option to complete the order with cash on delivery, it's always better to have an online payment system. Here, we're going to use Paytm APIs because it covers all the major online payment methods like cards, netbanking, UPIs etc.


Since Paytm provides its own web based UI for the payment portal, we don't have to focus much on the UI part. Instead we'll just use a web view to simply aid the payment portal.


We'll be implementing firebase cloud functions and then integrating the payment web API into those firebase functions. Further we'll be using those functions in our flutter application using web view.

Requirements

  • Read the documentation for Paytm SDK integration in the Flutter app available here.
  • Download the Paytm web sample kit for Node.js form here.
  • We now need to set up our own firebase cloud functions and then integrate the payment web API.
  • Create a new firebase function and install express and body-parser dependencies.
  • Copy the checksum and crypt from the web sample kit to the functions directory(where the index file is located).
  • Now writing the index code is quite complicated. We'll be making a post request for payment and then extract the parameters received from the request. We'll then pass these parameters to create a checksum and then make a request to move forward with the payment.
  • In this function we'll also provide a callback URL i.e. the URL for the payment receipt function.
  • Payment receipt function will receive a post request on the payment receipt. We get the checksum from the response data, then verify it for failed or successful transactions.
  • You can go ahead and test your function using Postman.
  • Now make the web view for the web payment portal. For this use the web view package from pub.dev.
  • Now build a measure to create a post request from the background with all the parameters(amount, client name, email etc).
  • Now after the transaction is complete the response will be received in the form of a JSON. So based on the status of transaction, payment success or failed screen can be shown.
  • For a further detailed walk through, I'd recommend you check the video tutorial provided in the References below.

Note

  • You will need a Paytm merchant account to be able to use it's SDK.
  • You may need to manipulate the AndroidManifest.xml file to make this work.
  • You should store the transaction Ids back at the order document. Hence, you should create a new order document after the transaction is complete and the transaction details are available.

Bring it On!

  • Try to make custom transaction status screens.
  • Display loading spinners while the web page is being loaded.

Expected Outcome

You should now have a working online payment system. The complete UI should look something like this:

  • Payment web view:

    Paytm web view

  • Transaction status page:

    transaction status page


Adding push notifications

This part is completely optional, but push notifications does give that fancy touch to the application.


We'll be using Firebase cloud messaging to implement easy push notifications. Find documentation on Firebase cloud messaging here.

Requirements

  • Add the package dependency to your project’s “pubspec.yaml”. Find the Firebase Cloud Messaging for Flutter package here.
  • On the Android side the Google Services Gradle Plugin needs to be included in the Gradle configuration.
  • The package has a function to generate a unique FCM identifier a.k.a. "token" for each device. Which will help the cloud function to send notification to a specific device.
  • When a user logs in, we must generate the token for the device and then store it in one of the fields of the user document.
  • Now write cloud functions for sending push notifications at different events, like when a new order is placed, canceled etc. Check the documentation for more details on how to write the functions.
  • One such example is shown below:
exports.newOrderTrigger = functions.firestore.document('orders/{orderId}').onCreate((snapshot, context) => {
  orderData = snapshot.data();
  db.collection('users').doc(orderData.userId).get().then(doc => {
    if (!doc.exists) {
      console.log('No such User document!');
      throw new Error('No such User document!'); 
    } else {
      //console.log('Document data:', doc.data());
      console.log('Document data:', doc.data().tokens);
      //tokens = doc.data().tokens;
      for (var token of doc.data().tokens) {
        tokens.push(token);
      }
      var payload = {
        "notification": {
          "alert": "Order Placed!",
          "title": "New order placed",
          "body": "Your order for " + orderData.prodName + " has been placed successfully.",
          "clickAction": "FLUTTER_NOTIFICATION_CLICK",
          "sound": "default",
          "badge": "2",
        }
      }
      // eslint-disable-next-line promise/no-nesting
      return admin.messaging().sendToDevice(tokens, payload).then((_response) => {
        console.log('pushed to all');
        return true;
      }).catch((err) => {
        console.log(err);
      })
    }
  })
    .catch(err => {
      console.log('Error getting document', err);
      return false;
    });
 

References

Note

  • Device tokens should be handled properly. Token generation should only take place upon sign-in or registration, not every time the user opens the app.
  • Device tokens should also be removed from firestore when the user logs-out from a particular device. Else that device will keep on receiving sensitive notifications.

Bring it On!

  • Try to incorporate a variety of notifications at different events.
  • Make use of Advanced message targeting, to easily send personalised notifications to different groups of users.
  • Customize notification content.

Expected Outcome

Your application should now be equipped with a strong push-notification system. Users will now receive customized notifications at various different events.

Enhance your application by implementing some more cool features

Till now, you have set up your app’s basic form. Now it’s time to amp up the app by adding some interesting features.


Note: This is an optional milestone for those who want to go that extra mile.

Requirements

Here are some recommendations you can adopt to make your app even better:

  • A map to show the exact location of all the stores nearby. Which further has an option to set the location of the user. Check this tutorial out for adding google maps and markers to your flutter application.

map

  • A profile page. Here, the user can edit all the personal information like phone number, address.

    profile

  • Automated email and texts. Use of automated emails and texts for verifications and sending useful information.

  • Products recommendations.

  • Use of google analytics, for better understanding the user behaviour towards the application. Check this page for more details.

  • A proper functioning search bar.

    search

  • Users should have the option of sharing the product details to other users.

  • Using advanced flutter animations to further improve the appearance of the application.

  • Add a feature for collecting user feedbacks.

    feedback

  • Every products and shops can be rated by the users.

  • Customised search results based on user's activity.

  • Finally when you're all done, publish your app to the google playstore.

  • Make necessary changes for the iOS devices and then move forward to publish it in the app store as well.

References

Expected Outcome

The final product should look somewhat like this.