This Banner is For Sale !!
Get your ad here for a week in 20$ only and get upto 15k traffic Daily!!!

How to build a mood tracker app in Flutter


Over time, founders, motivational audio system, and different specialists have constructed instruments and methods to assist folks monitor their moods. It has helped folks spot patterns, develop coping mechanisms, and reply a few of their most profound questions on life.

On this submit, we are going to learn to construct a personalised temper tracker utilizing Appwrite’s database function in a Flutter utility. The mission’s GitHub repository will be discovered here.



Stipulations

To completely grasp the ideas introduced on this tutorial, the next are required:



Getting began

To get began, we have to clone the mission by navigating to the specified listing and operating the command beneath:

git clone https://github.com/Mr-Malomz/mood_tracker.git
Enter fullscreen mode

Exit fullscreen mode



Working the mission

First, we have to set up the mission dependencies by operating the command beneath:

    flutter pub get
Enter fullscreen mode

Exit fullscreen mode

Then, run the mission utilizing the next command:

    flutter run
Enter fullscreen mode

Exit fullscreen mode

The command above will run the appliance on the chosen system.

Home screen
Trend screen



Arrange temper tracker service on Appwrite

To get began, we have to log into our Appwrite console, click on the Create mission button, enter mood-tracker because the identify, after which click on Create.

Create project

Create a database, assortment, and attributes

With our mission created, we will arrange our utility database. First, navigate to the Database tab, click on the Create database button, enter trackers because the identify, after which click on Create.

Create database

Secondly, we have to create a group for storing a consumer’s temper. To do that, click on the Create assortment button, enter moods because the identify, after which click on Create.

Moods collection

Thirdly, we have to create attributes to symbolize our database fields. To do that, we have to navigate to the Attributes tab and create attributes for the gathering, as proven beneath:

Attribute key Attribute sort Measurement Required
fee integer YES
description string 5000 YES
createdAt datetime YES

Create attributes
Moods collection attributes

We additionally must replace our assortment permissions to handle them accordingly. To do that, navigate to the Settings tab, scroll to the Replace Permissions part, choose Any, mark accordingly, after which click on Replace.

Select any
Mark permission and update

Add platform assist

So as to add assist for our Flutter app, navigate to the House menu and click on the Flutter App button.

Add platform

We are able to modify the Flutter utility relying on the system used to run it, as proven beneath.

iOS

To acquire our Bundle ID, navigate to the trail beneath:
ios > Runner.xcodeproj > mission.pbxproj

Open the mission.pbxproj file and seek for PRODUCT_BUNDLE_IDENTIFIER.

Select new Flutter app

Subsequent, open the mission listing on Xcode, open the Runner.xcworkspace folder within the app’s iOS folder, choose the Runner mission within the Xcode mission navigator, choose the Runner goal within the major menu sidebar, after which choose iOS 11 within the deployment data’s goal.

Change deployment target

Android

To get our bundle identify, we will navigate to an XML file utilizing the next path:
android > app > src > debug > AndroidManifest.xml

Open the AndroidManifest.xml file and replica the bundle worth.

Android

Subsequent, let’s modify the AndroidManifext.xml as proven beneath:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        bundle="com.instance.mood_tracker">
        <uses-permission android:identify="android.permission.INTERNET"/>
        <utility ...>
        <exercise android:identify="com.linusu.mobile_wallet.CallbackActivity" android:exported="true">
          <intent-filter android:label="mood_tracker">
            <motion android:identify="android.intent.motion.VIEW" />
            <class android:identify="android.intent.class.DEFAULT" />
            <class android:identify="android.intent.class.BROWSABLE" />
            <knowledge android:scheme="appwrite-callback-[PROJECT_ID]" />
          </intent-filter>
        </exercise>
      </utility>
    </manifest>
Enter fullscreen mode

Exit fullscreen mode

Additionally, the highlighted [PROJECT_ID] should be changed with the precise Appwrite mission ID.



Constructing the temper tracker service

