Build Your First DApp Using Flutter | Web3 | Solidity | Blockchain

Rivaan Ranawat
13 min readMay 26, 2022

--

If you prefer watching video tutorial, you can find it here: https://youtu.be/2Jx95kYfYbM

This is Notes App UI and in this tutorial we will perform CRUD operations by creating, compiling and deploying smart contracts and then connecting it with Flutter making it your very first Decentralized app! If you prefer watching a tutorial, check out this video on my channel.

Notes App UI

Installation

First things first, make sure node is install on your system. If not go to nodejs.org and install either of the following versions.

Nodejs Website

Next, install Truffle globally on your system using the command: npm install -g truffle
Now, we need to initialise Truffle in our Flutter project.

What is Truffle?

Before initialising, let’s understand what Truffle is. Truffle provides us with a local development environment for creating, compiling and testing smart contracts.
Two main alternatives to Truffle are Hardhat,which comes built-in with Hardhat Network, a local Ethereum network and Remix IDE, a browser based smart contract playground. For this tutorial, we will be using Truffle.

Hardhat
Remix IDE

Folder Structure

After running the command truffle init (which initialises Truffle in our Flutter project), we have access to some folders and a file.
First is contracts, which stores the smart contracts in Solidity programming language
Second is migrations,which stores The javascript files used by Truffle to deploy smart contracts.
Third is truffle-config file, written in Javascript containing configuration settings for Truffle.
To test our smart contracts, we can use the Flutter’s test folder.

Folders after truffle init

What are Smart Contracts?

Smart contracts are like traditional contracts, main difference being that the smart contracts are lines of code that automate the terms whereas Traditional Contracts are terms enforceable by law and are described in a human language. Since Smart contracts are stored on Blockchain, there is no involvement of 3rd party. They are secure since each entry on the Blockchain is linked to the entries before and after it, similar to Linked List, thus hackers would have to change the entire chain to change a single record.

Linked List Data Structure

Exploring in VS Code

Next, install the Solidity VS Code extension by Juan Blanco for proper code highlighting and indentation.
If we expand the contracts folder in VS Code, we find a solidity file already created, which is Migrations.sol where sol extension stands for Solidity, a language in which Smart Contracts are written, Syntactically similar to Javascript.

Migrations.sol

This Migrations contract keeps track of migrations done on the network. This is done by storing a number(number is stored in last_completed_migration variable) that corresponds to the last migration script, found in the migrations folder. This means that Truffle will not run those scripts again while deploying. So, in future, if you modify an existing contract, you will have to create a new script with an increased number, as the numbering convention is x_script_name_migration.js, where x starts at 1. Contracts that we create will typically come in scripts starting at 2.

First Contract

Let’s type our First contract now. Create a new file named NotesContract.sol and the first line we need to type is the license line.

// SPDX-License-Identifier: MIT

Not providing this will result in a warning because trust in smart contracts are better established if their source code is available.

Making source code available touches legal copyright problems. So, we need to provide machine Readable SPDX License Identifier which can be chosen from one of the multiple licenses mentioned on their official website.

Next is the pragma line which specifies that the code we type will work for Solidity version 0.4.22 and above till 0.9.0.

pragma solidity >=0.4.22 <0.9.0;

This is done to ensure that the contract doesn’t behave differently when a newer version comes which might potentially break our code.

Finally, we create our smart contract using the contract keyword. This is pretty similar to classes. They contain persistent data in state variables and functions can modify these variables. You can even inherit from another contract to implement specific set of functions.

contract NotesContract {}

Inside this contract, we need to create public state variable of data type uint256 which stands for unsigned integer meaning it is a positive integer with a maximum value of 2²⁵⁶. Let’s name it noteCount as it will keep track of number of notes. A getter is automatically created on public state variables.

uint256 public noteCount = 0;

Then, we will create the skeleton of our note. This is done using the struct keyword. Inside this, we define some properties of Note like id, which will be unsigned integer again because it will get the value of noteCount when a new note is created and then title and description, both of them strings.

Then we need to keep track of all the notes in our dapp for which we we make use of mapping keyword which stores data in form of key-value pair. It can be considered as a List of Map, hashtable Or dictionary. The id is going to be the key so we pass uint256 and the value is going to be Note structure. Then we specify it’s visibility which is going to be public and name it.

mapping(uint256 => Note) public notes;

