Firebase | Using CloudFirestore in Flutter app

For people who are new to Firebase, Firebase is a server-less architecture service provided by Google it supports Android, iOS, Web and unity Projects.

The Firebase service includes

  • Realtime Database
  • CloudFirestore
  • Firebase Cloud Messaging
  • Analytics
  • and more

Here in this tutorial we will learn how to use Cloud Firestore in Flutter Apps.

Before starting please go through this article to Create and integrate new project in Firebase. Flutter, Getting started with Firebase

Introduction

What is Cloud Firestore ?

Cloud Firestore is a NoSql database without any rows or columns all the data are saved as Documents or Collections and subcollections soo on. Where the document is an object and collection is list of the documents(objects). Which can be considered as a big json file mapped together. The Cloud firestore supports Realtime listening to the changes that is happening in the document or collections are even in chained queries.

Adding Firestore to Flutter

Please check this article Flutter, Getting started with Firebase for adding Google Services to the flutter app.

To use firestore add the following dependency to the flutter app

dependencies:
  cloud_firestore: ^0.13.5

Run flutter pub get based on your IDE.

Write data to Cloud Firestore

Create a instance of the cloud firestore reference

CollectionReference _fireStore = Firestore.instance.collection('users');

Add data to user collection,

  void addUser() {
    _fireStore.add({
      'userName': 'Leo',
      'email': 'leyo@gmail.com',
      'phone': 9876543210,
      'emailVerified': true,
    }).then((document) {
      // prints the document id when data adding succeed.
      debugPrint(document.documentID);
    });
  }

The above method will create a random document ID for the added document on the other hand we can add our own document ID as follows

String documentID = _fireStore.document().documentID;
  void addUser() {
    // this will generate a document ID
    String documentID = _fireStore.document().documentID;
    // or if you want to add a specific Doc ID for example
    // firebase auth User ID
    // var firebaseUser = await FirebaseAuth.instance.currentUser();
    // documentID = firebaseUser.uid;
    _fireStore.document(documentID).setData({
      'userName': 'Leo',
      'email': 'leyo@gmail.com',
      'phone': 9876543210,
      'emailVerified': true,
    }).then((_) {
      // prints the document id when data adding succeed.
      debugPrint('added successfully');
    });
  }

The setData() includes an additional param merge , if merge is set to true it will support both add and update. setDataw with merge true will update the document if already exists or add new one if not.

 void addUser() {
    // this will generate a document ID
    String documentID = _fireStore.document().documentID;
    // or if you want to add a specific Doc ID for example
    // firebase auth User ID
    // var firebaseUser = await FirebaseAuth.instance.currentUser();
    // documentID = firebaseUser.uid;
    _fireStore.document(documentID).setData({
      'userName': 'Leo',
      'email': 'leyo@gmail.com',
      'phone': 9876543210,
      'emailVerified': true,
    }, merge: true).then((_) {
      // prints the document id when data adding succeed.
      debugPrint('added successfully');
    });
  }

Update Data in Firebase

To update a specific document inside a collection you can do the following, Also while updating you can add a new parameter

Here we are updating the userName and adding new param dob.

  void updateUser(String userId) {
    String documentID = '$userId';

    _fireStore.document(documentID).updateData({
      'userName': 'elstin',   // userName is updated
      'email': 'leyo@gmail.com',
      'phone': 9876543210,
      'dob' : '21-Aug',   // new parameter
    },).then((_) {
      // prints the document id when data adding succeed.
      debugPrint('added successfully');
    });
  }

Add sub collection to document.

For example you want to add some posts that are specific to the current user, in that case we can create a subcollection to add the posts. The sub collection won’t be retrieved when you call the document collection, If you call the documents from user collection it won’t contain the posts sub collection.

To create a posts sub collection

  void addPost() {
    // this will generate a document ID
    String documentID = _fireStore.document().documentID;
    // or if you want to add a specific Doc ID for example
    // firebase auth User ID
    // var firebaseUser = await FirebaseAuth.instance.currentUser();
    // documentID = firebaseUser.uid;
    _fireStore.document(documentID).setData({
      'userName': 'Leo',
      'email': 'leyo@gmail.com',
      'phone': 9876543210,
      'emailVerified': true,
    }, merge: true).then((_) {
      
      // add new post dub collection to the document
      _fireStore.document(documentID).collection('posts').add({
        'title': 'hey this is a new collection',
        'content': 'This is a nes collction content',
      });
    });
  }

Delete data from Firestore

