Create a Simple Stock Price Tracker in Flutter
Tags: flutter, appwrite, tracker, dart
Published: 2022-09-23 10:19:52

---
title: Create a Simple Stock Price Tracker in Flutter
published: true
description: Learn how to create a stock price tracker in Flutter using the Marketstack open API to search for our favourite stock details and save the details on Appwrite’s database.
tags: #flutter #appwrite #tracker #dart
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/67h056abakfqp94qf30s.png
# Use a ratio of 100:42 for best results.
---
Trackers are mechanisms, applications, or systems used by investors to track the financial performance of a particular stock. They give investors the required leverage to make a profitable financial decision as an individual or a company.

In this post, we will learn how to create a stock price tracker in Flutter using the [Marketstack](https://marketstack.com/) open API to search for our favourite stock details and save the details on Appwrite’s database.

## Prerequisites

To fully grasp the concepts presented in this tutorial, we require the following:

- Basic understanding of Dart and Flutter
- Flutter [SDK](https://flutter.dev/docs/get-started/install) installed
- [Xcode](https://developer.apple.com/xcode/) with a developer account (for Mac users)
- Either [IOS Simulator](https://developer.apple.com/documentation/xcode/running-your-app-in-the-simulator-or-on-a-device), [Android Studio](https://developer.android.com/studio), or [Chrome web browser](https://support.google.com/chrome/answer/95346?hl=en-GB&co=GENIE.Platform%3DDesktop) to run our application
- Docker [installation](https://www.docker.com/get-started/)
- An Appwrite instance; check out this [article](https://dev.to/hackmamba/create-a-local-appwrite-instance-in-3-steps-19n9?utm_source=hackmamba&utm_medium=hackmamba) on how to set up an instance locally or one-click install on [DigitalOcean](https://marketplace.digitalocean.com/apps/appwrite) or [Gitpod](https://gitpod.io/#https://github.com/appwrite/integration-for-gitpod)
- Marketstack account. **[Sign up](https://marketstack.com/product) is completely free**

## Getting Started

In this post, we will focus on implementations only. The project UI has already been set up.

To get started, we need to clone the project by navigating to the desired directory and running the command below:

```bash
git clone https://github.com/Mr-Malomz/stock_tracker && cd stock_tracker
```

The complete source code is also available on the [**dev**](https://github.com/Mr-Malomz/stock_tracker/tree/dev) branch of the same repository.

## Running the Project

First, we need to install the project dependencies by running the command below:

```bash
flutter pub get
```

Then run the project using the command below:

```bash
flutter run
```

The command above will run the application on the selected device.

![Home Screen](https://paper-attachments.dropbox.com/s_3E357D761EE954E337F5B7F4B0843EB2F74DE4CF333753C636F0F58FD6FC1D78_1660818851323_Screenshot+2022-08-18+at+11.32.31.png)
![Favourite Screen](https://paper-attachments.dropbox.com/s_3E357D761EE954E337F5B7F4B0843EB2F74DE4CF333753C636F0F58FD6FC1D78_1660818861277_Screenshot+2022-08-18+at+11.33.34.png)

## Creating a New Appwrite Project

To create a new project, start the Appwrite instance and navigate to the specified hostname and port `http://localhost:80`. Next, we need to log into our account or create an account if we don’t have one. 


![Appwrite Running](https://paper-attachments.dropbox.com/s_9C6F95223612512F2BFCBBAA719BAF62E5E51518F8EC1B3A16905A2D91336244_1650158621491_screencapture-localhost-auth-signup-2022-04-17-02_22_56.png)


On the console, click the **Create Project** button, input `stock_tracker` as the name, and click **Create**.

![Create Project](https://paper-attachments.dropbox.com/s_9C6F95223612512F2BFCBBAA719BAF62E5E51518F8EC1B3A16905A2D91336244_1650159125946_Screenshot+2022-04-17+at+02.28.39.png)
![Enter Project Name](https://paper-attachments.dropbox.com/s_BDB4B2E9DEBC947CF21789BC4224D1B5D1E6999B18844A1ABC22F2142E2E2A61_1653953612228_Screenshot+2022-05-30+at+15.00.51.png)


Next, we need to create a database to save our favourite stocks. Navigate to the **Database** tab, click **Add Database**, input `stocks` as the database name, and click **Create**.

![Click on Add Database](https://paper-attachments.dropbox.com/s_1AF7D1BE0ADF7EDE6E675290822A2A4A6CAEEB0CB3D05C84CB9D946F913A7CD3_1657817537975_Screenshot+2022-07-14+at+17.50.40.png)
![Enter Database Name & Create](https://paper-attachments.dropbox.com/s_1AF7D1BE0ADF7EDE6E675290822A2A4A6CAEEB0CB3D05C84CB9D946F913A7CD3_1657817547622_Screenshot+2022-07-14+at+17.52.06.png)


With that done, we need to create a collection for grouping our stocks. Click **Add Collection**, input `stock_list` as the collection name, and click on **Create**.

![Click on Add Collection](https://paper-attachments.dropbox.com/s_9C6F95223612512F2BFCBBAA719BAF62E5E51518F8EC1B3A16905A2D91336244_1650179872134_Screenshot+2022-04-17+at+08.09.34.png)
![Enter Collection Name & Create](https://paper-attachments.dropbox.com/s_1AF7D1BE0ADF7EDE6E675290822A2A4A6CAEEB0CB3D05C84CB9D946F913A7CD3_1657817955690_Screenshot+2022-07-13+at+10.21.14.png)


Appwrite has an advanced yet flexible way to manage access to users, teams, or roles to specific resources. We will modify the permission `role:all` to enable access from any application. Then click on **Update** to save changes.

![Modify Permission](https://paper-attachments.dropbox.com/s_1AF7D1BE0ADF7EDE6E675290822A2A4A6CAEEB0CB3D05C84CB9D946F913A7CD3_1657844341998_Screenshot+2022-07-13+at+10.21.59.png)

## Add Attributes

Attributes are fields that our database will possess. Navigate to the Attributes tab, click on **Add Attributes**, add a `New String Attribute` and size of `225` for **name**, **symbol**, **open**, and **exchange** fields with the `required` option marked, and click on **Create**.

![Select Attribute](https://paper-attachments.dropbox.com/s_9C6F95223612512F2BFCBBAA719BAF62E5E51518F8EC1B3A16905A2D91336244_1650181607632_Screenshot+2022-04-17+at+08.41.22.png)
![Create Field](https://paper-attachments.dropbox.com/s_9C6F95223612512F2BFCBBAA719BAF62E5E51518F8EC1B3A16905A2D91336244_1650181638011_Screenshot+2022-04-17+at+08.45.03.png)


***Note****: The* `*name*`*,* `*symbol*`*,* `*open*`*, and* `*exchange*` *fields represent the name of the stock, ticker or symbol of the stock, the opening price, and the stock marketplace, respectively.*


## Setting up Marketstack

With that done, we need to create an account on Marketstack and get an **Access Key** to use the open stock API.

![Create a Free Account](https://paper-attachments.dropbox.com/s_3E357D761EE954E337F5B7F4B0843EB2F74DE4CF333753C636F0F58FD6FC1D78_1660825644620_Screenshot+2022-08-16+at+23.31.05.png)
![Access Key](https://paper-attachments.dropbox.com/s_3E357D761EE954E337F5B7F4B0843EB2F74DE4CF333753C636F0F58FD6FC1D78_1660825644628_Screenshot+2022-08-16+at+23.34.13.png)


Next, we need to navigate to the `lib` directory and create a `utils.dart` file and add the snippet below:

```dart
    class AppConstant {
      //marketstack
      final String accessKey = "REPLACE WITH ACCESS KEY";
    }
```

## Connecting Appwrite to Flutter

To add support for our Flutter app, navigate to the **Home** menu, click on the **Add Platform** button, and select **New Flutter App**.

![Add Platform](https://paper-attachments.dropbox.com/s_9C6F95223612512F2BFCBBAA719BAF62E5E51518F8EC1B3A16905A2D91336244_1650183238195_Screenshot+2022-04-17+at+09.11.20.png)
![Select New Flutter App](https://paper-attachments.dropbox.com/s_9C6F95223612512F2BFCBBAA719BAF62E5E51518F8EC1B3A16905A2D91336244_1650183238206_Screenshot+2022-04-17+at+09.12.40.png)


Depending on the device we are running our Flutter application, we can modify it as shown below.

**i****OS**
To obtain our Bundle ID, we can navigate using the path below:
**ios > Runner.xcodeproj > project.pbxproj**

Open the `project.pbxproj` file and search for `PRODUCT_BUNDLE_IDENTIFIER`.

![iOS](https://paper-attachments.dropbox.com/s_3E357D761EE954E337F5B7F4B0843EB2F74DE4CF333753C636F0F58FD6FC1D78_1660826110812_Screenshot+2022-08-18+at+02.04.44.png)


Next, open the project directory on [Xcode](https://developer.apple.com/xcode/), open the `Runner.xcworkspace` folder in the app's iOS folder, select the Runner project in the Xcode project navigator, select the Runner target in the main menu sidebar, and then select **i****OS 11** in the deployment info’s target.

![Change Deployment Target](https://paper-attachments.dropbox.com/s_9C6F95223612512F2BFCBBAA719BAF62E5E51518F8EC1B3A16905A2D91336244_1650208061820_Screenshot+2022-04-17+at+16.05.22.png)


**Android**
To get our package name, we can navigate using the path below:
**android > app > src > debug > AndroidManifest.xml**

Open the `AndroidManifest.xml` file and copy the `package` value.

![Android](https://paper-attachments.dropbox.com/s_3E357D761EE954E337F5B7F4B0843EB2F74DE4CF333753C636F0F58FD6FC1D78_1660826166074_Screenshot+2022-08-18+at+02.06.27.png)


Next, we need to modify the `AndroidManifext.xml` as shown below:

```xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.stock_tracker">
    <uses-permission android:name="android.permission.INTERNET"/>
    <application ...>
    <activity android:name="com.linusu.flutter_web_auth.CallbackActivity" android:exported="true">
      <intent-filter android:label="flutter_web_auth">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="appwrite-callback-[PROJECT_ID]" />
      </intent-filter>
    </activity>
  </application>
</manifest>
```

Then, we need to update the  `utils.dart` file as shown below:
```dart
class AppConstant {
  //marketstack
  final String accessKey = "REPLACE WITH ACCESS KEY";

  //appwrite
  final String databaseId = "REPLACE WITH DATABASE ID";
  final String projectId = "REPLACE WITH PROJECT ID";
  final String endpoint = "REPLACE WITH ENDPOINT";
  final String collectionId = "REPLACE WITH COLLECTION ID";
}
```
Navigate to the **Database** menu, click on the `Stocks` database, click on the **Settings** tab, and copy the `Database ID`.

![Copy Database ID](https://paper-attachments.dropbox.com/s_3E357D761EE954E337F5B7F4B0843EB2F74DE4CF333753C636F0F58FD6FC1D78_1660827093394_Screenshot+2022-08-18+at+13.44.55.png)


Switch to the **Collections** tab, click on the `stock_list` collection, click on the **Settings** tab, and copy the `Collection ID`.

![Click on the stock_list](https://paper-attachments.dropbox.com/s_3E357D761EE954E337F5B7F4B0843EB2F74DE4CF333753C636F0F58FD6FC1D78_1660827118383_Screenshot+2022-08-18+at+13.46.25.png)
![Copy the Collection ID](https://paper-attachments.dropbox.com/s_3E357D761EE954E337F5B7F4B0843EB2F74DE4CF333753C636F0F58FD6FC1D78_1660827127615_Screenshot+2022-08-18+at+13.48.50.png)


Navigate to the **Settings** menu to copy the `Project ID` and `API Endpoint`.

![Copy Project ID and API Endpoint](https://paper-attachments.dropbox.com/s_3E357D761EE954E337F5B7F4B0843EB2F74DE4CF333753C636F0F58FD6FC1D78_1660827157800_Screenshot+2022-08-18+at+13.49.49.png)



For the `endpoint` property, we need to modify it to work with our system's local network address. We can adjust accordingly:

**i****OS**
Navigate to the **Network** section, copy the IP address, and modify it as shown below:
```dart
class AppConstant {
  //marketstack
  final String accessKey = "REPLACE WITH ACCESS KEY";

  //appwrite
  final String databaseId = "REPLACE WITH DATABASE ID";
  final String projectId = "REPLACE WITH PROJECT ID";
  final String endpoint = "http://192.168.1.195/v1";
  final String collectionId = "REPLACE WITH COLLECTION ID";
}
```
![](https://paper-attachments.dropbox.com/s_9C6F95223612512F2BFCBBAA719BAF62E5E51518F8EC1B3A16905A2D91336244_1650365320233_Screenshot+2022-04-18+at+19.38.08.png)


**Android**
We can connect our Android emulator to the system’s IP using the `10.0.2.2` IP address.
```dart
class AppConstant {
  //marketstack
  final String accessKey = "REPLACE WITH ACCESS KEY";

  //appwrite
  final String databaseId = "REPLACE WITH DATABASE ID";
  final String projectId = "REPLACE WITH PROJECT ID";
  final String endpoint = "http://10.0.2.2/v1";
  final String collectionId = "REPLACE WITH COLLECTION ID";
}
```

## Building the Stock Price Tracker

To get started, we need to create models to convert the response sent from Marketstack and Appwrite to a Dart object. The models will also cater to JSON serialization. To do this, add the snippet below in the same `utils.dart` file:
```dart

class AppConstant {
  //code goes here
}

class TickerResponse {
  String name;
  String symbol;

  TickerResponse({required this.name, required this.symbol});

  factory TickerResponse.fromJson(Map<dynamic, dynamic> json) {
    return TickerResponse(
      name: json['name'],
      symbol: json['symbol'],
    );
  }
}

class PriceResponse {
  String open;
  String exchange;

  PriceResponse({required this.open, required this.exchange});

  factory PriceResponse.fromJson(Map<dynamic, dynamic> json) {
    return PriceResponse(
      open: json['open'],
      exchange: json['exchange'],
    );
  }
}

class Stock {
  String? $id;
  String name;
  String symbol;
  String open;
  String exchange;

  Stock({
    this.$id,
    required this.name,
    required this.symbol,
    required this.open,
    required this.exchange,
  });

  factory Stock.fromJson(Map<dynamic, dynamic> json) {
    return Stock(
      $id: json['\$id'],
      name: json['name'],
      symbol: json['symbol'],
      open: json['open'],
      exchange: json['exchange'],
    );
  }

  Map<dynamic, dynamic> toJson() {
    return {'name': name, 'symbol': symbol, 'open': open, 'exchange': exchange};
  }
}
```

The snippet above does the following:

- Creates a `TickerResponse`, `PriceResponse`, and `Stock` classes with required properties
- Adds a constructor with unrequired and required parameters
- Creates a `fromJson` and `toJson` method for JSON serialization

Next, we need to create a service file to separate the application core logic from the UI. To do this, create a `stock_service.dart` file inside the `lib` directory and first update it by doing the following:
```dart

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:stock_tracker/utils.dart';
import 'package:appwrite/appwrite.dart';

class StockService {
  final _accessKey = AppConstant().accessKey;
  Client client = Client();
  Databases? db;

  StockService() {
    _init();
  }

  //initialize the application
  _init() async {
    client
        .setEndpoint(AppConstant().endpoint)
        .setProject(AppConstant().projectId);
    db = Databases(client, databaseId: AppConstant().databaseId);
    //get current session
    Account account = Account(client);
    try {
      await account.get();
    } on AppwriteException catch (e) {
      if (e.code == 401) {
        account
            .createAnonymousSession()
            .then((value) => value)
            .catchError((e) => e);
      }
    }
  }
}
```

The snippet above does the following:

- Imports the required dependencies
- Creates a `StockService` class with `_accessKey`, `client`, and `db` properties to connect to Marketstack, Appwrite instance, and the database
- Creates an `_init` method that configures the Appwrite using the property and also conditionally creates an anonymous user to access the Appwrite database

Finally, we can use the configuration above to create the required methods by updating as shown below:

```dart

//imports goes here

class StockService {
  final _accessKey = AppConstant().accessKey;
  Client client = Client();
  Databases? db;

  StockService() {
    _init();
  }

  //initialize the application
  _init() async {
    //init code goes here
  }

  Future<TickerResponse> _getTicker(String ticker) async {
    String tickerKey = ticker.toUpperCase();
    var response = await http.get(Uri.parse(
        'http://api.marketstack.com/v1/tickers/$tickerKey?access_key=$_accessKey&limit=1'));
    var data = jsonDecode(response.body);
    if (response.statusCode == 200) {
      return TickerResponse(name: data['name'], symbol: data['symbol']);
    } else {
      throw Exception('Error getting ticker');
    }
  }

  Future<PriceResponse> _getPrice(String ticker) async {
    String tickerKey = ticker.toUpperCase();
    var response = await http.get(Uri.parse(
        'http://api.marketstack.com/v1/eod?access_key=$_accessKey&symbols=$tickerKey&limit=1'));
    var data = jsonDecode(response.body);
    if (response.statusCode == 200) {
      return PriceResponse(
          open: data['data'][0]['open'].toString(),
          exchange: data['data'][0]['exchange']);
    } else {
      throw Exception('Error getting price');
    }
  }

  Future saveStock(String ticker) async {
    try {
      var tickerDetails = await _getTicker(ticker);
      var priceDetails = await _getPrice(ticker);
      Stock newStock = Stock(
        name: tickerDetails.name,
        symbol: tickerDetails.symbol,
        open: priceDetails.open,
        exchange: priceDetails.exchange,
      );
      var data = await db?.createDocument(
        collectionId: AppConstant().collectionId,
        documentId: 'unique()',
        data: newStock.toJson(),
      );
      return data;
    } catch (e) {
      throw Exception('Error creating stock');
    }
  }

  Future<List<Stock>> getFavStocks() async {
    try {
      var data =
          await db?.listDocuments(collectionId: AppConstant().collectionId);
      var stockList =
          data?.documents.map((stock) => Stock.fromJson(stock.data)).toList();
      return stockList!;
    } catch (e) {
      throw Exception('Error getting list of products');
    }
  }
}
```

The snippet above does the following:

- Creates a `_getTicker` and `_getPrice` helper methods that use the Marketstack API and Access Key to get stock details and price
- Creates a `saveStock` method that uses the helper methods to get required data and uses the configured database `createDocument` function to create a stock by passing in the required arguments
- Creates a `getFavStocks` method that uses the configured database’s `listDocuments` function to get a list of stocks and converts the returned JSON to a list using the `Stock.fromJson` method

**Note**: *The underscore* `_` *prefixing variables and functions in Dart marks them as a private entity of a class, while the* `unique()` *flag passed to the* `createDocument` *function tells Appwrite to auto-generate a unique ID.*


## Consuming the Service

With that done, we can start using the service to perform the required operation.

**Search for Stock and Add to Favourites**
To search for stock and add to favourite, we need to navigate to the `screens` folder, open the `home.dart`, and modify as shown below:

```dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:stock_tracker/stock_service.dart'; //add

class Home extends StatefulWidget {
  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  final _formKey = GlobalKey<FormState>();
  final TextEditingController _ticker = TextEditingController();
  bool _isLoading = false;

  _getTickerAndSave() {
    setState(() {
      _isLoading = true;
    });
    StockService().saveStock(_ticker.text).then((value) {
      setState(() {
        _isLoading = false;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Stock saved successfully!')),
      );
    }).catchError((_) {
      setState(() {
        _isLoading = false;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Error saving stock!')),
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 30.0),
        child: Form(
          key: _formKey,
          child: Column(
            children: [
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  //title
                  Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const Text(
                        'Ticker',
                        style: TextStyle(
                          color: Colors.grey,
                          fontSize: 14.0,
                        ),
                      ),
                      const SizedBox(height: 5.0),
                      TextFormField(
                        validator: (value) {
                          if (value == null || value.isEmpty) {
                            return 'Please input ticker';
                          }
                          return null;
                        },
                        inputFormatters: [LengthLimitingTextInputFormatter(25)],
                        controller: _ticker,
                        decoration: InputDecoration(
                          contentPadding: const EdgeInsets.symmetric(
                              vertical: 10, horizontal: 20),
                          hintText: "enter ticker",
                          focusedBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(10),
                            borderSide: const BorderSide(color: Colors.grey),
                          ),
                          enabledBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(10),
                            borderSide: const BorderSide(color: Colors.grey),
                          ),
                          errorBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(10),
                            borderSide: const BorderSide(color: Colors.red),
                          ),
                        ),
                      ),
                    ],
                  ),
                ],
              ),
              const SizedBox(height: 30.0),
              SizedBox(
                height: 45,
                width: double.infinity,
                child: TextButton(
                  onPressed: _isLoading
                      ? null
                      : () {
                          if (_formKey.currentState!.validate()) {
                            _getTickerAndSave();
                          }
                        },
                  style: ButtonStyle(
                    backgroundColor:
                        MaterialStateProperty.all<Color>(Colors.blue),
                  ),
                  child: const Text(
                    'Search and Save Stock',
                    style: TextStyle(
                      color: Colors.white,
                      fontWeight: FontWeight.bold,
                      fontSize: 14.0,
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
```

The snippet above does the following:

- Imports the required dependency 
- **Line 12 - 13**: Creates a `_ticker` and `_isLoading` variable to control input and loading state
- **Line 15 - 34**: Creates a `_getTickerAndSave` method that uses the `StockService().saveStock` service to search and save stock, set states, and uses the `snackbar` to show the action performed
- **Line 68**: Adds the `_ticker` as a controller to manage input
- **Line 96 - 102**: Calls the `_getTickerAndSave` method when the search and save button is pressed

**Get the List of Favourite Stocks**
To get the list of stocks added as a favourite, we need to open the `favourite.dart` file in the same `screens` folder and modify as shown below:

```dart

import 'package:flutter/material.dart';
import 'package:stock_tracker/stock_service.dart'; //add
import 'package:stock_tracker/utils.dart'; //add

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

  @override
  State<Favourites> createState() => _FavouritesState();
}

class _FavouritesState extends State<Favourites> {
  List<Stock>? stocks;
  bool _isLoading = false;
  bool _isError = false;

  @override
  void initState() {
    _getFavStockList();
    super.initState();
  }
  _getFavStockList() {
    setState(() {
      _isLoading = true;
    });
    StockService().getFavStocks().then((value) {
      setState(() {
        stocks = value;
        _isLoading = false;
      });
    }).catchError((e) {
      setState(() {
        _isLoading = false;
        _isError = true;
      });
    });
  }

  Widget build(BuildContext context) {
    return _isLoading
        ? const Center(
            child: CircularProgressIndicator(
            color: Colors.blue,
          ))
        : _isError
            ? const Center(
                child: Text(
                  'Error loading stocks',
                  style: TextStyle(
                    color: Colors.red,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              )
            : stocks!.isEmpty
                ? const Center(
                    child: Text(
                      'No stock added to favourites yet',
                      style: TextStyle(
                        color: Colors.grey,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                  )
                : ListView.builder(
                    itemCount: stocks?.length,
                    itemBuilder: (context, index) {
                      return Container(
                        decoration: const BoxDecoration(
                          border: Border(
                            bottom: BorderSide(width: .5, color: Colors.grey),
                          ),
                        ),
                        padding: EdgeInsets.fromLTRB(10, 20, 10, 20),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                          children: [
                            Expanded(
                              flex: 7,
                              child: Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  Text(
                                    stocks![index].symbol,
                                    style: TextStyle(
                                        color: Colors.black,
                                        fontWeight: FontWeight.w800),
                                  ),
                                  SizedBox(height: 10.0),
                                  Text(stocks![index].name)
                                ],
                              ),
                            ),
                            Column(
                              crossAxisAlignment: CrossAxisAlignment.end,
                              children: [
                                Text(
                                  '\$ ' + stocks![index].open,
                                  style: TextStyle(
                                      color: Colors.blue,
                                      fontWeight: FontWeight.w800),
                                ),
                                SizedBox(height: 10.0),
                                Text(stocks![index].exchange)
                              ],
                            ),
                          ],
                        ),
                      );
                    },
                  );
  }
}
```

The snippet above does the following:

- Imports the required dependencies 
- **Line 13 - 15**: Creates the `stocks`, `_isLoading`, and `_isError` properties to manage the application state
- **Line 18 - 37**: Creates a `_getFavStockList` method to get the list of stocks added as favourite using the `StockService().getFavStocks` service, sets states accordingly, and uses the `initState`  method to call the `_getFavStockList` method when the object is inserted into the tree
- Modifies the UI widgets to show the stocks added as favourite coming from Appwrite


With that done, we restart the application using the code editor or run the command below:

```bash
flutter run
```

![](https://media.giphy.com/media/3iUrYHkpDGSlEUNUFv/giphy.gif)


[https://media.giphy.com/media/3iUrYHkpDGSlEUNUFv/giphy.gif](https://media.giphy.com/media/3iUrYHkpDGSlEUNUFv/giphy.gif)

We can also validate the entries by navigating to the **Database** section of Appwrite’s management console.

![Data on Appwrite](https://paper-attachments.dropbox.com/s_3E357D761EE954E337F5B7F4B0843EB2F74DE4CF333753C636F0F58FD6FC1D78_1660851161805_Screenshot+2022-08-18+at+20.32.29.png)

# Conclusion

This post discussed how to create a stock price tracker in Flutter and Appwrite. The Appwrite platform ships with services that speed up development processes. Try it out today and focus on what matters while Appwrite takes care of the tricky part.

These resources might be helpful:

- [Appwrite official documentation.](https://appwrite.io/docs?utm_source=hackmamba&utm_medium=hackmamba-blog)
- [Appwrite Flutter SDK.](https://appwrite.io/docs/getting-started-for-flutter?utm_source=hackmamba&utm_medium=hackmamba-blog)
- [Marketstack API Documentation](https://marketstack.com/documentation)