Building a Calorie Tracker App in Flutter
Published: 2022-03-29 01:48:20


In order to get a firm understanding of any new programming language we might be learning, it is best to build a simple application with that language in order to actively learn its pros/cons and its intricacies. 
What better way to learn Flutter than to build a Calorie Tracker app with it! In this tutorial post I will cover how to build a Calorie Tracker app using Flutter, so let's get started!

## What We Will Build

Here are some screenshots of the Calorie Tracker application that we will build:
![Flutter 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4v345hryrydsgoht2o7w.png)
![Flutter 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oldrw2wbrmkl8kegkr2p.png)
![Flutter 3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/abi4lwd39s2x6cnjptzl.png)
![Flutter 4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ons5ii1ejc63uti8vap.png)
![Flutter 5](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cwsczbmibvm4nindgb6w.png)

_Figure 1: Screenshots of the Calorie Tracker App we're about to build_

## Setup

GitHub Starter Template Here: https://github.com/ShehanAT/flutter_calorie_tracker_app/tree/starter-template

In order to follow along in this tutorial I highly recommend using the starter template project posted above as this will quicken the development process for all of us.
Then, make sure to install Flutter at [the following link](https://docs.flutter.dev/get-started/install). 
Finally, we'll need a Firebase project for the application we're building so make sure to head over to the Firebase Console and create a new project if you haven't already.
As for the development environment, I'll be using VSCode([download link here](https://code.visualstudio.com/download)) so if you don't have it installed, you can do so now. Alternatively, using Android Studio([download link here](https://developer.android.com/studio)) is also possible.

## Setting up Firebase 

Once you have a Firebase project up and running the project creation process will allow you to download a `google-services.json` file. Make sure to place this file in the `{root_dir}/android/app` directory as this is how our Flutter application is able to connect with our Firebase project. 
We will be using the Firestore database as our data source for this application so let's create a Firestore database instance.
In the Firebase Console, click on the 'Firestore Database' tab and then click on 'Create Database' as shown in this screenshot:
![Firebase Firestore
    Database](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/obropf3zajcdy6q4inv9.png)

Then select 'Start in Test Mode' in the modal pop-up, select a region closed to you and create the database.

## Installing Packages
First, visit the starter template link above and clone the `starter-template` branch into your local machine. Then, open it up in VSCode and run the following command in a `Git Bash` terminal instance while at the root directory of this project:

```
flutter pub get
```

This command is used to install all the necessary packages used in this application, most notably the charts_flutter library.

## Adding code to files

Now comes the development part!
First, let's add the following code to the main.dart file:

```
import 'package:calorie_tracker_app/src/model/food_track_task.dart';
import 'package:calorie_tracker_app/src/page/day-view/day-view.dart';
import 'package:calorie_tracker_app/src/page/settings/settings_screen.dart';
import 'package:flutter/material.dart';
import 'src/page/history/history_screen.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:provider/provider.dart';
import 'package:calorie_tracker_app/src/providers/theme_notifier.dart';
import 'package:calorie_tracker_app/src/services/shared_preference_service.dart';
import 'package:calorie_tracker_app/helpers/theme.dart';
import 'package:calorie_tracker_app/routes/router.dart';
import 'package:firebase_database/firebase_database.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  await SharedPreferencesService().init();
  runApp(CalorieTrackerApp());
}

class CalorieTrackerApp extends StatefulWidget {
  @override
  _CalorieTrackerAppState createState() => _CalorieTrackerAppState();
}

class _CalorieTrackerAppState extends State<CalorieTrackerApp> {
  DarkThemeProvider themeChangeProvider = DarkThemeProvider();
  late Widget homeWidget;
  late bool signedIn;

  @override
  void initState() {
    super.initState();
    checkFirstSeen();
  }

  void checkFirstSeen() {
    final bool _firstLaunch = true;

    if (_firstLaunch) {
      homeWidget = Homepage();
    }
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<DarkThemeProvider>(
      create: (_) {
        return themeChangeProvider;
      },
      child: Consumer<DarkThemeProvider>(
        builder:
            (BuildContext context, DarkThemeProvider value, Widget? child) {
          return GestureDetector(
              onTap: () => hideKeyboard(context),
              child: MaterialApp(
                  debugShowCheckedModeBanner: false,
                  builder: (_, Widget? child) => ScrollConfiguration(
                      behavior: MyBehavior(), child: child!),
                  theme: themeChangeProvider.darkTheme ? darkTheme : lightTheme,
                  home: homeWidget,
                  onGenerateRoute: RoutePage.generateRoute));
        },
      ),
    );
  }

  void hideKeyboard(BuildContext context) {
    final FocusScopeNode currentFocus = FocusScope.of(context);
    if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
      FocusManager.instance.primaryFocus!.unfocus();
    }
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: FlatButton(
          onPressed: () {
            // Navigate back to homepage
          },
          child: const Text('Go Back!'),
        ),
      ),
    );
  }

  @override
  State<StatefulWidget> createState() {
    return _Homepage();
  }
}

class _Homepage extends State<Homepage> with SingleTickerProviderStateMixin {
  @override
  void initState() {
    super.initState();
  }

  void onClickHistoryScreenButton(BuildContext context) {
    Navigator.of(context)
        .push(MaterialPageRoute(builder: (context) => HistoryScreen()));
  }

  void onClickSettingsScreenButton(BuildContext context) {
    Navigator.of(context)
        .push(MaterialPageRoute(builder: (context) => SettingsScreen()));
  }

  void onClickDayViewScreenButton(BuildContext context) {
    Navigator.of(context)
        .push(MaterialPageRoute(builder: (context) => DayViewScreen()));
  }

  @override
  Widget build(BuildContext context) {
    final ButtonStyle buttonStyle =
        ElevatedButton.styleFrom(textStyle: const TextStyle(fontSize: 20));

    return Scaffold(
        appBar: AppBar(
          title: Text(
            "Flutter Calorie Tracker App",
            style: TextStyle(
                color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold),
          ),
        ),
        body: new Column(
          children: <Widget>[
            new ListTile(
                leading: const Icon(Icons.food_bank),
                title: new Text("Welcome To Calorie Tracker App!",
                    textAlign: TextAlign.center,
                    style: const TextStyle(fontWeight: FontWeight.bold))),
            new ElevatedButton(
                onPressed: () {
                  onClickDayViewScreenButton(context);
                },
                child: Text("Day View Screen")),
            new ElevatedButton(
                onPressed: () {
                  onClickHistoryScreenButton(context);
                },
                child: Text("History Screen")),
            new ElevatedButton(
                onPressed: () {
                  onClickSettingsScreenButton(context);
                },
                child: Text("Settings Screen")),
          ],
        ));
  }
}

class MyBehavior extends ScrollBehavior {
  @override
  Widget buildViewportChrome(
      BuildContext context, Widget child, AxisDirection axisDirection) {
    return child;
  }
}
```

Now for an explanation of the important parts of the above code:

- `Future<void> main() async`: Instead of the standard `void main()` method we have to specify the return type as `Future<void>` because we are using the `async` and `await` keywords in this method. Since these keywords are asynchronous, we have to adjust the return type of the method accordingly.
- `class CalorieTrackerApp`: This class is the main entrypoint of the application. It responsible for rendering the `Homepage` widget when the app is first launched. Its `build()` method does the rendering and uses the `ChangeNotifierProvider` provider-wrapper class to set a dark theme for the entire application, with the help of `DarkThemeProvider`
- `class Homepage`: This class is the home screen for the application. This class renders three buttons for navigating to the Day View, History and Settings screens of the application. We use the `Navigator.of(context).push(MaterialPageRoute(builder: (context) => DayViewScreen()))` statement to switch to the desired screen

Now let's build out the model files, starting with `lib\src\model\food_track_task.dart`:

```
import 'package:json_annotation/json_annotation.dart';
import 'package:calorie_tracker_app/src/utils/uuid.dart';
import 'package:firebase_database/firebase_database.dart';

@JsonSerializable()
class FoodTrackTask {
  String id;
  String food_name;
  num calories;
  num carbs;
  num fat;
  num protein;
  String mealTime;
  DateTime createdOn;
  num grams;

  FoodTrackTask({
    required this.food_name,
    required this.calories,
    required this.carbs,
    required this.protein,
    required this.fat,
    required this.mealTime,
    required this.createdOn,
    required this.grams,
    String? id,
  }) : this.id = id ?? Uuid().generateV4();

  factory FoodTrackTask.fromSnapshot(DataSnapshot snap) => FoodTrackTask(
      food_name: snap.child('food_name').value as String,
      calories: snap.child('calories') as int,
      carbs: snap.child('carbs').value as int,
      fat: snap.child('fat').value as int,
      protein: snap.child('protein').value as int,
      mealTime: snap.child('mealTime').value as String,
      grams: snap.child('grams').value as int,
      createdOn: snap.child('createdOn').value as DateTime);

  Map<String, dynamic> toMap() {
    return <String, dynamic>{
      'mealTime': mealTime,
      'food_name': food_name,
      'calories': calories,
      'carbs': carbs,
      'protein': protein,
      'fat': fat,
      'grams': grams,
      'createdOn': createdOn
    };
  }

  FoodTrackTask.fromJson(Map<dynamic, dynamic> json)
      : id = json['id'],
        mealTime = json['mealTime'],
        calories = json['calories'],
        createdOn = DateTime.parse(json['createdOn']),
        food_name = json['food_name'],
        carbs = json['carbs'],
        fat = json['fat'],
        protein = json['protein'],
        grams = json['grams'];

  Map<dynamic, dynamic> toJson() => <dynamic, dynamic>{
        'id': id,
        'mealTime': mealTime,
        'createdOn': createdOn.toString(),
        'food_name': food_name,
        'calories': calories,
        'carbs': carbs,
        'fat': fat,
        'protein': protein,
        'grams': grams,
      };
}
```

So this model class is the primary class that will hold the information of each food tracking instance. The `mealTime` field defines the time in which the food was consumed, the `createdOn` field defines the time in which it was tracked and the `carbs`, `fat`, `protein` and `grams` fields convey the quality and nutritional value of the food consumed.

Next is a relatively minor model class: `lib\src\model\food_track_entry.dart`:

```
class FoodTrackEntry {
  DateTime date;
  int calories;

  FoodTrackEntry(this.date, this.calories);
}
```

This class will be used as entry points for the `charts_flutter` Time Series chart that we'll be developing in the History Screen.

Next up is the developing of the services folder. We'll start with the `lib/src/services/database.dart` file:

```
import 'package:calorie_tracker_app/src/model/food_track_task.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

class DatabaseService {
  final String uid;
  final DateTime currentDate;
  DatabaseService({required this.uid, required this.currentDate});

  final DateTime today =
      DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day);
  final DateTime weekStart = DateTime(2020, 09, 07);
  // collection reference
  final CollectionReference foodTrackCollection =
      FirebaseFirestore.instance.collection('foodTracks');

  Future addFoodTrackEntry(FoodTrackTask food) async {
    return await foodTrackCollection
        .doc(food.createdOn.millisecondsSinceEpoch.toString())
        .set({
      'food_name': food.food_name,
      'calories': food.calories,
      'carbs': food.carbs,
      'fat': food.fat,
      'protein': food.protein,
      'mealTime': food.mealTime,
      'createdOn': food.createdOn,
      'grams': food.grams
    });
  }

  Future deleteFoodTrackEntry(FoodTrackTask deleteEntry) async {
    print(deleteEntry.toString());
    return await foodTrackCollection
        .doc(deleteEntry.createdOn.millisecondsSinceEpoch.toString())
        .delete();
  }

  List<FoodTrackTask> _scanListFromSnapshot(QuerySnapshot snapshot) {
    return snapshot.docs.map((doc) {
      return FoodTrackTask(
        id: doc.id,
        food_name: doc['food_name'] ?? '',
        calories: doc['calories'] ?? 0,
        carbs: doc['carbs'] ?? 0,
        fat: doc['fat'] ?? 0,
        protein: doc['protein'] ?? 0,
        mealTime: doc['mealTime'] ?? "",
        createdOn: doc['createdOn'].toDate() ?? DateTime.now(),
        grams: doc['grams'] ?? 0,
      );
    }).toList();
  }

  Stream<List<FoodTrackTask>> get foodTracks {
    return foodTrackCollection.snapshots().map(_scanListFromSnapshot);
  }

  Future<List<dynamic>> getAllFoodTrackData() async {
    QuerySnapshot snapshot = await foodTrackCollection.get();
    List<dynamic> result = snapshot.docs.map((doc) => doc.data()).toList();
    return result;
  }

  Future<String> getFoodTrackData(String uid) async {
    DocumentSnapshot snapshot = await foodTrackCollection.doc(uid).get();
    return snapshot.toString();
  }
}
```

Now for an explanation for it:

- This is the class used to interact with the Firebase Firestore instance we created in the previous steps
- `uid`: This is the universal identifier for the Firestore instance. This value can be found by accessing the Firestore database in the Firebase Console
- `foodTrackCollection`: This is the `FirebaseFirestore` instance that allows us to connect to the `foodTracks` collection in the Firestore database we previously created
- `addFoodTrackEntry(FoodTrackTask food)`: This is the method used to create a new record in the `foodTracks` Firestore collection. Notice that the record identifier is the `millisecondsSinceEpoch` value based on the `FoodTrackTask` instance's `createdOn` field. This is done to ensure uniqueness in the collection
- `deleteFoodTrackEntry(FoodTrackTask deleteEntry)`: This is the method used to delete a record in the `foodTracks` Firestore collection. It uses the `millisecondsSinceEpoch` value based on the `createdOn` field from the `FoodTrackTask` instance that is passed in as a parameter to identify the record to be deleted
- `_scanListFromSnapshot(QuerySnapshot snapshot)`: This method is used to convert the data in the QuerySnapshot response object from Firestore to a List of `FoodTrackTask` instances. We use the popular `map()` function in order to do so
- `get foodtracks`: This method only used by a `StreamProvider` instance in the Day View screen(we'll get to building it soon) to provide a list of `FoodTrackTask` instances that will be displayed in a list format
- `getAllFoodTrackData()`: This method is used to fetch all `foodTrack` records from the database and return them in a `List<dynamic>` object. Note that we should avoid using the `dynamic` data type whenever possible, but it would be acceptable in this context because we're not 100% sure of what is being returned as a response from the database(More on the `dynamic` data type [in the `flutterbyexample.com` site](https://flutterbyexample.com/lesson/dynamic))
- `getFoodTrackData(String uid)`: This method is used to fetch a specific document from the Firestore database, based on its universal identifier. It can also provide the same result as the `getAllFoodTrackData()` method if the `uid` is set to the collection name

Ok making progress..
Next let's build out the `lib/src/services/shared_preference_service.dart` file:

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

class SharedPreferencesService {
  static late SharedPreferences _sharedPreferences;

  Future<void> init() async {
    _sharedPreferences = await SharedPreferences.getInstance();
  }

  static String sharedPreferenceDarkThemeKey = 'DARKTHEME';

  static Future<bool> setDarkTheme({required bool to}) async {
    return _sharedPreferences.setBool(sharedPreferenceDarkThemeKey, to);
  }

  static bool getDarkTheme() {
    return _sharedPreferences.getBool(sharedPreferenceDarkThemeKey) ?? true;
  }
}
```

Here is its explanation:

- The `shared_preferences` package is used for the common tasks of storing and caching values on disk. This service class will serve to return instances of the `SharedPreferences` class and therefore follows the Singleton design pattern
- On top of the above functionality, this class also assists in providing the dark theme functionality described in the `main.dart` file through the `getDarkTheme()` method

Ok on to the `providers` folder where we'll build the `lib/src/providers/theme_notifier.dart` file:

```
import 'package:flutter/cupertino.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:calorie_tracker_app/src/services/shared_preference_service.dart';

class DarkThemeProvider with ChangeNotifier {
  // The 'with' keyword is similar to mixins in JavaScript, in that it is a way of reusing a class's fields/methods in a different class that is not a super class of the initial class.

  bool get darkTheme {
    return SharedPreferencesService.getDarkTheme();
  }

  set dartTheme(bool value) {
    SharedPreferencesService.setDarkTheme(to: value);
    notifyListeners();
  }
}
```

This class will serve as a provider to the `CalorieTrackerAppState` class in the `main.dart` file while also enabling the dark theme that the application will use by default as of now. Providers are important because they help reduce inefficiencies having to do with re-rendering components whenever state changes occur. When using providers, the only widgets that have to be rebuilt whenever state changes occur are the ones that are assigned as consumers to their appropriate providers. More on Providers and Consumers [in this great blog post](https://www.technicalfeeder.com/2021/09/flutter-provider-example-changenotifierprovider-and-consumer/)

Moving on to the `utils` folder. Let's build the `lib/src/utils/charts/datetime_series_chart.dart` file now:

```
import 'package:charts_flutter/flutter.dart' as charts;
import 'package:firebase_database/firebase_database.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'package:calorie_tracker_app/src/services/database.dart';
import 'package:calorie_tracker_app/src/model/food_track_task.dart';
import 'package:calorie_tracker_app/src/model/food-track-entry.dart';

class DateTimeChart extends StatefulWidget {
  @override
  _DateTimeChart createState() => _DateTimeChart();
}

class _DateTimeChart extends State<DateTimeChart> {
  List<charts.Series<FoodTrackEntry, DateTime>>? resultChartData = null;
  DatabaseService databaseService = new DatabaseService(
      uid: "calorie-tracker-b7d17", currentDate: DateTime.now());

  @override
  void initState() {
    super.initState();

    getAllFoodTrackData();
  }

  void getAllFoodTrackData() async {
    List<dynamic> foodTrackResults =
        await databaseService.getAllFoodTrackData();
    List<FoodTrackEntry> foodTrackEntries = [];

    for (var foodTrack in foodTrackResults) {
      if (foodTrack["createdOn"] != null) {
        foodTrackEntries.add(FoodTrackEntry(
            foodTrack["createdOn"].toDate(), foodTrack["calories"]));
      }
    }
    populateChartWithEntries(foodTrackEntries);
  }

  void populateChartWithEntries(List<FoodTrackEntry> foodTrackEntries) async {
    Map<String, int> caloriesByDateMap = new Map();
    if (foodTrackEntries != null) {
      var dateFormat = DateFormat("yyyy-MM-dd");
      for (var foodEntry in foodTrackEntries) {
        var trackedDateStr = foodEntry.date;
        DateTime dateNow = DateTime.now();
        var trackedDate = dateFormat.format(trackedDateStr);
        if (caloriesByDateMap.containsKey(trackedDate)) {
          caloriesByDateMap[trackedDate] =
              caloriesByDateMap[trackedDate]! + foodEntry.calories;
        } else {
          caloriesByDateMap[trackedDate] = foodEntry.calories;
        }
      }
      List<FoodTrackEntry> caloriesByDateTimeMap = [];
      for (var foodEntry in caloriesByDateMap.keys) {
        DateTime entryDateTime = DateTime.parse(foodEntry);
        caloriesByDateTimeMap.add(
            new FoodTrackEntry(entryDateTime, caloriesByDateMap[foodEntry]!));
      }

      caloriesByDateTimeMap.sort((a, b) {
        int aDate = a.date.microsecondsSinceEpoch;
        int bDate = b.date.microsecondsSinceEpoch;

        return aDate.compareTo(bDate);
      });
      setState(() {
        resultChartData = [
          new charts.Series<FoodTrackEntry, DateTime>(
              id: "Food Track Entries",
              colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
              domainFn: (FoodTrackEntry foodTrackEntry, _) =>
                  foodTrackEntry.date,
              measureFn: (FoodTrackEntry foodTrackEntry, _) =>
                  foodTrackEntry.calories,
              labelAccessorFn: (FoodTrackEntry foodTrackEntry, _) =>
                  '${foodTrackEntry.date}: ${foodTrackEntry.calories}',
              data: caloriesByDateTimeMap)
        ];
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    if (resultChartData != null) {
      return Scaffold(
        body: new Center(
            child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text("Caloric Intake By Date Chart"),
            new Padding(
                padding: new EdgeInsets.all(32.0),
                child: new SizedBox(
                  height: 500.0,
                  child:
                      charts.TimeSeriesChart(resultChartData!, animate: true),
                ))
          ],
        )),
      );
    } else {
      return CircularProgressIndicator();
    }
  }
}
```

Here is its explanation:

- This class is responsible for displaying a Time Series chart that will be shown in the History screen
- `initState()`: In the `initState()` lifecycle method we will call the `getAllFoodTrackData()` method in order to fetch all the `FoodTrackEntry` objects from the Firestore database
- `getAllFoodTrackData()`: This method fetches all records from the 'foodTracks' collection, converts them into `FoodTrackEntry` instances and adds them to a List. Finally, it calls the `populateChartWithEntries()` method, as the name sounds, to populate the Time Series chart
- `populateChartWithEntries()`: This method converts the `List<FoodTrackEntry>` list passed in as a parameter into another `List<FoodTrackEntry>` list that aggregates the calorie amounts based on the date. For example: if there are 3 `FoodTrackEntry` instances with a date value of `2022-03-25` and calorie values of `300`, `400`, and `500` respectively, then the new `List<FoodTrackEntry>` list would only contain one `FoodTrackEntry` instance with a date value of `2022-03-25` and a calorie value of `1200`. Doing so allows us to chart the caloric intake of the user by date. Once the new `List<FoodTrackEntry>` has been created, the `setState()` method will reassign the value of the `resultChartData` variable. Consequently, the widget will be rebuilt and the `charts.TimeSeriesChart()` widget will display the updated chart data
- `build()`: This method will render a Scaffold layout containing the title of the Time Series chart and the actual Time Series chart itself

Ok let's move on to some files where we will defined some constant values...

Here's `lib/src/utils/constants.dart`:

```
const DATABASE_UID = "<ENTER-YOUR-COLLECTION-ID-HERE>";
```

The collection ID that this file requires can be found in the Firebase Console's Firestore Database page:
![Firestore Database page showing Collection
    ID](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9slx1wt1h7dls7pmbeoa.png)
_Figure 2: Firestore Database page showing Collection ID_

and then here's `lib/src/utils/theme_colors.dart`:

```
const CARBS_COLOR = 0xffD83027;
const PROTEIN_COLOR = 0x9027D830;
const FAT_COLOR = 0xFF0D47A1;
```

These color codes will be used to define the colors used in the Day View screen. Feel free to change them based on your preferences.

And to wrap up with the `utils` folder, let build out the `lib/src/utils/uuid.dart` file:

```
import 'dart:math';

class Uuid {
  final Random _random = Random();

  String generateV4() {
    final int special = 8 + _random.nextInt(4);

    return '${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}-'
        '${_bitsDigits(16, 4)}-'
        '4${_bitsDigits(12, 3)}-'
        '${_printDigits(special, 1)}${_bitsDigits(12, 3)}-'
        '${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}${_bitsDigits(16, 4)}';
  }

  String _bitsDigits(int bitCount, int digitCount) =>
      _printDigits(_generateBits(bitCount), digitCount);

  int _generateBits(int bitCount) => _random.nextInt(1 << bitCount);

  String _printDigits(int value, int count) =>
      value.toRadixString(16).padLeft(count, '0');
}
```

This class basically generates random universally unique identifiers, most frequently used as IDs when creating new instances of model classes.

Now we can start adding code to the files in the `pages` folder.

We are about to build the Day View screen so here is a screenshot of it:
![Day View Screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sg48iqeuae2amqe7zoim.png)
_Figure 3: Day View Screen_

Now that you have a better idea of how its supposed to look like, let's begin with the `lib/src/page/day-view/calorie-stats.dart` file:

```
import 'package:calorie_tracker_app/src/model/food_track_task.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:calorie_tracker_app/src/utils/theme_colors.dart';

class CalorieStats extends StatelessWidget {
  DateTime datePicked;
  DateTime today = DateTime.now();
  CalorieStats({required this.datePicked});

  num totalCalories = 0;
  num totalCarbs = 0;
  num totalFat = 0;
  num totalProtein = 0;
  num displayCalories = 0;

  bool dateCheck() {
    DateTime formatPicked =
        DateTime(datePicked.year, datePicked.month, datePicked.day);
    DateTime formatToday = DateTime(today.year, today.month, today.day);
    if (formatPicked.compareTo(formatToday) == 0) {
      return true;
    } else {
      return false;
    }
  }

  static List<num> macroData = [];

  @override
  Widget build(BuildContext context) {
    final DateTime curDate =
        new DateTime(datePicked.year, datePicked.month, datePicked.day);

    final foodTracks = Provider.of<List<FoodTrackTask>>(context);

    List findCurScans(List<FoodTrackTask> foodTracks) {
      List currentFoodTracks = [];
      foodTracks.forEach((foodTrack) {
        DateTime trackDate = DateTime(foodTrack.createdOn.year,
            foodTrack.createdOn.month, foodTrack.createdOn.day);
        if (trackDate.compareTo(curDate) == 0) {
          currentFoodTracks.add(foodTrack);
        }
      });
      return currentFoodTracks;
    }

    List currentFoodTracks = findCurScans(foodTracks);

    void findNutriments(List foodTracks) {
      foodTracks.forEach((scan) {
        totalCarbs += scan.carbs;
        totalFat += scan.fat;
        totalProtein += scan.protein;
        displayCalories += scan.calories;
      });
      totalCalories = 9 * totalFat + 4 * totalCarbs + 4 * totalProtein;
    }

    findNutriments(currentFoodTracks);

    // ignore: deprecated_member_use
    List<PieChartSectionData> _sections = <PieChartSectionData>[];

    PieChartSectionData _fat = PieChartSectionData(
      color: Color(FAT_COLOR),
      value: (9 * (totalFat) / totalCalories) * 100,
      title:
          '', // ((9 * totalFat / totalCalories) * 100).toStringAsFixed(0) + '%',
      radius: 50,
      // titleStyle: TextStyle(color: Colors.white, fontSize: 24),
    );

    PieChartSectionData _carbohydrates = PieChartSectionData(
      color: Color(CARBS_COLOR),
      value: (4 * (totalCarbs) / totalCalories) * 100,
      title:
          '', // ((4 * totalCarbs / totalCalories) * 100).toStringAsFixed(0) + '%',
      radius: 50,
      // titleStyle: TextStyle(color: Colors.black, fontSize: 24),
    );

    PieChartSectionData _protein = PieChartSectionData(
      color: Color(PROTEIN_COLOR),
      value: (4 * (totalProtein) / totalCalories) * 100,
      title:
          '', // ((4 * totalProtein / totalCalories) * 100).toStringAsFixed(0) + '%',
      radius: 50,
      // titleStyle: TextStyle(color: Colors.white, fontSize: 24),
    );

    _sections = [_fat, _protein, _carbohydrates];

    macroData = [displayCalories, totalProtein, totalCarbs, totalFat];

    totalCarbs = 0;
    totalFat = 0;
    totalProtein = 0;
    displayCalories = 0;

    Widget _chartLabels() {
      return Padding(
        padding: EdgeInsets.only(top: 78.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Row(
              children: <Widget>[
                Text('Carbs ',
                    style: TextStyle(
                      color: Color(CARBS_COLOR),
                      fontFamily: 'Open Sans',
                      fontSize: 16.0,
                      fontWeight: FontWeight.w500,
                    )),
                Text(macroData[2].toStringAsFixed(1) + 'g',
                    style: TextStyle(
                      color: Color.fromARGB(255, 0, 0, 0),
                      fontFamily: 'Open Sans',
                      fontSize: 16.0,
                      fontWeight: FontWeight.w500,
                    )),
              ],
            ),
            SizedBox(height: 3.0),
            Row(
              children: <Widget>[
                Text('Protein ',
                    style: TextStyle(
                      color: Color(0xffFA8925),
                      fontFamily: 'Open Sans',
                      fontSize: 16.0,
                      fontWeight: FontWeight.w500,
                    )),
                Text(macroData[1].toStringAsFixed(1) + 'g',
                    style: TextStyle(
                      color: Color.fromARGB(255, 0, 0, 0),
                      fontFamily: 'Open Sans',
                      fontSize: 16.0,
                      fontWeight: FontWeight.w500,
                    )),
              ],
            ),
            SizedBox(height: 3.0),
            Row(
              children: <Widget>[
                Text('Fat ',
                    style: TextStyle(
                      color: Color(0xff01B4BC),
                      fontFamily: 'Open Sans',
                      fontSize: 16.0,
                      fontWeight: FontWeight.w500,
                    )),
                Text(macroData[3].toStringAsFixed(1) + 'g',
                    style: TextStyle(
                      color: Color.fromARGB(255, 0, 0, 0),
                      fontFamily: 'Open Sans',
                      fontSize: 16.0,
                      fontWeight: FontWeight.w500,
                    )),
              ],
            ),
          ],
        ),
      );
    }

    Widget _calorieDisplay() {
      return Container(
        height: 74,
        width: 74,
        decoration: BoxDecoration(
          color: Color(0xff5FA55A),
          shape: BoxShape.circle,
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(macroData[0].toStringAsFixed(0),
                style: TextStyle(
                  fontSize: 22.0,
                  color: Colors.white,
                  fontFamily: 'Open Sans',
                  fontWeight: FontWeight.w500,
                )),
            Text('kcal',
                style: TextStyle(
                  fontSize: 14.0,
                  color: Colors.white,
                  fontFamily: 'Open Sans',
                  fontWeight: FontWeight.w500,
                )),
          ],
        ),
      );
    }

    if (currentFoodTracks.length == 0) {
      if (dateCheck()) {
        return Flexible(
          fit: FlexFit.loose,
          child: Text('Add food to see calorie breakdown.',
              textAlign: TextAlign.center,
              style: TextStyle(
                fontSize: 40.0,
                fontWeight: FontWeight.w500,
              )),
        );
      } else {
        return Flexible(
          fit: FlexFit.loose,
          child: Text('No food added on this day.',
              textAlign: TextAlign.center,
              style: TextStyle(
                fontSize: 40.0,
                fontWeight: FontWeight.w500,
              )),
        );
      }
    } else {
      return Container(
        child: Row(
          children: <Widget>[
            Stack(alignment: Alignment.center, children: <Widget>[
              AspectRatio(
                aspectRatio: 1,
                child: PieChart(
                  PieChartData(
                    sections: _sections,
                    borderData: FlBorderData(show: false),
                    centerSpaceRadius: 40,
                    sectionsSpace: 3,
                  ),
                ),
              ),
              _calorieDisplay(),
            ]),
            _chartLabels(),
          ],
        ),
      );
    }
  }
}
```

Here is the explanation for it:

- The `datePicked`, `totalCalories`, `totalCarbs`, `totalProtein`, `displayCalories` variables store the nutritional data for the date picked
- `dateCheck()`: This method checks if the `datePicked` DateTime value is equivalent to today's date. This method is used for prompting the user to add food in the current Day View page
- `macroData`: This array is used to store the macro-nutritional values and quantity(carbs, protein, fat, and grams) of each type of food that is added in the Day View Screen
- `build(BuildContext context)`: This method renders the pie chart display of the macro-nutritional ratios and the macro quantities of the three macro-nutritional groups

Next up is the `lib/src/page/day-view.dart` file.

Here is its code:

```
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:calorie_tracker_app/src/model/food_track_task.dart';
import 'package:calorie_tracker_app/component/iconpicker/icon_picker_builder.dart';
import 'package:calorie_tracker_app/src/utils/charts/datetime_series_chart.dart';
import 'calorie-stats.dart';
import 'package:provider/provider.dart';
import 'package:calorie_tracker_app/src/services/database.dart';
import 'package:openfoodfacts/model/Product.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'dart:math';
import 'package:calorie_tracker_app/src/utils/theme_colors.dart';
import 'package:calorie_tracker_app/src/utils/constants.dart';

class DayViewScreen extends StatefulWidget {
  DayViewScreen();

  @override
  State<StatefulWidget> createState() {
    return _DayViewState();
  }
}

class _DayViewState extends State<DayViewScreen> {
  String title = 'Add Food';
  double servingSize = 0;
  String dropdownValue = 'grams';
  DateTime _value = DateTime.now();
  DateTime today = DateTime.now();
  Color _rightArrowColor = Color(0xffC1C1C1);
  Color _leftArrowColor = Color(0xffC1C1C1);
  final _addFoodKey = GlobalKey<FormState>();

  DatabaseService databaseService = new DatabaseService(
      uid: "calorie-tracker-b7d17", currentDate: DateTime.now());

  late FoodTrackTask addFoodTrack;

  @override
  void initState() {
    super.initState();
    addFoodTrack = FoodTrackTask(
        food_name: "",
        calories: 0,
        carbs: 0,
        protein: 0,
        fat: 0,
        mealTime: "",
        createdOn: _value,
        grams: 0);
    databaseService.getFoodTrackData(DATABASE_UID);
  }

  void resetFoodTrack() {
    addFoodTrack = FoodTrackTask(
        food_name: "",
        calories: 0,
        carbs: 0,
        protein: 0,
        fat: 0,
        mealTime: "",
        createdOn: _value,
        grams: 0);
  }

  Widget _calorieCounter() {
    return Padding(
      padding: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0),
      child: new Container(
        decoration: BoxDecoration(
            color: Colors.white,
            border: Border(
                bottom: BorderSide(
              color: Colors.grey.withOpacity(0.5),
              width: 1.5,
            ))),
        height: 220,
        child: Row(
          children: <Widget>[
            CalorieStats(datePicked: _value),
          ],
        ),
      ),
    );
  }

  Widget _addFoodButton() {
    return IconButton(
      icon: Icon(Icons.add_box),
      iconSize: 25,
      color: Colors.white,
      onPressed: () async {
        setState(() {});
        _showFoodToAdd(context);
      },
    );
  }

  Future _selectDate() async {
    DateTime? picked = await showDatePicker(
      context: context,
      initialDate: _value,
      firstDate: new DateTime(2019),
      lastDate: new DateTime.now(),
      builder: (BuildContext context, Widget? child) {
        return Theme(
          data: ThemeData.light().copyWith(
            primaryColor: const Color(0xff5FA55A), //Head background
          ),
          child: child!,
        );
      },
    );
    if (picked != null) setState(() => _value = picked);
    _stateSetter();
  }

  void _stateSetter() {
    if (today.difference(_value).compareTo(Duration(days: 1)) == -1) {
      setState(() => _rightArrowColor = Color(0xffEDEDED));
    } else
      setState(() => _rightArrowColor = Colors.white);
  }

  checkFormValid() {
    if (addFoodTrack.calories != 0 &&
        addFoodTrack.carbs != 0 &&
        addFoodTrack.protein != 0 &&
        addFoodTrack.fat != 0 &&
        addFoodTrack.grams != 0) {
      return true;
    }
    return false;
  }

  _showFoodToAdd(BuildContext context) {
    return showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Text(title),
            content: _showAmountHad(),
            actions: <Widget>[
              FlatButton(
                onPressed: () => Navigator.pop(context), // passing false
                child: Text('Cancel'),
              ),
              FlatButton(
                onPressed: () async {
                  if (checkFormValid()) {
                    Navigator.pop(context);
                    var random = new Random();
                    int randomMilliSecond = random.nextInt(1000);
                    addFoodTrack.createdOn = _value;
                    addFoodTrack.createdOn = addFoodTrack.createdOn
                        .add(Duration(milliseconds: randomMilliSecond));
                    databaseService.addFoodTrackEntry(addFoodTrack);
                    resetFoodTrack();
                  } else {
                    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                      content: Text(
                          "Invalid form data! All numeric fields must contain numeric values greater than 0"),
                      backgroundColor: Colors.white,
                    ));
                  }
                },
                child: Text('Ok'),
              ),
            ],
          );
        });
  }

  Widget _showAmountHad() {
    return new Scaffold(
      body: Column(children: <Widget>[
        _showAddFoodForm(),
        _showUserAmount(),
      ]),
    );
  }

  Widget _showAddFoodForm() {
    return Form(
      key: _addFoodKey,
      child: Column(children: [
        TextFormField(
          decoration: const InputDecoration(
              labelText: "Name *", hintText: "Please enter food name"),
          validator: (value) {
            if (value == null || value.isEmpty) {
              return "Please enter the food name";
            }
            return null;
          },
          onChanged: (value) {
            addFoodTrack.food_name = value;

            // addFood.calories = value;
          },
        ),
        TextFormField(
          decoration: const InputDecoration(
              labelText: "Calories *",
              hintText: "Please enter a calorie amount"),
          validator: (value) {
            if (value == null || value.isEmpty) {
              return "Please enter a calorie amount";
            }
            return null;
          },
          keyboardType: TextInputType.number,
          onChanged: (value) {
            try {
              addFoodTrack.calories = int.parse(value);
            } catch (e) {
              // return "Please enter numeric values"
              addFoodTrack.calories = 0;
            }

            // addFood.calories = value;
          },
        ),
        TextFormField(
          decoration: const InputDecoration(
              labelText: "Carbs *", hintText: "Please enter a carbs amount"),
          validator: (value) {
            if (value == null || value.isEmpty) {
              return "Please enter a carbs amount";
            }
            return null;
          },
          keyboardType: TextInputType.number,
          onChanged: (value) {
            try {
              addFoodTrack.carbs = int.parse(value);
            } catch (e) {
              addFoodTrack.carbs = 0;
            }
          },
        ),
        TextFormField(
          decoration: const InputDecoration(
              labelText: "Protein *",
              hintText: "Please enter a protein amount"),
          validator: (value) {
            if (value == null || value.isEmpty) {
              return "Please enter a calorie amount";
            }
            return null;
          },
          onChanged: (value) {
            try {
              addFoodTrack.protein = int.parse(value);
            } catch (e) {
              addFoodTrack.protein = 0;
            }
          },
        ),
        TextFormField(
          decoration: const InputDecoration(
              labelText: "Fat *", hintText: "Please enter a fat amount"),
          validator: (value) {
            if (value == null || value.isEmpty) {
              return "Please enter a fat amount";
            }
            return null;
          },
          onChanged: (value) {
            try {
              addFoodTrack.fat = int.parse(value);
            } catch (e) {
              addFoodTrack.fat = 0;
            }
          },
        ),
      ]),
    );
  }

  Widget _showUserAmount() {
    return new Expanded(
      child: new TextField(
          maxLines: 1,
          autofocus: true,
          decoration: new InputDecoration(
              labelText: 'Grams *',
              hintText: 'eg. 100',
              contentPadding: EdgeInsets.all(0.0)),
          keyboardType: TextInputType.number,
          inputFormatters: <TextInputFormatter>[
            FilteringTextInputFormatter.digitsOnly
          ],
          onChanged: (value) {
            try {
              addFoodTrack.grams = int.parse(value);
            } catch (e) {
              addFoodTrack.grams = 0;
            }
            setState(() {
              servingSize = double.tryParse(value) ?? 0;
            });
          }),
    );
  }

  Widget _showDatePicker() {
    return Container(
      width: 250,
      child: Row(
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
          IconButton(
            icon: Icon(Icons.arrow_left, size: 25.0),
            color: _leftArrowColor,
            onPressed: () {
              setState(() {
                _value = _value.subtract(Duration(days: 1));
                _rightArrowColor = Colors.white;
              });
            },
          ),
          TextButton(
            // textColor: Colors.white,
            onPressed: () => _selectDate(),
            // },
            child: Text(_dateFormatter(_value),
                style: TextStyle(
                  fontFamily: 'Open Sans',
                  fontSize: 18.0,
                  fontWeight: FontWeight.w700,
                )),
          ),
          IconButton(
              icon: Icon(Icons.arrow_right, size: 25.0),
              color: _rightArrowColor,
              onPressed: () {
                if (today.difference(_value).compareTo(Duration(days: 1)) ==
                    -1) {
                  setState(() {
                    _rightArrowColor = Color(0xffC1C1C1);
                  });
                } else {
                  setState(() {
                    _value = _value.add(Duration(days: 1));
                  });
                  if (today.difference(_value).compareTo(Duration(days: 1)) ==
                      -1) {
                    setState(() {
                      _rightArrowColor = Color(0xffC1C1C1);
                    });
                  }
                }
              }),
        ],
      ),
    );
  }

  String _dateFormatter(DateTime tm) {
    DateTime today = new DateTime.now();
    Duration oneDay = new Duration(days: 1);
    Duration twoDay = new Duration(days: 2);
    String month;

    switch (tm.month) {
      case 1:
        month = "Jan";
        break;
      case 2:
        month = "Feb";
        break;
      case 3:
        month = "Mar";
        break;
      case 4:
        month = "Apr";
        break;
      case 5:
        month = "May";
        break;
      case 6:
        month = "Jun";
        break;
      case 7:
        month = "Jul";
        break;
      case 8:
        month = "Aug";
        break;
      case 9:
        month = "Sep";
        break;
      case 10:
        month = "Oct";
        break;
      case 11:
        month = "Nov";
        break;
      case 12:
        month = "Dec";
        break;
      default:
        month = "Undefined";
        break;
    }

    Duration difference = today.difference(tm);

    if (difference.compareTo(oneDay) < 1) {
      return "Today";
    } else if (difference.compareTo(twoDay) < 1) {
      return "Yesterday";
    } else {
      return "${tm.day} $month ${tm.year}";
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
            elevation: 0,
            bottom: PreferredSize(
              preferredSize: const Size.fromHeight(5.0),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: <Widget>[
                  _showDatePicker(),
                  _addFoodButton(),
                ],
              ),
            )),
        body: StreamProvider<List<FoodTrackTask>>.value(
          initialData: [],
          value: new DatabaseService(
                  uid: "calorie-tracker-b7d17", currentDate: DateTime.now())
              .foodTracks,
          child: new Column(children: <Widget>[
            _calorieCounter(),
            Expanded(
                child: ListView(
              children: <Widget>[FoodTrackList(datePicked: _value)],
            ))
          ]),
        ));
  }
}