To delete document from the collection we can use the delete() method as follows

  void deletePost() {
    // this will generate a document ID
    String documentID = _fireStore.document().documentID;
    // or if you want to add a specific Doc ID for example
    // firebase auth User ID
    // var firebaseUser = await FirebaseAuth.instance.currentUser();
    // documentID = firebaseUser.uid;
    _fireStore
        .document(documentID)
        .collection('posts')
        .document('post_id')
        .delete()
        .then((_) {
      // delete success
    });
  }

The above function will delete the specific post document from the users post collection.

To delete a field from the document we can use FieldValue.delete() with updateData()

  void updateUser(String userId) {
    String documentID = '$userId';

    _fireStore.document(documentID).updateData(
      {
        'userName': 'elstin', // userName is updated
        'email': 'leyo@gmail.com',
        'phone': 9876543210,
        'dob': '21-Aug', // new parameter
        'userVerified' : FieldValue.delete()  // deletes the field
      },
    ).then((_) {
      // prints the document id when data adding succeed.
      debugPrint('added successfully');
    });
  }

Fetch data from Firestore

To retrieve documents from a collection the getDocuments() method can be used

  void getUsers() {
    _fireStore.getDocuments().then((QuerySnapshot snapshot) {
      snapshot.documents.forEach(
        (DocumentSnapshot documentSnapshot) {
          // prints all the documents available
          // in the collection
          debugPrint(documentSnapshot.data.toString());
        },
      );
    });
  }

The snapshot.documents will return all the available in the collection as List<DocumentSnapshot>.

Get a Document

To get only on particular from the collection we can use the documentID along with document(<docID>).get() method

void getUserData(String docID) {
  _fireStore.document(docID).get().then((DocumentSnapshot snapshot) {
    // prints the document Map{}
    debugPrint(snapshot.data.toString());
  });
}

Listen for Real Time Updates

We can also listen to change of any document in the collection.The real time event will be triggered each and every time update/ delete / addition or any changes happens to the document of the collection.

  void listenToChanges() {
    _fireStore.snapshots().listen((snapshot) {
      // this will be called each and every time
      // any changes happens in the collection
      snapshot.documents.forEach(
        (DocumentSnapshot documentSnapshot) {
          // prints all the documents available
          // in the collection
          debugPrint(documentSnapshot.data.toString());
        },
      );
    });
  }

Also the snapshots() method returns a Stream<Queryapshot>, therefore we can listen to the changes happen in realtime and we can use the stream builder to bind the stream directly to the UI

class BookList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamBuilder<QuerySnapshot>(
      stream: _fireStore.snapshots(),
      builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
        if (snapshot.hasError)
          return new Text('Error: ${snapshot.error}');
        switch (snapshot.connectionState) {
          case ConnectionState.waiting: return new Text('Loading...');
          default:
            return new ListView(
              children: snapshot.data.documents.map((DocumentSnapshot document) {
                return new ListTile(
                  title: new Text(document['title']),
                  subtitle: new Text(document['author']),
                );
              }).toList(),
            );
        }
      },
    );
  }
}

I hope you enjoyed the Article. Follow me on Linkedin| Github | Twitter @leoelstin for more articles.

Here is a Open source application I built with CloudFirestore as Backend.

Flutter Architecture # The State Management in Provider Way 💙

Managing the state of the application is one of the hardest job in architecting an application, Here in out Flutter application we will be using Provider package to Architect our application.

Flutter comes with its own widget to manage the state of the application the StatefulWidget class

A widget that has mutable state.

Sample State management

 void grow() {
    setState(() { _size += 0.1; });
  }

Lets move on into our Application, Our application is a Covid’19 Tracker application that reads the data from a public api ( India Only ) and displays the data into the UI

Let’s quickly go over the structure. The lib folder is divided into four folders.

  • Ui
  • Models
  • ViewModel
  • Service

UI holds the views and is again further divided based on screen

Models holds all the plain data models, here is good tool to autogenerate your models QuickType.io which instantly parses the JSON into available languages.

ViewModel holds the Provider models for each of the Widget views

Services holds the dedicated files that will handle actual business logic

Flutter, Getting Started with Firebase.

How to add firebase to flutter app ? Let’s get started.

Step 1: Set up your environment

Fluter.io provides a neat document on how to install flutter and setup your favourite IDE : Get Started

Once you have completed setup create a new Flutter project with your desired name.

Step 2: Create a Firebase project

Before adding firebase to flutter app go to Firebase console and create new Firebase project.

Tap on Add Project enter your Projects name , firebase will assign a project id based on the entered name you can also edit , Accept terms and Create project.

Your new firebase project will be created.

