Building with Flutter and Metamask
Tags: metamask, flutter, blockchain, tutorial
Published: 2022-06-04 20:59:22

Building apps is one of the coolest things to be done as a software developer and tech enthusiast.  Apps are not only more portable and user friendly, based on the requirements, but sometimes they are the only option for a particular application In this tutorial and the upcoming series, we will learn how to build a cool cross-platform mobile application using **Flutter** and **Metamask**.

## The problem we are solving

To interact with the blockchain, the users must have an account (public and private key pair) on the blockchain that is used to sign the transactions. Sharing the private key with someone is equivalent to sharing access to the account. Because of this, users will be reluctant to provide their private key to the app since this would raise a lot of security concerns. The industry-trusted method is to use a “Non-custodial wallet” like [Metamask](https://metamask.io/).

Although there are numerous tutorials on how to use the browser extension of Metamask, using the mobile app version isn’t properly documented yet. In this tutorial series, we will be covering how to connect a Flutter App with Metamask for user login and in later articles, interacting with smart contracts will also be covered. We are using [Flutter](https://flutter.dev/) as our development framework of choice because we want to build a cross-platform application that is supported in both Android and IOS.

In this tutorial, we will be building an App that will be using Metamask for login. It will get the public key from Metamask. Metamask can also be used for signing messages and transactions but that will be covered in a different tutorial. The following GIF shows what we are going to build:

![Demo GIF](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oatwqsu074b76lci0652.gif)
## Pre-requisites

- Flutter is installed in your system.  You can follow the official guide of Flutter [here](https://docs.flutter.dev/get-started/install).
- Have an Android / Ios emulator or physical device connected that will be used for testing the application. I recommend using Android Emulator as there are some bugs while working with Ios.
- Install and set up [Metamask Mobile App](https://play.google.com/store/apps/details?id=io.metamask&hl=en_IN&gl=US) in your Android emulator.

<aside>
💡 It is recommended to use [VS Code](https://code.visualstudio.com/) with [Flutter](https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter) extension.

</aside>

## Known challenges

While writing this article, there are some challenges with building and running the app on IoS.

- IoS doesn’t support installing third-party applications from App Store for simulators. So we have to install Metamask directly from its [Github repo](https://github.com/MetaMask/metamask-mobile).
- If you are using a MacBook with Apple Silicon, there are some extra steps needed for setting up Metamask in a simulator. You can read about it here.
- [Deep Linking](https://medium.com/wolox/ios-deep-linking-url-scheme-vs-universal-links-50abd3802f97) with Metamask is not working as expected. (For this tutorial it is recommended to use Android Emulator)

I will update this article if I find the solutions to the above problems. Till then your helpful comments are highly expected.

## Project initiation

We start by running `flutter doctor` to make sure we have everything set up properly for our development journey. You should see similar to :

![Running Flutter Doctor](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p84go1ylipc8160zkjfk.png)

Now we start by creating a new flutter project. To do this, open the location where you want to store your project folder in your terminal and type the following command:

```bash
flutter create my_app
```

Here `my_app` is the name of the project we are going to build. This will create a folder with the same name where all our code will reside. You should see an output similar to:

![Flutter initiation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gswalntyn86lp7p6adny.png)

Open this folder in a code editor of your choice.  You can run your app by typing `flutter run` or using the *Flutter Debugger* if you are using VS Code. 

### Installing dependencies

For this project we will require the following dependencies:

- **[url_launcher:](https://pub.dev/packages/url_launcher)** This will be used for opening `Metamask` from our app using a URI.
- [walletconnect_dart:](https://pub.dev/packages/walletconnect_dart) This will be used for generating a URI that will be used to launch Metamask.
- [google_fonts:](https://pub.dev/packages/google_fonts) Optional dependency for using Google Fonts in our app.
- [slider_button:](https://pub.dev/packages/slider_button) Optional dependency for using a Slider Button for login purposes.

To install these dependencies, type in the following command

```bash
flutter pub add url_launcher walletconnect_dart google_fonts slider_button
```

### Adding assets folder

We want to use static images in our app’s UI. For that, we have to create a folder that will contain our assets and tell flutter to use them as assets for our project. 

Create a folder called `assets` inside the root `my_app` folder. The name of the root folder will be whatever name you used for creating the flutter project. Inside the `assets` folder, we will create an `images` folder for storing our image assets. Finally, inside the `pubspec.yaml` file we add this folder by adding the following lines in the `flutter` section:

```yaml
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  assets:
    - assets/images/
```

### Understanding the flow

Finally, before we start coding our app, it is important to understand the user flow. The following diagram represents the flow a user will go through starting from when he opens our app:

![Flow diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yaafygrtugvirggejee8.png)

## Code Along

We will start from the `main.dart` inside the `lib` folder. The `main.dart` is the first file to be compiled and executed by Flutter. Clear all the contents of this file and paste in the following lines of code:

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

void main(List<String> args) {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp();
  }
}
```

We start by creating a new Stateless Widget. This widget will act as the starting point of our project. Based on the flow diagram shown above, the first thing to do will be to create the Login Page. Although for this tutorial our app will have only one page, we should have a proper routing system so that we can easily keep on adding newer pages as we proceed with our project.

### Creating routes

The way routing works in flutter is quite similar to how it works for web apps, i.e. using the `/path` format. In simple words, routes are nothing but a mapping of a path to its respective widget. An example of how routes work is:

```dart
return MaterialApp(
  initialRoute: "/login",
    routes: {
      "/login": (context) => const LoginPage(),
		  "/home": (context) => const HomePage()
    },
);
```

Inside `routes` we define all the routes that will be used in our project and their respective widgets. In this example we are saying that the widget `LoginPage` will be rendered when the user is in the `/login` router and similarly when the user is in the `/home` route, the `HomePage` widget will be rendered. The `initialRoute` field tells the initial or starting route to be loaded. In this example, the first widget the user sees on opening the app will be the `LoginPage` widget.

Since there will be multiple routes present in a project which are used across multiple files, it is not wise to directly type the route. Rather, one should have constant variable names defined for the code to be more robust. For this create a new folder called `utils` inside the `lib` folder and inside the `utils` folder create a new file called `routes.dart`. This file will store all our routes. Inside this file define the routes like this:

```dart
class MyRoutes {
  static String loginRoute = '/login';
}
```

Now let’s get back to our `main.dart` file and make the following changes:

```dart
import 'package:flutter/material.dart';
import 'package:my_app/utils/routes.dart';

void main(List<String> args) {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: MyRoutes.loginRoute,
      routes: {
        MyRoutes.loginRoute: (context) => const LoginPage(),
      },
    );
  }
}
```

Here we are importing our newly created `routes.dart` file and using the variable name instead of directly typing the route. Since we don’t have the `LoginPage` widget yet, we will be getting an error message. So let’s create our login page.

### Creating Login Page

Inside the `lib` folder, let’s create a new folder called `pages` that will have all our pages. This folder will have all our pages. Inside the `pages` folder, create a new file called `login_page.dart`. Inside this file paste in the following code:

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

class LoginPage extends StatefulWidget {
  const LoginPage({Key? key}) : super(key: key);

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold();
  }
}
```

Here we are creating a new Stateful Widget called `LoginPage`. Now we can import this into our `main.dart` file by adding `import 'package:my_app/pages/login_page.dart';` at the start of the file. The final main.dart file looks like this:

```dart
import 'package:flutter/material.dart';
import 'package:my_app/utils/routes.dart';
import 'package:my_app/pages/login_page.dart';

void main(List<String> args) {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: MyRoutes.loginRoute,
      routes: {
        MyRoutes.loginRoute: (context) => const LoginPage(),
      },
    );
  }
}
```

### Designing the Login Page

Now it’s time to design our Login Page. For this tutorial, we will be designing a very simple Login Page. To start with, it only has an image along with `Connect with Metamask` button. When Metamask is connected, it will display the account address and the chain connected with it. If the chain is not the officially supported chain (Mumbai Testnet for our case), we display a warning asking the users to connect to the appropriate chain. Finally, if the user is connected with the connected network, we show the details along with a “Slide to login” slider. These three are shown in the following diagrams respectively:

![App Screens](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x6u9s73ds2y15q79ncbl.png)

### Building the default Login Page

We start by editing the `login_page.dart` file. Make the following changes inside the `_LoginPageState` class:

```dart
class _LoginPageState extends State<LoginPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Login Page'),
      ),
      body: SingleChildScrollView(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Image.asset(
              'assets/images/main_page_image.png',
              fit: BoxFit.fitHeight,
            ),
            ElevatedButton(
                onPressed: () => {}, child: const Text("Connect with Metamask"))
          ],
        ),
      ),
    );
  }
}
```

Here we are doing the following:

- We start by returning a `Scaffold`. `Scaffold` in flutter is used to implement the basic material design layout. You can read more about it [here](https://api.flutter.dev/flutter/material/Scaffold-class.html).
- Then we are defining an `AppBar` with the title “Login Page”. This will be the title to be displayed on top of our app.
- We start the body of our app by defining a `SingleChildScrollView`. This is helpful when our App is opened on a phone with a relatively smaller display. It enables the users to scroll through our widget. Read more about it [here](https://api.flutter.dev/flutter/widgets/SingleChildScrollView-class.html).
- Inside the `SingleChildScrollView` we define a `Column` to contain the various components of our page as its `children`.
- The first child we define is an image. We want to render an image stored inside our `assets` folder. For this, we use `Image.asset()` and pass in the path to where the image is stored. Remember to use a path already added as a source of assets. Previously we added the `assets/images/` as a source of assets. I am using [this](https://pbs.twimg.com/card_img/1529204914425061376/Q6EAQTO3?format=png&name=small) image that I downloaded into the `images` folder and named `main_page_image.png`.
- Next, we create a button using the `ElevatedButton` class. It takes two arguments:
    - `onPressed`: The function to be executed when the button is clicked. For now, this is blank.
    - `child`: A child widget that will determine how our button will look. For now, it is a `Text` with the string `“Connect with Metamask”`.

If you run the app now, you should see something like:

![Initial look](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9l8nqllyt6u41mzk54jf.png)

Although pressing the button doesn’t do anything right now, we have our default look ready. It only gets more interesting from here 😎😎😎.

### Understanding the dependencies

Next, we will be writing the logic behind the “Connect with Metamask” button. For these we will be using two important dependencies:

- `walletconnect_dart`: This dependency will be used for connecting with Metamask. Practically it can be used with other wallets like Trust Wallet as well, but for this tutorial, we will focus only on Metamask.
    
    To understand how this works, we must first understand how [Wallet Connect](https://walletconnect.com/) works. Wallet Connect is a popularly used protocol for connecting web apps with mobile wallets (commonly by scanning a QR code). It generates a URI that is used by the mobile app for securely signing transactions over a remote connection. The way our app works is, that we directly open the URI in Metamask using our next dependency.
    
    `walletconnect_dart` is a package for flutter written in `dart` programming language. We will use this dependency to generate our URI and connect with Metamask. This package also provides us with callback functions that can be used to listen to any changes done in Metamask, like changing the network connected with.
    
- `url_launcher`: This dependency is used for launching URLs in android and ios. We will be using this dependency for launching the URI generated by `walletconnect_dart` in the Metamask app.

### Using the dependencies in our code

We start by importing the dependencies in our `login_page.dart` file

```dart
import 'package:walletconnect_dart/walletconnect_dart.dart';
import 'package:url_launcher/url_launcher_string.dart';
```

Next, inside our `_LoginPageState` class we define a connector that will be used to connect with Metamask

```dart
var connector = WalletConnect(
  bridge: 'https://bridge.walletconnect.org',
  clientMeta: const PeerMeta(
    name: 'My App',
    description: 'An app for converting pictures to NFT',
    url: 'https://walletconnect.org',
    icons: [
      'https://files.gitbook.com/v0/b/gitbook-legacy-files/o/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media'
    ]));
```

We are using the `WalletConnect` class to define our connector. It takes in the following arguments:

- `bridge`: Link to the Wallet Connect bridge
- `clientMeta`: This contains optional metadata about the client
    - `name`: Name of the application
    - `description`: A small description of the application
    - `url`: Url of the website
    - `icon`: The icon to be shown in the Metamask connection pop-up

We also define two variables called `_session` and `_uri`, which will be used to store the session and URI respectively when our widget state is updated.

We define a function called `loginUsingMetamask` to handle the login process as follows:

```dart
loginUsingMetamask(BuildContext context) async {
  if (!connector.connected) {
    try {
      var session = await connector.createSession(onDisplayUri: (uri) async {
        _uri = uri;
        await launchUrlString(uri, mode: LaunchMode.externalApplication);
       });
			 print(session.accounts[0]);
			 print(session.chainId);
       setState(() {
         _session = session;
       });
    } catch (exp) {
      print(exp);
    }
  }
}
```

Here we are doing the following:

- First, we check if the connection is already established by checking the value of `connector.connected` variable. If the connection is not already established, we proceed with the code inside the `if` block.
- We use `try-catch` block to catch any exception that may arise during establishing the connection, like the user clicking on `cancel` in the Metamask pop-up.
- Inside the `try` block, we create a new session by using the `connector.createSession()` function. It takes in a function as an argument that is executed when the URI is generated. Inside this function, we use the `launchUrlString()` function to open the generated URI in an external app. We pass in the generated URI as a parameter and since it will be opening an external application, we set the `mode` as `LaunchMode.externalApplication`. Finally, since we want our code to wait until the connection is confirmed using Metamask, we use the `await` keyword with `launchUrlString()` function.
- We can fetch the accounts connected by using `session.accounts` and the chain id by using `session.chainId`. For now, we print the selected account using `session.accounts[0]` and the chain Id to the console to check if our code is working properly.
- Finally, we update the state of our app using `setState` and store the created session in the `_session` variable.
- If any exception is generated in any of the above statements, the `catch` block will be executed. Right now we only print the generated exception, but in the latter stages of the project, we can use more robust exception handling.

Finally, we call the `loginUsingMetamask` function as the `onPressed` argument of our created button. The final code looks something like this:

```dart
import 'package:flutter/material.dart';
import 'package:walletconnect_dart/walletconnect_dart.dart';
import 'package:url_launcher/url_launcher_string.dart';

class LoginPage extends StatefulWidget {
  const LoginPage({Key? key}) : super(key: key);

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  var connector = WalletConnect(
      bridge: 'https://bridge.walletconnect.org',
      clientMeta: const PeerMeta(
          name: 'My App',
          description: 'An app for converting pictures to NFT',
          url: 'https://walletconnect.org',
          icons: [
            'https://files.gitbook.com/v0/b/gitbook-legacy-files/o/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png?alt=media'
          ]));

  var _session, _uri;

  loginUsingMetamask(BuildContext context) async {
    if (!connector.connected) {
      try {
        var session = await connector.createSession(onDisplayUri: (uri) async {
          _uri = uri;
          await launchUrlString(uri, mode: LaunchMode.externalApplication);
        });
        setState(() {
          _session = session;
        });
      } catch (exp) {
        print(exp);
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Login Page'),
      ),
      body: SingleChildScrollView(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Image.asset(
              'assets/images/main_page_image.png',
              fit: BoxFit.fitHeight,
            ),
            ElevatedButton(
                onPressed: () => loginUsingMetamask(context),
                child: const Text("Connect with Metamask"))
          ],
        ),
      ),
    );
  }
}
```

Now, we run our app 🤞🏾. If everything is done as described, we will be greeted with a familiar Login Page. But when we click on the `Connect with Metamask` button, it will redirect to Metamask. Metamask will prompt you to connect your wallet. It will show the URL and icon specified in the `clientMeta` field.

![Metamask popup](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sdzkbhf6kl62jwgspc63.png)

When we click on the blue `Connect` button, we will be redirected back to our wallet. Right now we won’t see anything different, but if we check back the logs, you should see flutter printed the account address and the chain id.

![Showing chainid and account](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d7sltsqoivzb0lgwfvr8.png)

**Congratulations 🥳 🎉!! You have successfully connected with your Metamask wallet and it was that simple.** 

There is still one challenge left. Users may not connect with the blockchain your Smart Contracts are deployed to. So before we let users inside our platform, we should check if connected with the correct blockchain. Also, we should update if the user changes the connected network and also the selected account.

### Subscribing to events

Using our `connector` variable we can subscribe to `connect`, `session_update` and `disconnect` event. Paste the following code inside the `build` function:

```dart
Widget build(BuildContext context) {
    connector.on(
        'connect',
        (session) => setState(
              () {
                _session = _session;
              },
            ));
    connector.on(
        'session_update',
        (payload) => setState(() {
              _session = payload;
							print(payload.accounts[0]);
							print(payload.chainId);
            }));
    connector.on(
        'disconnect',
        (payload) => setState(() {
              _session = null;
            }));

    ...
  }
```

Here we are subscribing to the different events. On a `session_update` we update the state of our app using `setState` and assign the updated payload inside the `_session` variable. We also print the new account address and the chain Id, so that we can check if our code is working properly from the terminal. 

Perform a hot reload of your app and perform the same steps to connect Metamask with your app. Now you can change the network and the connected account from inside Metamask and observe the chain Id and account address change in your terminal/console.

![Updating ChainId and Account](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cozagymj7ikwxrb2qdck.png)

### Displaying the data on the screen

We have successfully connected Metamask with our app. Although the tutorial can end right here, I would prefer to display the details on the screen for users to verify and create a better login experience.

The first thing we want is when the user has connected with Metamask, we want to display the details instead of the button. For this we wrap our `ElevatedButton` in a ternary operator as follows:

```dart
(_session != null) ? Container() : ElevatedButton()
```

Here, if the `_session` variable is `null`, i.e.  Metamask is not connected, it would render the `ElevatedButton` else the `Container` will be rendered.

We start with the following code inside our `Container`:

```dart
Container(
  padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Text(
        'Account',
        style: GoogleFonts.merriweather(
          fontWeight: FontWeight.bold, fontSize: 16),
      ),
      Text(
        '${_session.accounts[0]}',
        style: GoogleFonts.inconsolata(fontSize: 16),
      ),
    ]
  )
)
```

- We start by adding small padding of `20px` from left and right.
- We want our cross-axis alignment to be from the start, so we define `crossAxisAlignment` as `CrossAxisAlignment.start`.
- We want the first widget in our column to be a simple saying `Account` and below it shows the account address of the connected Metamask account. We use the `Text` widget for displaying the data and use `GoogleFonts` for styling. You can import Google fonts by writing
    
    ```dart
    import 'package:google_fonts/google_fonts.dart';
    ```
    
    on top of the file. We use the `${}` notation to access the `_session` variable inside a pair of single-quote(`’’`).
    

The next thing we want to show is the name of the chain users are connected to. We want to display it in the following way:

![Showing Network name](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/21b2iemn4dt68ca0p0ac.png)

Since most of the users may not be familiar with the chain Ids of the different blockchains, it’s better to show them the name of the blockchain, rather than just the chain Id. To do this, we can write a simple function that takes in the `chainId` as input and returns the name of the chain. Inside the `_LoginPageState` define a function called `getNetworkName` as follows:

```jsx
getNetworkName(chainId) {
  switch (chainId) {
    case 1:
      return 'Ethereum Mainnet';
    case 3:
      return 'Ropsten Testnet';
    case 4:
      return 'Rinkeby Testnet';
    case 5:
      return 'Goreli Testnet';
    case 42:
      return 'Kovan Testnet';
    case 137:
      return 'Polygon Mainnet';
    case 80001:
      return 'Mumbai Testnet';
    default:
      return 'Unknown Chain';
  }
}
```

The function uses `switch-case` statements to return the name of the chain based on `chainId`.

Inside our `Container`, after the two `Text` widgets, we add a `SizedBox` with `height` of 20px to add some gap. Next we define a `Row` with two children widgets, the text “Chain” and the name of the chain obtained by calling the `getNetworkName` function. We do it like this:

```dart
Row(
  children: [
    Text(
      'Chain: ',
      style: GoogleFonts.merriweather(
        fontWeight: FontWeight.bold, fontSize: 16),
      ),
      Text(
        getNetworkName(_session.chainId),
        style: GoogleFonts.inconsolata(fontSize: 16),
      )
  ],
),
```

Next, we want to check if the user is connected to the correct network. We check with `_session.chainId` matches the chain id of our supported blockchain (in this case 80001 for Mumbai Testnet). If it’s not equal to the required chain id, we create a `Row` to display our icon and the helper text, otherwise, we create a `Container` that will be used for our `SliderButton`.

```dart
(_session.chainId != 80001)
  ? Row(
      children: const [
        Icon(Icons.warning,
          color: Colors.redAccent, size: 15),
        Text('Network not supported. Switch to '),
        Text(
          'Mumbai Testnet',
          style:
            TextStyle(fontWeight: FontWeight.bold),
        )
      ],
    )
  : Container()
```

Next, we add out `SliderButton`. We import our dependency with the following statement at the start of our file:

```dart
import 'package:slider_button/slider_button.dart';
```

Finally inside our `Container`, we define our `SliderButton` like this:

```dart
Container(
  alignment: Alignment.center,
  child: SliderButton(
    action: () async {
      // TODO: Navigate to main page
    },
    label: const Text('Slide to login'),
    icon: const Icon(Icons.check),
  ),
)
```

For now, the `SliderButton` doesn’t do anything, but in further tutorials, it will navigate us to the main page of our application.

Now your app is fully ready to be run. If everything was done as described in this tutorial, your app should be now ready. You should be able to Login In to your app using Metamask. Although the app doesn’t login into any page, still you can connect with Metamask using your mobile app. ***How awesome is that ?!!*** 

## Wrapping Up

Wow!! That was a log tutorial. In this tutorial, we covered how to build a very basic flutter app from scratch. We learned how to interact with Metamask from our app. We explored two important dependencies, `walletconnect_dart` and `url_launcher`, and learned how they work and how they can be used to connect an app with a wallet like Metmask. We also learned how to update our app when the user updates the Metamask session. And finally, I hope we all had a great time learning something new and interesting.

The code for this project is uploaded to Github [here](https://github.com/BhaskarDutta2209/FlutterAppWithMetamask).

I plan to extend this application into an app that does more cool things and dive deeper into the world of Defi, Blockchain, and beyond. If you liked this tutorial, don’t forget to show your love and share it on your socials or help me improve by posting your feedback in the Discussion. If you want to connect with me or recommend any topic, you can find me on [LinkedIn](https://www.linkedin.com/in/itsbhaskardutta/), [Twitter](https://twitter.com/Bhaskar2209), or through my mail.

We will meet again with another new tutorial or blog, till then stay safe, spend time with your family and ***KEEP BUIDLING!***