class FoodTrackList extends StatelessWidget {
  final DateTime datePicked;
  FoodTrackList({required this.datePicked});

  @override
  Widget build(BuildContext context) {
    final DateTime curDate =
        new DateTime(datePicked.year, datePicked.month, datePicked.day);

    final foodTracks = Provider.of<List<FoodTrackTask>>(context);

    List findCurScans(List foodTrackFeed) {
      List curScans = [];
      foodTrackFeed.forEach((foodTrack) {
        DateTime scanDate = DateTime(foodTrack.createdOn.year,
            foodTrack.createdOn.month, foodTrack.createdOn.day);
        if (scanDate.compareTo(curDate) == 0) {
          curScans.add(foodTrack);
        }
      });
      return curScans;
    }

    List curScans = findCurScans(foodTracks);

    return ListView.builder(
      scrollDirection: Axis.vertical,
      physics: ClampingScrollPhysics(),
      shrinkWrap: true,
      itemCount: curScans.length + 1,
      itemBuilder: (context, index) {
        if (index < curScans.length) {
          return FoodTrackTile(foodTrackEntry: curScans[index]);
        } else {
          return SizedBox(height: 5);
        }
      },
    );
  }
}

class FoodTrackTile extends StatelessWidget {
  final FoodTrackTask foodTrackEntry;
  DatabaseService databaseService = new DatabaseService(
      uid: "calorie-tracker-b7d17", currentDate: DateTime.now());