Step 3: Configure your app to use Firebase

To configure firebase to flutter we need some platform specific details to android and iOS apps. If you are working only on android you don’t need to add iOS specific changes.

Configure an iOS App: (Optional )

  1. In your Firebase project console overview page , Launch the iOS setup wizard.
  • Find this bundle ID by opening your app in XCode, then accessing the General tab in the top-level Runner directory. The value of the Bundle Identifier field is the iOS bundle ID (for example,com.yourcompany.yourproject). (Source)

2. Enter the Name and app store id (optional ) click next. Now download the GoogleService-info.plist

Copy the GoogleService-info.plist into iOS — Runner — Runner directory of your flutter project. In console click next and skip other steps.

Your iOS app is configured with Firebase.

Configure an Android App: (Optional )

  1. In your Firebase project console overview page , Launch the Android setup wizard.

Enter your application id into the Android package field, other fields are optional.

  • An application ID is sometimes referred to as as a package name.
  • Find this application ID in your module (app-level) Gradle file, usually android/app/build.gradle(example application ID: com.yourcompany.yourproject). (Source)

Register the app. On the next step download google-services.json and move the file into android/app folder of your flutter project.

2. Android requires another few more steps to add firebase support in your app .

In your root-level (project-level) Gradle file (android/build.gradle), add rules to include the Google Services plugin. Check that you have Google’s Maven repository, as well.

buildscript {
// ...
dependencies {
// ...
// Add the following line:
classpath 'com.google.gms:google-services:3.2.1' // Google Services plugin
}
}
allprojects {
// ...
repositories {
// Check that you have following line (if not, add it):
google() // Google's Maven repository
// ...
}
}

In your module (app-level) Gradle file (usually android/app/build.gradle), add the following line to the bottom of the file.

dependencies {
// ...
}

// ...

// Add the following line to the bottom of the file:
apply plugin: 'com.google.gms.google-services' // Gradle plugin

Run flutter packages get.

Now our app is ready to integrate flutter firebase plugins.

Open your pubspec.yaml file and add the following line under dependencies

dependencies:
flutter:
sdk: flutter
firebase_core: ^0.2.5 # add dependency for Firebase Core

Run your app and verify if app is working.

Documentation from firebase.google.com.

Flutter, working with Tabs !

Most of you people from Mobile app development might know it and have used it. Here in this tutorial let us learn about how to use tab, control the tabs and swipes.

Components of Tab Layout.

  1. Create the TabView
  2. Tab Controller
  3. TabBarView contents

1. Create the TabView.

Import the material package which contains the widgets for creating the Tab and Tab controller.

import 'package:flutter/material.dart';

Create a class name TabPage that extends to StatelessWidget.

import 'package:flutter/material.dart';


class TabsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return null;
  }
}

Add the Scaffold widget and AppBar to it .

import 'package:flutter/material.dart';

class TabsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Tab Example'),
      ),
      body: Center(
        child: Text('Tab Layout'),
      ),
    );
  }
}

The AppBar contains an argument name bottom , we need to pass TabBar as a widget to that bottom argument. The TabBar takes the tabs as List of Widgets.

appBar: AppBar(
        title: Text('Tab Example'),
        bottom: TabBar(
          tabs: [
            Text('Text'),
            Icon(Icons.offline_pin),
            Row(
              children: [
                Icon(Icons.pageview),
                Text('Icons and Text'),
              ],
            )
          ],
        ),
      ),

Run the app, but it will not run and show the following error.

As the error show we need to pass the TabController to our TabsPage we can either add the TabController as and argument (controller) in TabBar or we can wrap the Scaffold widget inside a DefaultTabController.

2. TabController

Pass the Scaffold as child argument to DefaultTabController , length should be equal to the number of tabs added into the TabBar, the initialIndex  is used to show the current tab when opening the page.

DefaultTabController(
      // initialIndex: 1,
      length: 3,
      child: Scaffold(
        appBar: AppBar(
          title: Text('Tab Example'),
          bottom: TabBar(
            tabs: [
              Text('Text'),
              Icon(Icons.offline_pin),
              Text('data')
            ],
          ),
        ),
        body: Center(
          child: Text('Tab Layout'),
        ),
      ),
    );

Hit run , you can see the following screen below.

Now we have our app with DefaultTabController and our TabBar. The TabBar can have Icons or Text as tabs.

3. TabBarView contents