Then we need to create events. Events are ways to tell our client side app, in this case our Flutter app, that something, “some event” has taken place on the Blockchain. Basically the Blockchain can emit events to which our Flutter app can listen and make changes in UI accordingly. This first event in our app is when a new note is created, so the event keyword is used followed by variable name. id, title and description with their respective data types are taken in the parameter. Similarly we are going to create an event for Notedeleted. We will only take id to delete a note.

Note that events do not contain the logic for creating or deleting a note. For that, we need functions. So let’s create one for createNote, the syntax is similar to Javascript. We write function keyword, then the variable name and in the parameter, we need to get title and description, because we will use these functions in client side and we need these inputs from user. We use memory keyword here to temporarily store variables and their value. Alternative is to use calldata but it is used for parameters of function which have visibility marked as external, this one is public.

Whenever the user creates a new note, we want the notes hashtable to get updated. So, we will set notes at noteCount, which will initially be 0, equal to a new Note Structure because we defined that notes hashtable(struct) is going to have the key as uint256 and value as Note structure. Then, according to the defined arguments of Note struct, pass in the data.
Then, we need to notify our client app that the note has been created so we will emit the NoteCreated event, passing in the required arguments again. Finally, we will increase noteCount by 1, so that we store the new note created in a different key.

Finally, we will create the deleteNote function, similar to createNote but parameter will be the note id that is to be deleted and then delete notes at id. The delete keyword just appears to delete but in reality it sets the variable to their default value, for example variable with type uint is set to 0. Then we need to emit NoteDeleted event and decrease the count by 1.

Migrating Scripts

Congrats on building your first smart contract! Let’s deploy this to our local blockchain for testing. For that, we need to setup our migration scripts. Head to migration folder, create a new script named as 2_notesContract_migration.js and require the NotesContract using artifacts.require. It’s similar to Node’s require but here, it specifically returns a contract abstraction.Then, we need to export the function that deploys our contract.

Let’s compile our smart contracts using the truffle compile command.
Next step is to install Ganache, a local blockchain for running tests. Install it from their official website.

Ganache

When you run the software, you’ll land on this screen. It asks us to select Ethereum or Corda. Depending on your use case, you need to select one. For this tutorial, I’ll be going forward with Ethereum.
Create a new workspace, name it and save the workspace.

Having this set up, we need to perform migration of our contract. For that, we need to edit settings in truffle-config.js file, remove everything and replace them with this code.

With this, we specify host and port on which Ganache is running, link to our contracts directory and some optimizations which can be found on Truffle’s official website.
Now, we can run truffle migrate to deploy it. If Ganache is not running(running means, not kept on) in the background, we will get this error:

The Error

Flutter App Connection — Setup

Let’s connect our Flutter App with our smart contract. Create a new file where all our business logic is going to stay. We will use Provider for state management and web3dart for connection, so install those dependencies. Create a class that extends ChangeNotifier.

class NotesServices extends ChangeNotifier {}

Then, we will create Note model in a different file which will have the properties similar to the Note struct — id, title and description.

Then create a global variable of list of note called notes which will initially be empty.
Then, we need rpcUrl, which stands for Remote Procedure Call, a url which requests for blockchain, data can be sent to. This can be found on Ganache.

RPC URL

PRO TRICK: This URL can be changed in Settings of Ganache!

Next, we need to define webSocketUrl which will be ‘ws://127.0.0.1:7545’.

Then, we need to grab private key of one of the accounts on Ganache (click on the key icon to find Private Key of any account). It’s not advised to use private keys in code, especially in production. You should use metamask with walletconnect instead. But we will look into it in some other tutorial.

Next, in the constructor we will call an init function. Then, create that asynchronous init function.This function will initialise our web3client from Web3dart package and call all other functions to get access to our deployed contract.
Create a global variable named web3client which will be initialised late and in init function, set it equal to web3client, passing in rpc url, then http client, from http package. Install this dependency in the project and finally, the socketconnector argument, which will be from web_socket_channel package. Install this as well. Now, return IOWebsocketchannel.connect, passing in the wsurl, casting it as string since socket connector requires streamchannel of string return type.

Then, we need to create getABI asynchronous function. As the name suggests, it will get ABI, which is Application Binary Interface, a standard way to interact with contracts.