  FoodTrackTile({required this.foodTrackEntry});

  List macros = CalorieStats.macroData;

  @override
  Widget build(BuildContext context) {
    return ExpansionTile(
      leading: CircleAvatar(
        radius: 25.0,
        backgroundColor: Color(0xff5FA55A),
        child: _itemCalories(),
      ),
      title: Text(foodTrackEntry.food_name,
          style: TextStyle(
            fontSize: 16.0,
            fontFamily: 'Open Sans',
            fontWeight: FontWeight.w500,
          )),
      subtitle: _macroData(),
      children: <Widget>[
        _expandedView(context),
      ],
    );
  }

  Widget _itemCalories() {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.center,
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text(foodTrackEntry.calories.toStringAsFixed(0),
            style: TextStyle(
              fontSize: 16.0,
              color: Colors.white,
              fontFamily: 'Open Sans',
              fontWeight: FontWeight.w500,
            )),
        Text('kcal',
            style: TextStyle(
              fontSize: 10.0,
              color: Colors.white,
              fontFamily: 'Open Sans',
              fontWeight: FontWeight.w500,
            )),
      ],
    );
  }

  Widget _macroData() {
    return Row(
      children: <Widget>[
        Container(
          width: 200,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              Row(
                children: <Widget>[
                  Container(
                    height: 8,
                    width: 8,
                    decoration: BoxDecoration(
                      color: Color(CARBS_COLOR),
                      shape: BoxShape.circle,
                    ),
                  ),
                  Text(' ' + foodTrackEntry.carbs.toStringAsFixed(1) + 'g    ',
                      style: TextStyle(
                        fontSize: 12.0,
                        color: Colors.white,
                        fontFamily: 'Open Sans',
                        fontWeight: FontWeight.w400,
                      )),
                  Container(
                    height: 8,
                    width: 8,
                    decoration: BoxDecoration(
                      color: Color(PROTEIN_COLOR),
                      shape: BoxShape.circle,
                    ),
                  ),
                  Text(
                      ' ' + foodTrackEntry.protein.toStringAsFixed(1) + 'g    ',
                      style: TextStyle(
                        fontSize: 12.0,
                        color: Colors.white,
                        fontFamily: 'Open Sans',
                        fontWeight: FontWeight.w400,
                      )),
                  Container(
                    height: 8,
                    width: 8,
                    decoration: BoxDecoration(
                      color: Color(FAT_COLOR),
                      shape: BoxShape.circle,
                    ),
                  ),
                  Text(' ' + foodTrackEntry.fat.toStringAsFixed(1) + 'g',
                      style: TextStyle(
                        fontSize: 12.0,
                        color: Colors.white,
                        fontFamily: 'Open Sans',
                        fontWeight: FontWeight.w400,
                      )),
                ],
              ),
              Text(foodTrackEntry.grams.toString() + 'g',
                  style: TextStyle(
                    fontSize: 12.0,
                    color: Colors.white,
                    fontFamily: 'Open Sans',
                    fontWeight: FontWeight.w300,
                  )),
            ],
          ),
        )
      ],
    );
  }

  Widget _expandedView(BuildContext context) {
    return Padding(
      padding: EdgeInsets.fromLTRB(20.0, 0.0, 15.0, 0.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          expandedHeader(context),
          _expandedCalories(),
          _expandedCarbs(),
          _expandedProtein(),
          _expandedFat(),
        ],
      ),
    );
  }

  Widget expandedHeader(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: <Widget>[
        Text('% of total',
            style: TextStyle(
              fontSize: 14.0,
              color: Colors.white,
              fontFamily: 'Open Sans',
              fontWeight: FontWeight.w400,
            )),
        IconButton(
            icon: Icon(Icons.delete),
            iconSize: 16,
            onPressed: () async {
              print("Delete button pressed");
              databaseService.deleteFoodTrackEntry(foodTrackEntry);
            }),
      ],
    );
  }

  Widget _expandedCalories() {
    double caloriesValue = 0;
    if (!(foodTrackEntry.calories / macros[0]).isNaN) {
      caloriesValue = foodTrackEntry.calories / macros[0];
    }
    return Padding(
      padding: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0),
      child: Row(
        children: <Widget>[
          Container(
            height: 10.0,
            width: 200.0,
            child: LinearProgressIndicator(
              value: caloriesValue,
              backgroundColor: Color(0xffEDEDED),
              valueColor: AlwaysStoppedAnimation<Color>(Color(0xff5FA55A)),
            ),
          ),
          Text('      ' + ((caloriesValue) * 100).toStringAsFixed(0) + '%'),
        ],
      ),
    );
  }

  Widget _expandedCarbs() {
    double carbsValue = 0;
    if (!(foodTrackEntry.carbs / macros[2]).isNaN) {
      carbsValue = foodTrackEntry.carbs / macros[2];
    }
    return Padding(
      padding: EdgeInsets.fromLTRB(0.0, 15.0, 0.0, 0.0),
      child: Row(
        children: <Widget>[
          Container(
            height: 10.0,
            width: 200.0,
            child: LinearProgressIndicator(
              value: carbsValue,
              backgroundColor: Color(0xffEDEDED),
              valueColor: AlwaysStoppedAnimation<Color>(Color(0xffFA5457)),
            ),
          ),
          Text('      ' + ((carbsValue) * 100).toStringAsFixed(0) + '%'),
        ],
      ),
    );
  }

  Widget _expandedProtein() {
    double proteinValue = 0;
    if (!(foodTrackEntry.protein / macros[1]).isNaN) {
      proteinValue = foodTrackEntry.protein / macros[1];
    }
    return Padding(
      padding: EdgeInsets.fromLTRB(0.0, 5.0, 0.0, 0.0),
      child: Row(
        children: <Widget>[
          Container(
            height: 10.0,
            width: 200.0,
            child: LinearProgressIndicator(
              value: proteinValue,
              backgroundColor: Color(0xffEDEDED),
              valueColor: AlwaysStoppedAnimation<Color>(Color(0xffFA8925)),
            ),
          ),
          Text('      ' + ((proteinValue) * 100).toStringAsFixed(0) + '%'),
        ],
      ),
    );
  }

  Widget _expandedFat() {
    double fatValue = 0;
    if (!(foodTrackEntry.fat / macros[3]).isNaN) {
      fatValue = foodTrackEntry.fat / macros[3];
    }
    return Padding(
      padding: EdgeInsets.fromLTRB(0.0, 5.0, 0.0, 10.0),
      child: Row(
        children: <Widget>[
          Container(
            height: 10.0,
            width: 200.0,
            child: LinearProgressIndicator(
              value: (foodTrackEntry.fat / macros[3]),
              backgroundColor: Color(0xffEDEDED),
              valueColor: AlwaysStoppedAnimation<Color>(Color(0xff01B4BC)),
            ),
          ),
          Text('      ' + ((fatValue) * 100).toStringAsFixed(0) + '%'),
        ],
      ),
    );
  }
}
```

Now for its explanation:

- `createState()`: This method creates a mutable state for the `DayViewScreen` widget
- `_value`: This `DateTime` value holds the current date that the Day View screen is set to. The left and right arrow buttons allow the user to switch dates accordingly, which will be updated in the `_value` variable
- `databaseService`: This is the `DatebaseService` instance used to fetch and add records to the `foodTracks` collection in the Firestore database we setup in previous steps
- `initState()`: This lifecycle method initializes the `addFoodTrack` variable to an empty `FoodTrackTask` instance. Then `databaseService.getFoodTrackData()` is called to fetch all the `FoodTrack` instances from the Firestore database
- `resetFoodTrack()`: This method is used to reset the `addFoodTrack` variable to an empty `FoodTrack` instance after adding a new `FoodTrack` instance in the Add Food modal
- The `_addFoodButton()`, `_showFoodToAdd()`, `_showAmountHad()`, `_showAddFoodForm()` and `_showUserAmount()` methods are used to render the Add Food Modal that popups when tapping the `Add Food +` button
- `_showDatePicker()`: This method renders the Date toggling mechanism on the top of the screen
- `build()`: In this render method we render the Add Food button and the Date picker widget onto the `appBar`. Then in the body, the `DatabaseService` class is used to fetch all `foodTrack` instances which will be listed using the `StreamProvider` class(more on the `StreamProvider` class in the [Flutter documentation](https://pub.dev/documentation/provider/latest/provider/StreamProvider-class.html)). The `FoodTrackList` class will render the listings. It will required the `_value` argument(which is the date picked by the user), which it will use to filter out the `foodTrack` instances that match that date to within a 24 hour time period and render those instances according. The `FoodTrackTile` class is used to render single items in the listing that the `FoodTrackList` class will render. These items will show the calorie amount, macro-nutritional amounts, and percentages of those values in comparison to the overall day's values in a chart format. Lastly, it will also render a delete button to delete any `foodTrack` instances

Whew! Now that we have gotten through building the Day View screen, let's build the History screen!

Here is a screenshot of how it looks:
![History Screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0h4qoxe7prmhkmkwxx88.png)
_Figure 4: History Screen_

Add the following code to the `lib/src/page/history/history-screen.dart` file:

```
import 'package:flutter/material.dart';
import 'package:calorie_tracker_app/src/model/food_track_task.dart';
import 'package:calorie_tracker_app/component/iconpicker/icon_picker_builder.dart';
import 'package:calorie_tracker_app/src/utils/charts/datetime_series_chart.dart';

