An open API service indexing awesome lists of open source software.

https://github.com/turskyi/portion_control

PortionControl is a minimalist weight management app that helps users track food portions in grams and adjust intake based on weight trends. With simple logging and dynamic recommendations, it offers a stress-free alternative to calorie counting, focusing on sustainable, healthy eating habits.
https://github.com/turskyi/portion_control

bloc drift flutter

Last synced: 3 months ago
JSON representation

PortionControl is a minimalist weight management app that helps users track food portions in grams and adjust intake based on weight trends. With simple logging and dynamic recommendations, it offers a stress-free alternative to calorie counting, focusing on sustainable, healthy eating habits.

Awesome Lists containing this project

README

          

[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner-direct-single.svg)](https://stand-with-ukraine.pp.ua)
[![style: flutter lints](https://img.shields.io/badge/style-flutter__lints-blue)](https://pub.dev/packages/flutter_lints)
[![codecov](https://codecov.io/gh/Turskyi/portion_control/graph/badge.svg?token=66LWUIL7WJ)](https://codecov.io/gh/Turskyi/portion_control)
[![Code Quality and Tests](https://github.com/Turskyi/portion_control/actions/workflows/code_quality_tests.yml/badge.svg)](https://github.com/Turskyi/portion_control/actions/workflows/code_quality_tests.yml)
[![Upload Android Build to App Tester](https://github.com/Turskyi/portion_control/actions/workflows/flutter_android_ci.yml/badge.svg)](https://github.com/Turskyi/portion_control/actions/workflows/flutter_android_ci.yml)
[![Codemagic build status](https://api.codemagic.io/apps/67b7c966163430e15999e56f/67b7c966163430e15999e56e/status_badge.svg)](https://codemagic.io/app/67b7c966163430e15999e56f/67b7c966163430e15999e56e/latest_build)
![GitHub release (latest by date)](https://img.shields.io/github/v/release/Turskyi/portion_control)
[![Deploy to Firebase Hosting on merge](https://github.com/Turskyi/portion_control/actions/workflows/firebase-hosting-merge.yml/badge.svg)](https://github.com/Turskyi/portion_control/actions/workflows/firebase-hosting-merge.yml)
[![Deploy to Firebase Hosting on PR](https://github.com/Turskyi/portion_control/actions/workflows/firebase-hosting-pull-request.yml/badge.svg)](https://github.com/Turskyi/portion_control/actions/workflows/firebase-hosting-pull-request.yml)
[![wakatime](https://wakatime.com/badge/user/f9df5074-b4ea-4c17-b001-fff428ab82aa/project/f2a257e8-361a-4986-8bdf-2cb2a69c765d.svg)](https://wakatime.com/badge/user/f9df5074-b4ea-4c17-b001-fff428ab82aa/project/f2a257e8-361a-4986-8bdf-2cb2a69c765d)
GitHub commit activity

# Portion Control

**Portion Control** is a simple and intuitive Flutter app designed to help users
track their food intake and weight straightforwardly. The app allows users to
record their body weight and food portions, making it easier to monitor the
relationship between food consumption and weight changes.

**Official Website:** [portioncontrol.ca](https://portioncontrol.ca)

## Table of Contents

- [Features](#features)
- [Getting Started](#getting-started)
- [Project Structure](#project-structure)
- [Code Explanation](#code-explanation)
- [Testing](#testing)
- [Contributing](#contributing)
- [Contact](#contact)
- [Screenshot](#screenshot)
- [Download](#download)

## Features

- **Track Body Weight**: Enter your current body weight (in kilograms).
- **Track Food Portions**: Input food portion weight (in grams) before every
meal.
- **Simple Interface**: The app focuses on simplicity, with no complex meal
logging or calorie counting.

## Getting Started

To get started with the project, follow the steps below:

### Prerequisites

Before you begin, make sure you have the following installed:

1. **Flutter SDK**: You can follow the installation instructions on the
[Flutter website](https://docs.flutter.dev/get-started/install).
2. **Android Studio** and **Visual Studio Code**: IDE for Flutter
development.
3. **Dart SDK**: It comes bundled with Flutter, but ensure you're on the
latest
stable version.

### Installation

Clone this repository to your local machine:

```bash
git clone https://github.com/Turskyi/portion_control.git
```

Navigate to the project directory:

```bash
cd portion_control
```

Create a `.env` file and add the following content:

```env
RESEND_API_KEY="re_abcdefg12345"
```

You can get a free API key from [https://resend.com](https://resend.com).

Install dependencies:

```bash
flutter pub get
```

### Create generated files:

```bash
dart run build_runner clean
dart run build_runner build --delete-conflicting-outputs
```

#### Post-clone required files (important)

After cloning the repository, some files are not versioned but are required for
the app to run. Add the following files before building or running the app:

1. `android/key.properties` with the following content (fill values as needed):

```properties
# dev debug environment variables
dev.SIGNING_KEY_DEBUG_PATH=../keystore/portion_control_debug.keystore
dev.SIGNING_KEY_DEBUG_PASSWORD=
dev.SIGNING_KEY_DEBUG_KEY=portion_control_debug
dev.SIGNING_KEY_DEBUG_KEY_PASSWORD=
# production release environment variables
production.SIGNING_KEY_RELEASE_PATH=../keystore/portion_control_release.keystore
production.SIGNING_KEY_RELEASE_PASSWORD=
production.SIGNING_KEY_RELEASE_KEY=portion_control_release
production.SIGNING_KEY_RELEASE_KEY_PASSWORD=
FIREBASE_ANDROID_APP_ID=
FIREBASE_TOKEN=
CODECOV_TOKEN=
GOOGLE_SERVICES=
RELEASE_KEYSTORE=
DEBUG_KEYSTORE=
KEY_PROPERTIES=
```

2. `android/keystore/portion_control_debug.keystore` (create or obtain the debug
keystore)
3. `android/keystore/portion_control_release.keystore` (create or obtain the
release keystore)
4. `android/app/google-services.json` (Firebase Android configuration file)

**Notes:**

- Keep these files out of version control.
- Fill in passwords and IDs (e.g., Firebase App ID, tokens) in
`android/key.properties` as appropriate.

### Running the App

To run the app, use the following command in your terminal:

```bash
flutter run
```

This will launch the app on your connected device or emulator.

## Project Structure

Here's a breakdown of the main components in this Flutter app:

```
lib/
├── application_services/
├── di/
├── domain/
├── env/
├── extensions/
├── infrastructure/
├── localization/
├── res/
├── router/
├── services/
├── ui/
├── app.dart
└── main.dart
```

## Code Explanation

`main.dart`

This file contains the main entry point for the app.

`home_page.dart`

Contains the layout for the home page, where users input their body weight and
food portion weight.

`Theme`

The app uses `Material3` components and a color scheme to create a clean and
vibrant user interface.

## Why are `repositories` located in the `infrastructure` component?

**Repositories** are placed in the `infrastructure` component because they
directly interact with the `database`. If we were to place `repositories` in
the application services layer, they would not have access to the outer layer,
and hence to the `database`.

## Testing

Join our testing program and provide valuable feedback:

- [Android App Distribution Tester Invite](https://appdistribution.firebase.dev/i/3a90590762e477b7)
- [Apple TestFlight Invite](https://testflight.apple.com/join/aJkP43FB)

### Widget Testing

Tests for this project ensure that the app's UI components render correctly.
You can run the tests using the following command:

```bash
flutter test
```

## Roadmap

This project is under active development.

## Contributing

We welcome contributions from the community!
To contribute:

1. Fork this repository.
2. Create a `.env` file and add the following content:

```env
RESEND_API_KEY="re_abcdefg12345"
```

You can get a free API key from [https://resend.com](https://resend.com).

**Also:** after cloning the repository add the unversioned files required to
run the app (see **"Post-clone required files"** in the **Getting Started**
section for details).

3. Create a new branch (`git checkout -b feature-name`).
4. Commit your changes (`git commit -am 'Add feature'`).
5. Push to the branch (`git push origin feature-name`).
6. Create a new Pull Request.

Please ensure your code follows the existing style guidelines and includes
tests where applicable.

Style guides:

[Style guide for Flutter](https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo),
[Dart style guide](https://dart.dev/effective-dart).

- [DO use trailing commas for all function calls and declarations unless the function call or definition, from the start of the function name up to the closing parenthesis, fits in a single line.](https://dart-lang.github.io/linter/lints/require_trailing_commas.html)

- [DON'T cast a nullable value to a non-nullable type. This hides a null check and most of the time it is not what is expected.](https://dart-lang.github.io/linter/lints/avoid_as.html)

- [PREFER using
`const` for instantiating constant constructors](https://dart-lang.github.io/linter/lints/prefer_const_constructors.html)

If a constructor can be invoked as const to produce a canonicalized instance,
it's preferable to do so.

- [DO sort constructor declarations before other members](https://dart-lang.github.io/linter/lints/sort_constructors_first.html)

- ### Avoid Mental Mapping

A single-letter name is a poor choice; it's just a placeholder that the
reader must mentally map to the actual concept. There can be no worse reason
for using the name `c` than because `a` and `b` were already taken.

- ### Method names

Methods should have verb or verb phrase names like `postPayment`, `deletePage`,
or `save`. Accessors, mutators, and predicates should be named for their value
and prefixed with `get`…, `set`…, and `is`… respectively.

- ### Use Intention-Revealing Names

If a name requires a comment, then the name does not reveal its intent.

- ### Use Pronounceable Names

If you can't pronounce it, you can't discuss it without sounding silly.

- ### Class Names

Classes and objects should have noun or noun phrase names and not include
indistinct noise words:

```dart
GOOD: Customer, WikiPage, Account, AddressParser.
BAD: Manager, Processor, Data, Info
```

- ### Functions should be small

Functions should hardly ever be 20 lines long.
Blocks within `if` statements, `else` statements, `while` statements, and so on
should be **_one_** line long. Probably that line should be a function call.

- ### Functions should do one thing

To know that a function is doing more than “one thing” is if you can extract
another function from it with a name that is not merely a restatement of its
implementation.

- ### One Level of Abstraction per Function

We want the code to read like a top-down narrative. We want every function to
be followed by those at the next level of abstraction so that we can read the
program, descending one level of abstraction at a time as we read down the list
of functions.

- ### Dependent Functions

If one function calls another, they should be vertically close, and the caller
should be **_above_** the callee, if possible.

- ### Use Descriptive Names

Don't be afraid to make a name long. A long descriptive name is better than
a short enigmatic name. A long descriptive name is better than a long
descriptive comment.

- ### Function Arguments

The ideal number of arguments for a function is zero (niladic). Next comes one
(monadic), followed closely by two (dyadic). Three arguments (triadic) should
be avoided where possible.

GOOD:`includeSetupPage()`

BAD: `includeSetupPageInto(newPageContent)`

- ### Flag Arguments

Flag arguments are ugly. Passing a boolean into a function is a truly terrible
practice. It immediately complicates the signature of the method, loudly
proclaiming that this function does more than one thing. It does one thing if
the flag is true and another if the flag is false!

GOOD: `renderForSuite()`
`renderForSingleTest()`

BAD: `render(bool isSuite)`

- ### Explain Yourself in Code

Only the code can truly tell you what it does. Comments are, at best, a
necessary evil. Rather than spend your time writing the comments that explain
the mess you've made, spend it cleaning that mess. Inaccurate comments are
far worse than no comments at all.

BAD:
`// Check to see if the employee is eligible for full benefits`
`if((employee.flags & hourlyFlag) && (employee.age > 65))`

GOOD:
`if (employee.isEligibleForFullBenefits())`

- ### TODO Comments

Nowadays, good IDEs provide special gestures and features to locate all the
`//TODO` comments, so it's not likely that they will get lost.

- ### Public APIs

There is nothing quite so helpful and satisfying as a well-described public API.
It would be challenging, at best, to write programs without them.

```dart
/// dart doc comment
```

- ### Commented-Out Code

We've had good source code control systems for a very long time now. Those
systems will remember the code for us. We don't have to comment it out
anymore.

- ### Position Markers

In general, they are the clutter that should be eliminated—especially the noisy
train of slashes at the end. If you overuse banners, they'll fall into the
background noise and be ignored.

```dart
// Actions //////////////////////////////////
```

- ### Don't Return Null

When we return `null`, we are essentially creating work for ourselves and
foisting problems upon our callers. All it takes is one missing `null` check to
send an app spinning out of control.

- ### Don't Pass Null

In most programming languages, there is no **GOOD** way to deal with a `null`
that is passed by a caller accidentally. Because this is the case, the rational
approach is to forbid passing null by default. When you do, you can code with
the knowledge that a `null` in an argument list is an indication of a problem,
and end up with far fewer careless mistakes.

- ### Classes Should Be Small!

With functions, we measured size by counting physical lines. With classes, we
use a different measure. **We count responsibilities.** The Single
Responsibility Principle (SRP) states that a class or module should have one,
and only one, reason to change. The name of a class should describe what
responsibilities it fulfills. The more ambiguous the class name, the more
likely it has too many responsibilities. The problem is that too many of us
think that we are done once the program works. We move on to the next problem
rather than going back and breaking the overstuffed classes into decoupled
units with single responsibilities.

- ### Artificial Coupling

In general, an artificial coupling is a coupling between two modules that
serves no direct purpose. It is a result of putting a variable, constant, or
function in a temporarily convenient, though inappropriate, location. For
example, general `enum`s should not be contained within more specific classes
because this forces the app to know about these more specific classes. The same
goes for general purpose `static` functions being declared in specific classes.

- ### Prefer Polymorphism to If/Else or Switch/Case

There may be no more than one switch statement for a given type of selection.
The cases in that switch statement must create polymorphic objects that take
the place of other such switch statements in the rest of the system.

- ### Replace Magic Numbers with Named Constants

In general, it is a bad idea to have raw numbers in your code. You should hide
them behind well-named constants. The term “Magic Number” does not apply only
to numbers. It applies to any token that has a value that is not
self-describing.

- ## Encapsulate Conditionals

Boolean logic is hard enough to understand without having to see it in the
context of an `if` or `while` statement. Extract functions that explain the
intent of the conditional.

GOOD: `if(shouldBeDeleted(timer))`

BAD: `if (timer.hasExpired() && !timer.isRecurrent())`

- ### Avoid Negative Conditionals

Negatives are just a bit harder to understand than positives. So, when
possible, conditionals should be expressed as positives.

GOOD: `if (buffer.shouldCompact())`

BAD: `if (!buffer.shouldNotCompact())`

- ### Encapsulate Boundary Conditions

Boundary conditions are hard to keep track of. Put the processing for them in
one place.

```
BAD:
if(level + 1 < tags.length) {
parts = Parse(body, tags, level + 1, offset + endTag);
body = null;
}

GOOD:
int nextLevel = level + 1;
if (nextLevel < tags.length) {
parts = Parse(body, tags, nextLevel, offset + endTag);
body = null;
}
```

- ### Constants versus Enums

Don't keep using the old trick of public `static` `final` `int`s. `enum`s
can have methods and fields. This makes them very powerful tools that allow much
more expression and flexibility.

## Contact

For any inquiries, please visit [portioncontrol.ca](https://portioncontrol.ca)
or contact [support@portioncontrol.ca](mailto:support@portioncontrol.ca).

## Screenshot

screenshot

## Download



google play badge


app store badge