# Implementing Smart Contracts On Flutter DApps

## Decentralized Application

A decentralized application(DApp) is an application that is meant to run in a decentralized computing system/blockchain and it was initially introduced by Ethereum Blockchain which is built using smart contracts. When using the Ethereum ecosystem, we have to use [Solidity](https://docs.soliditylang.org/en/v0.8.7/), a language which was built for writing smart contracts. Solidity is an OOP, high-level language for writing and implementing smart contracts that can later govern accounts and transactions on the Ethereum blockchain. In this article, we will build a web3 DApp project using Solidity and Flutter. We will use the [`web3dart`](https://pub.dev/packages/web3dart) package to interact with the Ethereum blockchain in our Flutter application.

> Note: When following this tutorial, I am assuming that the reader has basic knowledge of Ethereum, smart contracts and the Flutter framework.

**Things we will be covering in this article:**

- Setting up development for Solidity and Flutter app.
- Setting up Truffle project
- Creating smart contracts in solidity
- Compiling and migrating smart contract
- Linking smart contracts in the Flutter app
- Creating awesome UI for the Flutter app

We will be creating a note-taking app in which all the notes will be stored in a blockchain using smart contracts.


## Setting up development for Solidity

We will be using [Truffle](https://www.trufflesuite.com/) for developing smart contracts locally. Truffle is a popular framework for building and testing smart contracts using the Ethereum Virtual Machine(EVM). Truffle can be easily installed using the following command:

```bash
npm install -g truffle
```

We will use [Ganache](https://www.trufflesuite.com/ganache), which provides a testing Ethereum blockchain so that we can test and deploy smart contracts before publishing it.

## Setting up the Truffle project


- To initialize Truffle, we need to create a Flutter project first using the following command:

```bash
flutter create notetaking_dapp
cd notetaking_dapp
```


- Once we have the basic Flutter app ready, we can initialize truffle by creating a folder named `contracts` and running:

```bash
mkdir contracts
cd contracts
truffle init
```

![Screenshot 2021-08-19 at 11.59.58 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1629354604501/klFPbBQvQ.png)


- This command will create the following folders for you:

- `contracts/` : Contains smart contract code.
- `migrations/`: Contains migrations scripts which will be used be truffle to handle deployment.
- `test/`: Contains test scripts
- `truffle-config.js`: Contains truffle configurations

## Creating smart contracts in Solidity


- Create `NotesContract.sol` file in `contracts/` directory and add the following code:

```
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.5.16;

contract NotesContract {
    uint256 public notesCount;

    struct Notes {
        uint256 id;
        string noteTitle;
        string noteContent;
        uint256 timeStamp;
    }

    mapping(uint256 => Notes) public notes;

    constructor() public {
        notesCount = 0;
    }

    event NoteAdded(uint256 _id);
    event NoteDeleted(uint256 _id);
    event NoteEdited(uint256 _id);
}
```
- `notesCount` is an unsigned public integer that stores the total number of notes in the smart contract.
- `struct Notes` is the data structure for a note. It defines if and whether a note should contain an id, a title, content and timestamp. In this scenario, timestamp is an unsigned integer as we are already storing the time stamp in the Unix epoch. Unix epoch is the number of seconds that have elapsed since January 1, 1970.
- `notes` is the map that stores all the notes with an `id` as key and `Notes struct` as value.
- Create a constructor and set `notesCount = 0`. `NoteAdded`, `NoteDeleted`, and `NoteEdited` are the events that will be emitted by blockchain so that all the DApps can listen to these emitted events and function accordingly. All these events will emit IDs of specific notes along with them. Execute the following code to witness this:

```
    function addNote(
        string memory _noteTitle,
        string memory _noteContent,
        uint256 timeStamp
    ) public {
        notes[notesCount] = Notes(
            notesCount,
            _noteTitle,
            _noteContent,
            timeStamp
        );
        emit NoteAdded(notesCount);
        notesCount++;
    }
```


- `addNote` function accepts note title, note content, and the time stamp as parameters. We have to create a new struct of Notes with these data and assign it to the current `notesCount` value in the `notes` map. Once we have added the `Notes` struct to the map, we have to emit the `NotesAdded` event with the current `notesCount`. After emitting the event we can increment the `notesCount` variable for the next note. Add the following function after the events to go to the next step:

```
    function deleteNote(uint256 _id) public {
        delete notes[_id];
        emit NoteDeleted(_id);
    }
```


- Create a `deleteNote` function which accepts the ID of the note to be deleted. First, we have to remove the Notes from the `notes` map corresponding to the received ID. After that, we can emit the `NoteDeleted` event using the following code:

```
    function editNote(
        uint256 _id,
        string memory _noteTitle,
        string memory _noteContent
    ) public {
        notes[_id] = Notes(_id, _noteTitle, _noteContent, notes[_id].timeStamp);
        emit NoteAdded(_id);
    }
```


- For updating notes, create the `editNote` function that accepts the ID of the note to be edited, title, and content. Now create a new struct with these values and assign it to the received ID in the `notes` map.

We are done with executing the smart contract code. Now we can compile and migrate smart contracts!

## Compiling and migrating smart contract

In the terminal, go to the `contracts` directory and run the following command:

```
truffle compile
```

You should see an output similar to the image given below:

![Screenshot 2021-08-19 at 12.32.46 PM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1629356570234/3NM1VV6lT.png)

## Migration


- In the `migrations/` directory, you will notice a file called `1_initial_migration.js` which handles the deployment of the `Migrations.sol` contract. Each contract needs one migration file. Create a new file named `2_notes_contract_migration.js` in the `migrations/` directory and add the following code in that file:

```
const NotesContract = artifacts.require("NotesContract");

module.exports = function (deployer) {
  deployer.deploy(NotesContract);
};

```


- Before running the migration, we need a local blockchain up and running on our system. For that, start the Ganache app which will start an instance of Blockchain on port 7545 as displayed below:

![Screenshot 2021-08-19 at 12.39.26 PM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1629356971282/6m3eTmPAz.png)


- Now open the `truffle-config.js` and add the following content:

```js
module.exports = {
  networks: {
    development: {
      host: "127.0.0.1",
      port: 7545,
      network_id: "*",
    },
    advanced: {
      websocket: true,
    },
  },
  contract_build_directory: "./src/abis",
  compilers: {
    solc: {
      optimizer: {
        enabled: true,
        runs: 200,
      },
    },
  },
};

```


- Run the following code from the `contract/` directory:

```
 truffle migrate
```


- Once the above code is successfully executed, the first account will have slightly less than 100ETH as you can witness in Ganche which is due to the transaction cost of migrating smart contracts on the blockchain.

## Linking smart contracts 


- In your `pubspec.yaml` file add the following packages:

```yaml
  # Needed for interaction with smart contract
  http: ^0.13.3
  web3dart: ^2.1.4
  web_socket_channel: ^2.1.0
  provider: ^5.0.0

  # Needed for UI
  google_fonts: ^2.1.0
  flutter_staggered_grid_view: ^0.4.0
```


- Add `contracts/build/contracts/NotesContract.json` file as an asset in your `pubspec.yaml` which can also generated by the `truffle migrate` command. Use the following code:

```yaml
    assets:
    - contracts/build/contracts/NotesContract.json
```


- Now create a `note_controller.dart` file in the `lib` directory and add the following code:

```dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart';
import 'package:notetaking_dapp/models/note.dart';
import 'package:web3dart/web3dart.dart';
import 'package:web_socket_channel/io.dart';

class NoteController extends ChangeNotifier {
  List<Note> notes = [];
  bool isLoading = true;
  int noteCount;
  final String _rpcUrl = "http://127.0.0.1:7545";
  final String _wsUrl = "ws://127.0.0.1:7545/";

  final String _privateKey =
      "e3beb61794a259037a8ca3379f15d61a82e9bb631ba248ffe14283fdc90c04c8";

  Web3Client _client;
  String _abiCode;

  Credentials _credentials;
  EthereumAddress _contractAddress;
  DeployedContract _contract;

  ContractFunction _notesCount;
  ContractFunction _notes;
  ContractFunction _addNote;
  ContractFunction _deleteNote;
  ContractFunction _editNote;
  ContractEvent _noteAddedEvent;
  ContractEvent _noteDeletedEvent;
}
```
**Here are some important considerations to be looked into:**
- Importing all the required packages required.
- Creating a `ChangeNotifier` class called `NoteController`.
- Get the RPC URL from Ganache - 
![ganache-rpc.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1629358642444/ZJaGDDrZs.png)
- Get the Private key of any account from Ganache by clicking on the Key icon.
![Screenshot 2021-08-19 at 1.08.42 PM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1629358785947/8J8S5DmuH.png)
- `_client` variable can  used to connect to Ethereum node with `WebSockets`.
- `_abiCode` is used to store the ABI of the smart contract. Read [this](https://docs.soliditylang.org/en/v0.5.3/abi-spec.html)  to know more about ABI.
- `_credentials` will contain credentials of the smart contract deployer.
- `_contractAddress` will contain the smart contract's address on the blockchain.
- `_contract` is the instance of our smart contract which will be used to call all the functions.
- `_notesCount`, `_notes`, `_addNote`, `_deleteNote`, `_editNote` are the functions that are deployed on smart contracts.
- `_noteAddedEvent` and `_noteDeletedEvent` are the events emitted by our contract which can be listed in the flutter app.

- After variable declaration, create the following functions in `note_controller.dart`: 

```dart
 NoteController() {
    init();
  }

  init() async {
    _client = Web3Client(_rpcUrl, Client(), socketConnector: () {
      return IOWebSocketChannel.connect(_wsUrl).cast<String>();
    });
    await getAbi();
    await getCreadentials();
    await getDeployedContract();
  }

  Future<void> getAbi() async {
    String abiStringFile = await rootBundle
        .loadString("contracts/build/contracts/NotesContract.json");
    var jsonAbi = jsonDecode(abiStringFile);
    _abiCode = jsonEncode(jsonAbi['abi']);
    _contractAddress =
        EthereumAddress.fromHex(jsonAbi["networks"]["5777"]["address"]);
  }

  Future<void> getCreadentials() async {
    _credentials = await _client.credentialsFromPrivateKey(_privateKey);
  }

  Future<void> getDeployedContract() async {
    _contract = DeployedContract(
        ContractAbi.fromJson(_abiCode, "NotesContract"), _contractAddress);
    _notesCount = _contract.function("notesCount");
    _notes = _contract.function("notes");
    _addNote = _contract.function("addNote");
    _deleteeNote = _contract.function("deleteNote");
    _editNote = _contract.function("editNote");

    _noteAddedEvent = _contract.event("NoteAdded");
    _noteDeletedEvent = _contract.event("NoteDeleted");
    await getNotes();
  }

```

- In `getAbi()` function, we are fetching the `NotesContract.json` asset and decoding it to get json data. From that data, we can extract the ABI content and address of the deployed smart contract.
- In `getCreadentials()` function we will be passing our private key in `_client.credentialsFromPrivateKey()` function which returns an instance of the `Credentials` class.
- In `getDeployedContract()`, we will be creating an instance of `DeployedContract` using `abiCode`, contract name, and contract address. Once we have an instance of the contract we can create instances of all the functions and events we have in our smart contract as shown above.


- After these functions paste the following code to perform the CRUD operation.

```dart
  getNotes() async {
    List notesList = await _client
        .call(contract: _contract, function: _notesCount, params: []);
    BigInt totalNotes = notesList[0];
    noteCount = totalNotes.toInt();
    notes.clear();
    for (int i = 0; i < noteCount; i++) {
      var temp = await _client.call(
          contract: _contract, function: _notes, params: [BigInt.from(i)]);
      if (temp[1] != "")
        notes.add(
          Note(
            id: temp[0].toString(),
            title: temp[1],
            body: temp[2],
            created:
                DateTime.fromMillisecondsSinceEpoch(temp[3].toInt() * 1000),
          ),
        );
    }
    isLoading = false;
    notifyListeners();
  }

  addNote(Note note) async {
    isLoading = true;
    notifyListeners();
    await _client.sendTransaction(
      _credentials,
      Transaction.callContract(
        contract: _contract,
        function: _addNote,
        parameters: [
          note.title,
          note.body,
          BigInt.from(note.created.millisecondsSinceEpoch),
        ],
      ),
    );
    await getNotes();
  }

  deleteNote(int id) async {
    isLoading = true;
    notifyListeners();
    await _client.sendTransaction(
      _credentials,
      Transaction.callContract(
        contract: _contract,
        function: _deleteeNote,
        parameters: [BigInt.from(id)],
      ),
    );
    await getNotes();
  }

  editNote(Note note) async {
    isLoading = true;
    notifyListeners();
    print(BigInt.from(int.parse(note.id)));
    await _client.sendTransaction(
      _credentials,
      Transaction.callContract(
        contract: _contract,
        function: _editNote,
        parameters: [BigInt.from(int.parse(note.id)), note.title, note.body],
      ),
    );
    await getNotes();
  }
```

- `getNotes()` function is used to fetch all the notes present on the blockchain. First, we shall call the `_notesCount` function which will give us the total number of notes present in blockchain and then we can run a for loop, fetch each note and add it into a list of notes in Flutter.
- `addNote()` function is used when we want to create a new note. Since we are adding data to the blockchain this is considered as a transaction and hence we shall call the `_client.sendTransaction()` function and pass it through our `_credentials` which will then be used to pay gas fees for that transaction.
- `deleteNote()` function is used when we want to delete a specific note from the blockchain. Similar to the `addNote` function, we will be making changes in the state of the blockchain by deleting a note. Hence, we have to make a transaction as we did in the `addNote` function and pass `_credentials`.
- `editNote()` function is used to modify any data in a specific note. This is similar to add and delete note function.
- In `addNote`, `deleteNote` and `editNote` we will be calling `getNote()` function towards the end to get the updated notes from blockchain.

## Creating a UI for the Flutter app


- Before creating the UI we have to wrap out the `MaterialApp` widget in `main.dart` with `ChangeNotifierProvider` and pass an instance of `NoteController` so that we can use all the contract functions and values in our app. Refer to the code given below:

```dart
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => NoteController(),
      child: MaterialApp(
        title: 'Note Taking DApp',
        theme: ThemeData.dark(),
        home: HomeScreen(),
      ),
    );
  }
}
```


- Once we are done with setting up the provider for `NoteController`, we can start creating UI. You can create a `home_screen.dart` file and paste the following code to do this:

```dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:notetaking_dapp/controllers/note_controller.dart';
import 'package:notetaking_dapp/models/note.dart';
import 'package:notetaking_dapp/screens/notes_details_screen.dart';
import 'package:notetaking_dapp/screens/notes_edit_screen.dart';
import 'package:notetaking_dapp/utils/themes.dart';
import 'package:provider/provider.dart';

class HomeScreen extends StatefulWidget {
  HomeScreen({Key key}) : super(key: key);

  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  NoteController noteController;
  List<Note> notes;
  @override
  Widget build(BuildContext context) {
    noteController = Provider.of<NoteController>(context, listen: true);
    notes = noteController.notes;
    return Scaffold(
      floatingActionButton: GestureDetector(
        onTap: () {
          Navigator.of(context).push(
            MaterialPageRoute(
              builder: (context) => NotesEditScreen(),
            ),
          );
        },
        child: Container(
          height: 60,
          width: 60,
          decoration: BoxDecoration(
            color: Color(0xFF80DFEA),
            borderRadius: BorderRadius.circular(200),
          ),
          child: Center(
            child: Text(
              "+",
              style: TextStyle(
                fontSize: 40,
                fontWeight: FontWeight.w500,
              ),
            ),
          ),
        ),
      ),
      backgroundColor: ColorConstant.bg,
      body: AnnotatedRegion<SystemUiOverlayStyle>(
        value: SystemUiOverlayStyle.light,
        child: noteController.isLoading
            ? Center(
                child: CircularProgressIndicator(),
              )
            : SafeArea(
                child: Container(
                  padding: EdgeInsets.symmetric(horizontal: 25),
                  child: Column(
                    children: [
                      SizedBox(height: 30),
                      Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          Text(
                            "Notes",
                            style: GoogleFonts.montserrat(
                              fontSize: 40,
                              color: Colors.grey[100],
                            ),
                          ),
                          Container(
                            decoration: BoxDecoration(
                              color: ColorConstant.bgAccent,
                              borderRadius: BorderRadius.circular(15),
                            ),
                            padding: EdgeInsets.all(10),
                            child: Icon(
                              Icons.search,
                              color: Colors.white,
                              size: 30,
                            ),
                          ),
                        ],
                      ),
                      SizedBox(height: 30),
                      Expanded(
                        child: new StaggeredGridView.countBuilder(
                          crossAxisCount: 2,
                          itemCount: notes.length,
                          itemBuilder: (BuildContext context, int index) {
                            bool isThirdNote = (index + 1) % 3 == 0;
                            return GestureDetector(
                              onTap: () {
                                Navigator.of(context).push(
                                  MaterialPageRoute(
                                    builder: (context) => NotesDetailsScreen(
                                      note: notes[index],
                                    ),
                                  ),
                                );
                              },
                              child: Container(
                                decoration: BoxDecoration(
                                  color: ColorConstant.notesBg[index % 5],
                                  borderRadius: BorderRadius.circular(12),
                                ),
                                padding: EdgeInsets.all(25),
                                child: Column(
                                  mainAxisAlignment:
                                      MainAxisAlignment.spaceBetween,
                                  crossAxisAlignment: CrossAxisAlignment.start,
                                  children: [
                                    Text(
                                      notes[index].title,
                                      style: GoogleFonts.montserrat(
                                        fontSize: isThirdNote ? 32 : 24,
                                        fontWeight: FontWeight.w500,
                                      ),
                                      maxLines: 4,
                                      overflow: TextOverflow.ellipsis,
                                    ),
                                    Text(
                                      "May 21, 2021",
                                      style: GoogleFonts.montserrat(
                                        fontSize: 18,
                                        fontWeight: FontWeight.w500,
                                        color: ColorConstant
                                            .noteTextBg[index % 5]
                                            .withOpacity(1),
                                      ),
                                    ),
                                  ],
                                ),
                              ),
                            );
                          },
                          staggeredTileBuilder: (int index) =>
                              new StaggeredTile.count(
                                  (index + 1) % 3 == 0 ? 2 : 1, 1),
                          mainAxisSpacing: 16,
                          crossAxisSpacing: 16,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
      ),
    );
  }
}
```

## Sneak Peek

This is the Note-taking app we should have created by following this tutorial.

![ezgif.com-gif-maker.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1629353773744/yJ7bp5mhF.gif)

Here is the source code of this application for your  [reference.](https://github.com/viral-sangani/notes-dapp-flutter-solidity) 


## Conclusion

In this article, we have seen how we can create a smart contract using Solidity language while leveraging the Truffle framework to easily deploy and test out smart contracts locally. We have also learned how to integrate a smart contract using the `web3dart` package in a flutter. There are endless possibilities on what we can do with smart contracts and Decentralized apps. If you end up creating something cool do let me know [@Viral Sangani](https://twitter.com/viral_sangani_). 

That's all for this article. Thank you so much for reading 😊. 