class HistoryScreen extends StatefulWidget {
  HistoryScreen();

  @override
  State<StatefulWidget> createState() {
    return _HistoryScreenState();
  }
}

class _HistoryScreenState extends State<HistoryScreen> {
  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
  bool _isBack = true;
  @override
  void initState() {
    super.initState();
  }

  void onClickBackButton() {
    print("Back Button");
    Navigator.of(context).pop();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(
            "History Screen",
            style: TextStyle(
                color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold),
          ),
        ),
        body: Container(
          child: DateTimeChart(),
        ));
  }
}
```

The History screen just renders a simple Time Series chart using the `charts_flutter` library. It uses the `DateTimeChart` widget, covered in an earlier section of this post, to accomplish this.

Last but not least, we'll build the Settings screen. We won't be providing any tangible functionality for it and will only be looking at building its UI.

Here is a screenshot of what it looks like:
![Settings Screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i64bjegln92hdi49uuqt.png)
_Figure 5: Settings Screen_

And here its code which we'll add to the `lib/src/page/settings/settings_screen.dart` file:

```
import 'package:flutter/material.dart';
import 'package:calorie_tracker_app/src/model/food_track_task.dart';
import 'package:calorie_tracker_app/component/iconpicker/icon_picker_builder.dart';
import 'package:settings_ui/settings_ui.dart';