With all that completed, let’s construct the temper tracker service. First, we have to create fashions to transform the response despatched from Appwrite to a Dart object. The fashions will even cater to JSON serialization. To do that, we have to create a utils.dart file within the lib folder and add the snippet beneath:

    class AppConstant {
      ultimate String databaseId = "REPLACE WITH DATABASE ID";
      ultimate String projectId = "REPLACE WITH PROJECT ID";
      ultimate String collectionId = "REPLACE WITH COLLECTION ID";
      ultimate String endpoint = "ENDPOINT";
    }

    class Temper {
      String? $id;
      int fee;
      String description;
      DateTime? createdAt;

      Temper({
        this.$id,
        required this.fee,
        required this.description,
        required this.createdAt,
      });

      Map<dynamic, dynamic> toJson() {
        return {
          "fee": fee,
          "description": description,
          "createdAt": createdAt!.toIso8601String(),
        };
      }

      manufacturing unit Temper.fromJson(Map<dynamic, dynamic> json) {
        return Temper(
          $id: json['$id'],
          fee: json['rate'],
          description: json['description'],
          createdAt: DateTime.tryParse(json['createdAt']),
        );
      }
    }
Enter fullscreen mode

Exit fullscreen mode

We’ll want to change the endpoint property to work with our system’s native community tackle. We are able to regulate accordingly. Beneath are the directions for each iOS and Android.

iOS

Navigate to the Community part and replica the IP tackle.

IP address

Android
We are able to join our Android emulator to the system’s IP utilizing the 10.0.2.2 IP tackle.

ultimate String endpoint = "http://10.0.2.2/v1";
Enter fullscreen mode

Exit fullscreen mode

Observe: We are able to get the databaseId, missionId, and collectionId by navigating by means of the Appwrite console.

Subsequent, we should create a service file to separate the appliance core logic from the UI. To do that, create a mood_service.dart file contained in the lib listing. Then, add the snippet beneath:

    import 'bundle:appwrite/appwrite.dart';
    import 'bundle:mood_tracker/utils.dart';

    class MoodService {
      Shopper _client = Shopper();
      Databases? _db;

      MoodService() {
        _init();
      }

      //initialize the appliance
      _init() async {
        _client
            .setEndpoint(AppConstant().endpoint)
            .setProject(AppConstant().projectId);
        _db = Databases(_client);
        //get present session
        Account account = Account(_client);
        attempt {
          await account.get();
        } on AppwriteException catch (e) {
          if (e.code == 401) {
            account
                .createAnonymousSession()
                .then((worth) => worth)
                .catchError((e) => e);
          }
        }
      }

      Future createMood(int fee, String description) async {
        attempt {
          Temper newMood = Temper(
            fee: fee,
            description: description,
            createdAt: DateTime.now(),
          );
          var knowledge = await _db?.createDocument(
            databaseId: AppConstant().databaseId,
            collectionId: AppConstant().collectionId,
            documentId: ID.distinctive(),
            knowledge: newMood.toJson(),
          );
          return knowledge;
        } catch (e) {
          throw Exception('Error creating temper!');
        }
      }

      Future<Checklist<Temper>> getMoodList() async {
        attempt {
          var knowledge = await _db?.listDocuments(
            databaseId: AppConstant().databaseId,
            collectionId: AppConstant().collectionId,
          );
          var moodList =
              knowledge?.paperwork.map((temper) => Temper.fromJson(temper.knowledge)).toList();
          return moodList!;
        } catch (e) {
          throw Exception('Error getting listing of moods!');
        }
      }
    }
Enter fullscreen mode

Exit fullscreen mode

The snippet above does the next:

  • Imports the required dependencies
  • Creates a MoodService class with _client and _db properties to connect with the Appwrite occasion and the database
  • Creates an _init technique that configures Appwrite utilizing the properties and in addition conditionally creates an nameless consumer to entry the Appwrite database
  • Creates a createMood and getMoodList strategies that use the _db property to create and acquire a listing of saved moods accordingly



Consuming the service

With that completed, we will begin utilizing the service to carry out the required operation.

Creating moods

To get began, we have to modify the dwelling.dart file within the screens listing and replace it by doing the next:

First, we have to import the required dependencies and create a technique to avoid wasting the present temper to the database:

    import 'bundle:flutter/materials.dart';
    import 'bundle:flutter/providers.dart';
    import 'bundle:mood_tracker/mood_service.dart';

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

    class _HomeState extends State<House> {
      Checklist<int> listing = <int>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
      ultimate _formKey = GlobalKey<FormState>();
      ultimate TextEditingController _description = TextEditingController();
      late int _rate;
      bool _isLoading = false;

      _createMood() {
        setState(() {
          _isLoading = true;
        });
        MoodService().createMood(_rate, _description.textual content).then((worth) {
          setState(() {
            _isLoading = false;
          });
          _formKey.currentState!.reset();
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content material: Textual content('Temper saved efficiently!')),
          );
        }).catchError((_) {
          setState(() {
            _isLoading = false;
          });
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(content material: Textual content('Error saving temper!')),
          );
        });
      }

      @override
      Widget construct(BuildContext context) {
        //UI code goes right here
      }
    }