Pass the TabBarView as body to the scaffold. The TabBarView takes children as a List, Pass whatever widget you want as Children and the App will work.

  body: TabBarView(
          children: <Widget>[
            Center(
              child: Image.network('your image path'),
            ),
            Center(
              child: Icon(Icons.youtube_searched_for),
            ),Center(
              child: Text('Text with style'),
            )
          ],
        )

Hit run and you will see the above output.

Complete code:

import 'package:flutter/material.dart';



class TabsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      // initialIndex: 1,
      length: 3,
      child: Scaffold(
        appBar: AppBar(
          title: Text('Tab Example'),
          bottom: TabBar(
            tabs: <Widget>[
              Text('Image', ),
              Icon(Icons.offline_pin),
               Text('Text', style: TextStyle(
                fontSize: 18,
                color: Colors.yellow
              ),),
            ],
          ),
        ),
        body: TabBarView(
          children: <Widget>[
            Center(
              child: Image.network('your image'),
            ),
            Center(
              child: Icon(Icons.youtube_searched_for),
            ),Center(
              child: Text('Text with style'),
            )
          ],


        )
      ),
    );
  }
}

HTTP Requests in Flutter with Dio Library | Node JS | MongoDB

Making HTTP requests in mobile application is one of the common tasks. Thanks to http requests application can communicate with backend and selects data.

Flutter framework offers http package which works great when we need do basic stuff. When we need to do something more advanced we something bigger. And this can be done by using Dio. Dio is http connection library which has extra features like interceptors which will be helpful in many tasks (adding token authentication for each request, logging requests). Dio API is pretty easy and the library is being maintained by the authors. It’s really worth trying.

Node JS and Express Framework

Express is one of the most popular web frameworks for node.js. It is built on top of node.js http module, and adds support for routing, middleware, view system etc. It is very simple and minimal, unlike other frameworks that try do way to much, thereby reducing the flexibility for developers to have their own design choices.

I’ll be following the tutorial by Callicoder.com to build a Node Js application with MongoDB to build our restful api service

It covers

  1. Mongodb
  2. GET Request
  3. POST Request
  4. PUT Request/ Update
  5. DELETE Request

Flutter Notes App

Our application will have the following option

  • Create Note
  • Update Note
  • Delete Note

We will be using Future Builder to fetch the notes and Dio library to create HTTP requests.

Dio is a powerful Http client for Dart, which supports Interceptors, Global configuration, FormData, Request Cancellation, File downloading, Timeout etc.

DIO GET REQUEST EXAMPLE

import 'package:dio/dio.dart';

void getHttp() async {
  try {
    Response response = await Dio().get("http://www.google.com");
    print(response);
  } catch (e) {
    print(e);
  }
}

Notes Service Class

Let’s create a singleton class named NotesService to provide the HTTP services.

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';


class NotesService {
  Dio _dio;
  static const _baseUrl = 'http://localhost:3000';

  static final NotesService _singleton = NotesService._internal();

  factory NotesService() {
    return _singleton;
  }

  NotesService._internal() {
    _dio = Dio();
   }

}

Get Notes

The GET notes service will be a GET request, which fetches all the available notes in the DB

Future<String> getNotes() async {
  Response response;
  try {
    response = await _dio.get("$_baseUrl/notes");
    debugPrint(response.statusMessage);

    return json.encode(response.data);
  } catch (e) {
    throw e;
  }
}

Create Notes

The CREATE notes service will be a POST request, will CREATE a new OBJECT in the DB

Future<String> createNote(Map<String, dynamic> map) async {
  Response response;
  response = await _dio.post(
    "$_baseUrl/notes",
    data: map,
    options: Options(
      headers: {HttpHeaders.contentTypeHeader: 'application/json'},
    ),
  );

  debugPrint(response.statusMessage);

  return response.data.toString();
}

Update & Delete Note

The update and delete note will update the notes or delete the note based on the given ID.

UPDATE notes will be a PUT request and DELETE will be a DELETE request

Below is the complete NotesService Class

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';

class NotesService {
  Dio _dio;
  static const _baseUrl = 'http://localhost:3000';

  static final NotesService _singleton = NotesService._internal();

  factory NotesService() {
    return _singleton;
  }

  NotesService._internal() {
    _dio = Dio();
  }

  Future<String> getNotes() async {
    Response response;
    try {
      response = await _dio.get("$_baseUrl/notes");
      debugPrint(response.statusMessage);

      return json.encode(response.data);
    } catch (e) {
      throw e;
    }
  }

  Future<String> deleteNote(String noteID) async {
    Response response;
    try {
      response = await _dio.delete("$_baseUrl/notes/$noteID");
      debugPrint(response.statusMessage);

      return json.encode(response.data);
    } catch (e) {
      throw e;
    }
  }