class SettingsScreen extends StatefulWidget {
  SettingsScreen();

  @override
  State<StatefulWidget> createState() {
    return _SettingsScreenState();
  }
}

class _SettingsScreenState extends State<SettingsScreen> {
  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
  bool _isBack = true;

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    // const IconData computer = IconData(0xe185, fontFamily: 'MaterialIcons');
    return SettingsList(
      sections: [
        SettingsSection(
          title: Text('Settings',
              textAlign: TextAlign.center,
              style: const TextStyle(fontWeight: FontWeight.bold)),
          tiles: [
            SettingsTile(
              title: Text('Language',
                  textAlign: TextAlign.center,
                  style: const TextStyle(fontWeight: FontWeight.bold)),
              value: Text('English',
                  textAlign: TextAlign.center,
                  style: const TextStyle(fontWeight: FontWeight.bold)),
              leading: Icon(Icons.language),
              onPressed: (BuildContext context) {},
            ),
            SettingsTile(
              title: Text('Environment',
                  textAlign: TextAlign.center,
                  style: const TextStyle(fontWeight: FontWeight.bold)),
              // subtitle: 'English',
              value: Text('Development',
                  textAlign: TextAlign.center,
                  style: const TextStyle(fontWeight: FontWeight.bold)),
              leading: Icon(Icons.computer),
              onPressed: (BuildContext context) {},
            ),
            SettingsTile(
              title: Text('Environment',
                  textAlign: TextAlign.center,
                  style: const TextStyle(fontWeight: FontWeight.bold)),
              // subtitle: 'English',
              value: Text('Development',
                  textAlign: TextAlign.center,
                  style: const TextStyle(fontWeight: FontWeight.bold)),
              leading: Icon(Icons.language),
              onPressed: (BuildContext context) {},
            ),
          ],
        ),
        SettingsSection(
          title: Text('Account',
              textAlign: TextAlign.center,
              style: const TextStyle(fontWeight: FontWeight.bold)),
          tiles: [
            SettingsTile(
                title: Text('Phone Number',
                    textAlign: TextAlign.center,
                    style: const TextStyle(fontWeight: FontWeight.bold)),
                leading: Icon(Icons.local_phone)),
            SettingsTile(
                title: Text('Email',
                    textAlign: TextAlign.center,
                    style: const TextStyle(fontWeight: FontWeight.bold)),
                leading: Icon(Icons.email)),
            SettingsTile(
                title: Text('Sign out',
                    textAlign: TextAlign.center,
                    style: const TextStyle(fontWeight: FontWeight.bold)),
                leading: Icon(Icons.logout)),
          ],
        ),
        SettingsSection(
          title: Text('Misc',
              textAlign: TextAlign.center,
              style: const TextStyle(fontWeight: FontWeight.bold)),
          tiles: [
            SettingsTile(
                title: Text('Terms of Service',
                    textAlign: TextAlign.center,
                    style: const TextStyle(fontWeight: FontWeight.bold)),
                leading: Icon(Icons.document_scanner)),
            SettingsTile(
                title: Text('Open source licenses',
                    textAlign: TextAlign.center,
                    style: const TextStyle(fontWeight: FontWeight.bold)),
                leading: Icon(Icons.collections_bookmark)),
          ],
        )
      ],
    );
  }
}
```

Not much to explain here other than that we are using the `settings_ui` library(see its [GitHub page here](https://github.com/yako-dev/flutter-settings-ui)) to provide us with the `SettingsSection`, `SettingsList`, and `SettingsTile` widgets to create a typical setting screen, as seen in most modern mobile applications.

Ok, we have finished developing our application now! Give yourself a pat on the back if you made it this far.

Now I give an brief summary of Routing in Flutter and how we chose to implement routing for this application:

Flutter has two types of routing: **(1)** Imperative routing via the `Navigator` widget and **(2)** Idiomatic declarative routing via the `Router` widget. Traditionally, most web applications use idiomatic declarative routing while most mobile applications used some sort of imperative routing mechanism.
As a general guideline, it is best to use Imperative routing for smaller Flutter applications and Idiomatic declarative routing for larger apps. Accordingly, we have chosen the Imperative routing mechanism for this application, as evident by the `Navigator.of(context).push()` calls that are commonplace throughout this application.

## Running And Testing The App

Now all that's left is to run the application via the VSCode Debugger, if you're developing on VSCode, or using the _Run Icon_ if you're developing on Android Studio.

Here's [an informative blog post](https://flutteragency.com/set-up-an-emulator-for-vscode/) on how to run Flutter applications on VSCode if you need help with that and [here is one](https://www.geeksforgeeks.org/android-studio-setup-for-flutter-development/) for running Flutter apps on Android Studio, if you need it.

Here are some screenshots of what the app should look like:
![Flutter 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/slmwp9t12rr6sfvpsa8p.png)
![Flutter 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1b7btfa4jhh54728wfg9.png)
![Flutter 4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7u1qzb12safb7obhrro5.png)
![Flutter 5](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mb5j9hsh11pfk6v91x91.png)
_Figure 6: Screenshots of the Calorie Tracker App we've just built_

## Conclusion

If you managed to follow along, Congrats! You now know how to build a Calorie Tracker application on Flutter. If not, look into [my GitHub source code repo](https://github.com/ShehanAT/flutter_calorie_tracker_app) for this app and feel free to clone it and replicate it as you wish.

Well that’s all for today, I hope you found this article helpful. Thanks so much for reading my article! Feel free to follow me on [Twitter](https://twitter.com/Shehan_Atuk) and [GitHub](https://github.com/ShehanAT), connect with me on [LinkedIn](https://www.linkedin.com/in/shehan-atukorala-8631491a5/) and subscribe to my [YouTube channel](https://www.youtube.com/channel/UCtxed_NljgtAXrQMMdLvhrQ).