Enter fullscreen mode

Exit fullscreen mode

The snippet above does the next, damaged down by strains:

  • Traces 1-3: Import the required dependencies
  • Traces 13-15: Create the _description, _rate, and _isLoading properties to handle the appliance state
  • Traces 17-38: Create a _createMood technique to avoid wasting the present temper utilizing the MoodService().createMood service, set states accordingly

Lastly, we have to modify the UI to make use of the strategy and states created to course of the shape.

    //import goes right here

    class House extends StatefulWidget {
      //code goes right here
    }

    class _HomeState extends State<House> {
      //states goes right here

      _createMood() {
        //code goes right here
      }

      @override
      Widget construct(BuildContext context) {
        return Scaffold(
          physique: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 30.0),
            baby: Type(
              key: _formKey,
              baby: Column(
                kids: [
                  const Text('How are you feeling today',
                      style: TextStyle(
                        fontSize: 18.0,
                        fontWeight: FontWeight.bold,
                      )),
                  const SizedBox(height: 30.0),
                  Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      //title
                      Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          const Text(
                            'Rate your mood',
                            style: TextStyle(
                              color: Colors.grey,
                              fontSize: 14.0,
                            ),
                          ),
                          const SizedBox(height: 5.0),
                          DropdownButtonFormField(
                              items: list.map<DropdownMenuItem<int>>((int value) {
                                return DropdownMenuItem<int>(
                                  value: value,
                                  child: Text('$value'),
                                );
                              }).toList(),
                              validator: (value) {
                                if (value == null) {
                                  return 'Please rate your mood';
                                }
                                return null;
                              },
                              decoration: InputDecoration(
                                contentPadding: const EdgeInsets.symmetric(
                                    vertical: 10, horizontal: 20),
                                hintText: "select your mood",
                                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),
                                ),
                              ),
                              onChanged: ((value) {
                                setState(() {
                                  _rate = value!;
                                });
                              })),
                        ],
                      ),
                    ],
                  ),
                  const SizedBox(peak: 30.0),
                  Column(
                    crossAxisAlignment: CrossAxisAlignment.begin,
                    kids: [
                      const Text(
                        'Describing how you feel',
                        style: TextStyle(
                          color: Colors.grey,
                          fontSize: 14.0,
                        ),
                      ),
                      const SizedBox(height: 5.0),
                      TextFormField(
                        controller: _description,
                        validator: (value) {
                          if (value == null || value.isEmpty) {
                            return 'Please input your feeling';
                          }
                          return null;
                        },
                        inputFormatters: [LengthLimitingTextInputFormatter(70)],
                        ornament: InputDecoration(
                          contentPadding: const EdgeInsets.symmetric(
                              vertical: 10, horizontal: 20),
                          hintText: "describe your feeling",
                          fillColor: Colours.white,
                          focusedBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.round(10),
                            borderSide: const BorderSide(coloration: Colours.gray),
                          ),
                          enabledBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.round(10),
                            borderSide: const BorderSide(coloration: Colours.gray),
                          ),
                          errorBorder: OutlineInputBorder(
                            borderRadius: BorderRadius.round(10),
                            borderSide: const BorderSide(coloration: Colours.purple),
                          ),
                        ),
                        minLines: 6,
                        keyboardType: TextInputType.multiline,
                        maxLines: null,
                      ),
                    ],
                  ),
                  const SizedBox(peak: 30.0),
                  SizedBox(
                    peak: 45,
                    width: double.infinity,
                    baby: TextButton(
                      onPressed: _isLoading
                          ? null
                          : () {
                              if (_formKey.currentState!.validate()) {
                                _createMood();
                              }
                            },
                      model: ButtonStyle(
                        backgroundColor:
                            MaterialStateProperty.all<Colour>(Colours.blue),
                      ),
                      baby: const Textual content(
                        'Save',
                        model: TextStyle(
                          coloration: Colours.white,
                          fontWeight: FontWeight.daring,
                          fontSize: 14.0,
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }
Enter fullscreen mode

Exit fullscreen mode

Get the listing of moods

To acquire the listing of moods, we have to modify the tendencies.dart file in the identical screens listing as proven beneath:

    import 'bundle:fl_chart/fl_chart.dart';
    import 'bundle:flutter/materials.dart';
    import 'bundle:mood_tracker/mood_service.dart';
    import 'bundle:mood_tracker/utils.dart';

    class Tendencies extends StatefulWidget {
      const Tendencies({Key? key}) : tremendous(key: key);

      @override
      State<Tendencies> createState() => _TrendsState();
    }

    class _TrendsState extends State<Tendencies> {
      late Checklist<Temper> moods;
      bool _isLoading = false;
      bool _isError = false;

      @override
      void initState() {
        _getMoodList();
        tremendous.initState();
      }

      _getMoodList() {
        setState(() {
          _isLoading = true;
        });
        MoodService().getMoodList().then((worth) {
          setState(() {
            moods = worth;
            _isLoading = false;
          });
        }).catchError((e) {
          setState(() {
            _isLoading = false;
            _isError = true;
          });
        });
      }

      Widget construct(BuildContext context) {
        return _isLoading
            ? const Heart(
                baby: CircularProgressIndicator(
                coloration: Colours.blue,
              ))
            : _isError
                ? const Heart(
                    baby: Textual content(
                      'Error getting listing of transactions',
                      model: TextStyle(
                        coloration: Colours.purple,
                        fontWeight: FontWeight.daring,
                      ),
                    ),
                  )
                : Padding(
                    padding: const EdgeInsets.solely(high: 30, backside: 30, proper: 30),
                    baby: Container(
                        baby: LineChart(
                      LineChartData(
                        minY: 0,
                        maxY: 10,
                        minX: 0,
                        maxX: 15,
                        borderData: FlBorderData(
                          present: true,
                          border: Border.all(),
                        ),
                        titlesData: FlTitlesData(
                          present: true,
                          topTitles: AxisTitles(
                            sideTitles: SideTitles(showTitles: false),
                          ),
                          rightTitles: AxisTitles(
                            sideTitles: SideTitles(showTitles: false),
                          ),
                          bottomTitles: AxisTitles(
                            sideTitles: SideTitles(showTitles: false),
                          ),
                        ),
                        lineBarsData: [
                          LineChartBarData(
                            spots: moods
                                .asMap()
                                .map((key, value) => MapEntry(key,
                                    FlSpot(key.toDouble(), value.rate.toDouble())))
                                .values
                                .toList(),
                            isCurved: true,
                            color: Colors.blueAccent,
                            barWidth: 2.5,
                            belowBarData: BarAreaData(
                              show: true,
                              color: Color.fromARGB(99, 142, 152, 169),
                            ),
                          ),
                        ],
                      ),
                    )),
                  );
      }
    }
Enter fullscreen mode

Exit fullscreen mode

The snippet above does the next:

  • Traces 1-4: Import the required dependencies
  • Traces 14-16: Create the moods, _isLoading, and _isError properties to handle the appliance state
  • Traces 18-40: Create a _getMoodList technique to get the listing of moods utilizing the MoodService().getMoodList service, set states accordingly, and use the initState technique to name the _getMoodList technique when the thing is inserted into the tree
  • Modifies the UI widgets to make use of the info saved on Appwrite to plot a graph exhibiting the temper development

With that completed, we restart the appliance utilizing the code editor or run the command beneath:

    flutter run
Enter fullscreen mode

Exit fullscreen mode



Conclusion

This submit mentioned tips on how to use Appwrite’s database options to construct a personalised temper tracker in a Flutter utility. We constructed a base implementation to display Appwrite’s assist for scaffolding a working prototype. We are able to additional lengthen the appliance’s performance by leveraging options like real-time communication, localization, and authentication.

These assets may be useful:

The Article was Inspired from tech community site.
Contact us if this is inspired from your article and we will give you credit for it for serving the community.

This Banner is For Sale !!
Get your ad here for a week in 20$ only and get upto 10k Tech related traffic daily !!!

Leave a Reply

Your email address will not be published. Required fields are marked *

Want to Contribute to us or want to have 15k+ Audience read your Article ? Or Just want to make a strong Backlink?