If we go to the build function, we have a newdirectory named contracts, which came in after running truffle compile. It includes json file of our contracts from which we need to extract abi property.

So, we load the asset json file using rootBundle from package:flutter/src/services. Then convert the string to json. Then, create a global variable abi. In the function, we need to convert it to ContractABI from abi json we extracted which is done using ContractABI.fromJSON.
We will also create global variable contractAddress which can be found in 5777 property, inside networks property which is inside json abi. Contract Address is stored by converting the hex value of address property into EthereumAddress type using EthereumAddress.fromHex

NotesContract.json

Now,we need to get credentials, so we will create another function with global variable creds which will be equal to EthPreviateKey.fromHex and pass in the privateKey we globally set.

For the last part of setup, we need to create deployed contract assembling everything we have together and get access to the functions or variables that we created in our solidity smart contract. So create deployedContract, createNote, deleteNote, notes and noteCount variables globally. Only deployedContract is of the type DeployedContract because we need to make a deployedContract with abi and contract address. Rest of them are functions or variables, so we can make use of deployedContract to get access to these functions using _deployedContract.function. Make sure that the names passed in parameter are same as the ones in our contract.

Note that this property can only be used for public or external functions in our contract. Simply, call these functions in init function so that they run whenever the constructor is called.

Flutter App Connection — GET Notes

Now, we need to fetch the notes so create that function and make use of web3client to call the function. We need to pass in the contract which is our deployedContract, function, which will be the noteCount and params as empty list since we don’t need to pass in anything for noteCount. This will give us a list with 1 element, which will be BigInt so we need to extract it and convert to int.
Then, we will clear off all the notes in the list. After that, run a for loop from 0 to the total tasks and use web3client.call, passing in the contract, the notes function and params which needs to be a list and bigInt so convert int to BigInt like this and store result in temp variable.
temp is a list with values of id, title and description at indexes 0,1 and 2 respectively (in the order we store the data in our Note struct in Smart Contract). So, if title (i.e. temp[1]) is not empty, we will add to the notes variable, a note model, with id as temp at 0,which will be BigInt converted to int, then title and description. After the for loop we will notify all the listeners about the notes variable change. Then, call this function in getDeployedContract.

Provider Config

Go in the main.dart file, wrap the app with ChangeNotifierProvider, passing in NotesServices in create property. Check out Provider Documentation to know more about it.

Flutter App Connection — Create Note

BUT we won’t see any change in our app, it’s still blank because there’s no note added. So create a function to add note, taking title and description from the parameter. Whenever, we need to send data to blockchain, we use web3client.sendTransaction (and whenever fetch data web3client.call), passing in credentials, then Transaction.callContract, passing in the contract, function to be used and then parameters, according to the position of the parameters in our smart contract, title and description. Then, we will fetch the notes since it’s not realtime.

Finally, bind the addNote function to our UI.

In UI Class
Add Button

Also, make sure itemCount, title and subtitle in the listviewbuilder are passed in correctly like this.

Ignore the deleteNote part, it’s the next section

App Test (Important)

Let’s test the app now! If an error comes up while running on an Android Emulator, make these changes.

Click on the Add button after entering title and description. We see a new listtile containing our data appearing after a while. Even when refreshed, data is persisted, that means data is stored on Blockchain. Now, let’s implement loading indicator.

Loading…

In NotesServices, create a global variable named isLoading set to true. In fetchNotes, after fetching the notes, set isLoading to false. In addNote, before fetching notes, set isLoading to true and notify listeners.

Global Variable in NotesServices
In fetchNotes
In addNotes

Then, in home screen’s body. Check if isLoading is true, if it is, display loading indicator else the widget tree.

Restart the app to see loading indicator while the notes are being fetched.

Flutter App Connection — Delete Note

Similar to addNote, create a function for delete note, taking id as parameter and call web3client.sendTransaction, pass in the credentials and then Transaction.callacontract with deployedContract, deleteNote function and for parameters id, converted to bigint.
Set isLoading to true, notify the listeners and fetch notes again.

Bind this function to the delete button and click on delete icon. The note will disappear.

Congrats on building your first Decentralised app! Thank you for reading, see you in the next one (hopefully)!

Source Code: https://github.com/RivaanRanawat/flutter-dapp-tutorial

Find Me Here:

--

--

Rivaan Ranawat

Hello There! I am Rivaan Ranawat, 19 year old coding enthusiast.