  Future<String> updateNote(String noteId, Map<String, dynamic> map) async {
    Response response;
    response = await _dio.put("$_baseUrl/notes/$noteId", data: map);
    debugPrint(response.statusMessage);

    return response.data.toString();
  }

  Future<String> createNote(Map<String, dynamic> map) async {
    Response response;
    response = await _dio.post(
      "$_baseUrl/notes",
      data: map,
      options: Options(
        headers: {HttpHeaders.contentTypeHeader: 'application/json'},
      ),
    );
    debugPrint(response.statusMessage);

    return response.data.toString();
  }
}

NotesController Class

NotesController will help us to keep in control of the data between the UI and Service class

import 'package:flutter/material.dart';
import 'package:flutter_widgets/note_app/dto/notes_dto.dart';
import 'package:flutter_widgets/note_app/service/notes_service.dart';

class NotesController {
  NotesService _notesService;

  static final NotesController _singleton = NotesController._internal();

  factory NotesController() {
    return _singleton;
  }

  NotesController._internal() {
    _notesService = NotesService();
  }

  Future<List<NotesDto>> getNotes() async {
    try {
      String response = await _notesService.getNotes();
      debugPrint(response);
      return notesDtoFromJson(response);
    } catch (e) {
      throw e;
    }
  }

  Future<String> deleteNote(String noteID) async {
    try {
      String response = await _notesService.deleteNote(noteID);
      debugPrint(response);
      return '';
    } catch (e) {
      throw e;
    }
  }

  Future<String> createNotes(String title, String content) async {
    try {
      Map<String, dynamic> map = {
        'title': title,
        'content': content,
      };
      String response = await _notesService.createNote(map);
      debugPrint(response);
      return '';
    } catch (e) {
      throw e;
    }
  }

  Future<String> updateNote(String id, String title, String content) async {
    try {
      Map<String, dynamic> map = {
        'title': title,
        'content': content,
      };
      String response = await _notesService.updateNote(id, map);
      debugPrint(response);
      return '';
    } catch (e) {
      throw e;
    }
  }
}

The UI and rest of the integration are available in Flutter Ui Kit

Connect with me on LinkedIn

Flutter Web : Create web app and host it with Firebase.

Flutter for Web is announced in Google I/O few months back as a technical preview. Here in the article let’s learn to create a new flutter web app and host the app to firebase with firebase hosting.

Before we start, let’s just upgrade flutter, so we have the latest version. Inside a terminal window, run:

flutter upgrade

Might take sometime relax, have a coffee. Wait for it to complete.. Done. Great!

Create new flutter web project:

Here i’m going to use VS Code as my IDE for editing and creating new flutter web app.

From menu View-> Command palette.. or Ctrl + shift +p

type Flutter Web from suggestion choose Flutter: New Web Project

Next enter your project name and press enter on prompt choose your project location and press enter.

Wait .. it will take some time to completeOnce done, Choose Debug -> start Debugging or Press F5

VS Code will prompt to Activate WebDev press Activate Webdev

VS Code will start compiling the project and serves the project from `web` on http://127.0.0.1

Or manually navigate to the created projects path in terminal and enter the following command.

flutter packages pub global activate webdev
flutter packages upgrade

Running the Flutter Project in Browser

Well, if you followed all the above mentioned steps properly, then this should be as simple as executing the following command from inside the flutter web project directory.

webdev serve
No alt text provided for this image

Once done Open http://127.0.0.1:8080/#/ in any browser. If everything is success it should open the following page.

No alt text provided for this image

Now everything is all set. Open the main.dart file under lib folder and start editing your project.

No alt text provided for this image

Now build the project , enter the following command in terminal

webdev build

After the command is executed, we can see that in our project folder a new folder named build is created.

Deploying to firebase Hosting:

Setup the firebase CLI by following this guide.

.. and continue with this once the setup is competed. Under the flutter project root execute the following command

firebase init

Choose Hosting: Configure and deploy Firebase Hosting sites by pressing space bar and press enter

No alt text provided for this image

Then choose your project from the list and press enter.

Once it asks for public directory enter build and press enter

No alt text provided for this image

When it asks for rewrite index.html give and hit enter

No alt text provided for this image

Once the initialization is completed , Enter the following command in terminal

firebase deploy

If everything went fine the terminal will give the following logs

No alt text provided for this image

The hosting url and project console path is logged press ctrl+ click to open the link. for eg : Hosting URL: https://xyz.firebaseapp.com

Hope this article helps you. Like and share 🙂

Thank you for reading.