Dart package for the Open Food Facts API. Free and Easy access to food products information from all around the world.
Open Food Facts is an open and crowdsourced database that contains various information about over a million food products. Data such as Ingredients, Additives, Brands, Nutritional intakes, Serving sizes, Nutriscore, NOVA group, and many more can be found in the Open Food Facts database.
You are free to use the API but also contribute to it, the products list is constantly growing thanks to the many awesome contributors we have, and your help is more than welcomed.
We use the ability of the Open Food Facts API to return products results in Json, we then generate easily understandable objects structures to make it simple for you to use.
This plugin also allows you to edit a product or upload a new one to Open Food Facts. Using the same simple product structure you can create a product object or edit an existing one and send it to the API using a single function.
Jump to Object structures, Functions or Examples.
This section details all the available fields in each class.
No authentication is required for read and search operations, however, you have to add a User-Agent HTTP Header with the name of your app, the version, system and a url (if any), not to be blocked by mistake.
OpenFoodAPIClient.userAgent = UserAgent(name: '', version: '', system:'', url:'', comment:'');
This class handles user identification when writing into the database.
String userId
String password
This class is the result of a product request.
int status
String barcode
String statusVerbose
Product product
This class contains all the data regarding a specific product.
String barcode
String productName
OpenFoodFactsLanguage lang
String quantity
String servingSize
String brands
List<String> brandTags
String imgSmallUrl
ImageList selectedImages
List<Ingredient> ingredients
String ingredientsText
Nutriments nutriments
NutrimentLevels nutrimentLevels
String nutrimentEnergyUnit
String nutrimentDataPer
String nutriscore
Additives additives
String categories;
List<String> categoriesTags
List<String> labelsTags
List<String> miscTags
List<String> stateTags
List<String> tracesTags
This class handles the different types and sizes of the image, it also contains it's URL.
static const String FIELD_FRONT
static const String FIELD_INGREDIENTS
static const String FIELD_NUTRITION
static const String FIELD_OTHER
static const String SIZE_SMALL
static const String SIZE_THUMB
static const String SIZE_DISPLAY
String field
String size
OpenFoodFactsLanguage language
String url
This class contains essential data of an ingredient of the product.
int rank
String id
String text
int percent
bool bold
This class contains the nutrition facts of the product.
---- For a serving of 100g ----
double salt
double fiber
double sugars
double fat
double saturatedFat
double proteins
double carbohydrates
double energy
int novaGroup
---- Per recommanded serving ----
double saltServing
double saltServing
double sugarServing
double fatServing
double saturatedFatServing
double proteinsServing
double carbohydratesServing
double energyServing
int novaGroupServing
This class indicates the level of each nutrient from low to high.
Levels are classified in an enum.
enum Level { LOW, MODERATE, HIGH, UNDEFINED }
static const String NUTRIENT_SUGARS
static const String NUTRIENT_FAT
static const String NUTRIENT_SATURATED_FAT
static const String NUTRIENT_SALT
Map<String, Level> levels
This class contains a list in each format of the additives present in the product.
An id is formated as followed : 'en:e100i'
While a name is formated like this : 'E100i'
List<String> ids
List<String> names
This abstract class is used to pass parameters to a product search query. At the moment 8 types of parameters are available.
OutputFormat :
- OutputFormat(format: Format.JSON)
- OutputFormat(format: Format.JQM)
- OutputFormat(format: Format.XML)
ContainsAdditives :
- ContainsAdditives(filter: true)
- ContainsAdditives(filter: false)
Page :
- Page(page: 0)
- Page(page: 1)
- Page(page: 2)
- ...
PageSize :
- PageSize(size: 1)
- PageSize(size: 2)
- PageSize(size: 3)
- ...
SearchSimple :
- SearchSimple(active: true)
- SearchSimple(active: false)
SearchTerms :
- SearchTerms(["Fruit", "Coques", ...])
SortBy :
- SortBy(option: SortOption.PRODUCT_NAME)
- SortBy(option: SortOption.CREATED)
- SortBy(option: SortOption.EDIT)
- SortBy(option: SortOption.POPULARITY)
TagFilter :
- TagFilter(tagType: "categories", contains: true, tagName: "breakfast_cereals")
- TagFilter(tagType: "nutrition_grades", contains: true, tagName: "A")
- ...
This class contains the result of a product search query.
int page
int pageSize
int count
int skip
list<Product> products
This class is the object template to be provided to the image upload function.
OpenFoodFactsLanguage lang
Uri imageUri
String barcode
String imageField
This class handles the status of a call to the API.
final status // to be completed
String error
String statusVerbose
int imageId
This class allows you to configure a Product query by specifying the language and fields. It is then passed to the getProduct() function.
String barcode;
OpenFoodFactsLanguage language;
List<ProductField> fields;
This class allows you to configure a Product query by specifying the language, fields and search parameters. It is then passed to the searchProducts() function.
OpenFoodFactsLanguage language;
List<ProductField> fields;
List<Parameter> parametersList;
This enum allows you to easily select product fields. You can find the full list of available fields here : Product Fields.
This enum contains all the supported languages (180+) and allows an easy and reliable language selection. You can find the full list of available languages here : Open Food Facts Languages.
Get a product from a barcode
This function retrieves the data of the product following the ProductQueryConfigurations passed to it. The result is a ProductResult.
Parameters : ProductQueryConfiguration config
Optional : User user
ProductResult result = await OpenFoodAPIClient.getProduct(configurations, user: TestConstants.TEST_USER);
See the example
Get a product from a barcode (RAW)
This function retrieves the data of the product but returns it "as is". Fields are returned in the selected language if available. The result is a ProductResult.
Parameters : String barcode, OpenFoodFactsLanguage lang
Optional : User user
ProductResult result = await OpenFoodAPIClient.getProductRaw("yourbarcode", OpenFoodFactsLanguage.FRENCH);
See the example
This function allows you to get a list of products according to a list of parameters. The result is a SearchResult.
Parameters : List<Parameter> parameterList
Optional : String lang
SearchResult result = await OpenFoodAPIClient.searchProducts(parameterList, lang: User.LANGUAGE_FR);
See the example
This functions sends a product to the API in order to be written into the database. The result is a Status.
Parameters : User user, ProductSearchQueryConfigurations config
Status result = await OpenFoodAPIClient.searchProducts(TestConstants.TEST_USER, configuration);
See the example
This function allows you to send a picture linked to an existing product in the database.
Parameters : User user, ProductImage image
Status result = await OpenFoodAPIClient.addProductImage(user, image);
See the example
This function checks if the user_id and password of a provided user object are correct.
Parameters : User user
bool result = await OpenFoodAPIClient.login(user);
This functions creates a new Open Food Facts user.
Parameters: User user, String name, String email, String? requested_org, bool newsletter = true,
Status status = await OpenFoodAPIClient.register(...);
When creating a producer account use requested_org
to name the Producer or brand
Possible status.status
responses:
Code | Meaning |
---|---|
201 | User created |
400 (+ status.error ) |
Request error |
500 | Server error |
String barcode = "0000000000000";
ProductQueryConfiguration configurations = ProductQueryConfiguration(
barcode,
language: OpenFoodFactsLanguage.GERMAN,
fields: [ProductField.NUTRIMENTS,
ProductField.INGREDIENTS_TEXT,
ProductField.INGREDIENTS,
ProductField.ADDITIVES
ProductField.NUTRIENT_LEVELS]);
ProductResult result = await OpenFoodAPIClient.getProduct(configurations,
user: TestConstants.TEST_USER);
if(result.status != 1) {
print("Error retreiving the product : ${result.status.errorVerbose}");
return;
}
String ingredientsT = result.product.ingredientsText;
List<Ingredient> ingredients = result.product.ingredients;
double energy_100g = result.product.nutriments.energy;
double fat_100g = result.product.nutriments.fat;
double salt_serving = result.product.nutriments.saltServing;
double fat_serving = result.product.nutriments.fatServing;
String firstAdditiveName = result.product.additives.names[0];
Level sugars_level = result.product.nutrientLevels.levels[NutrientLevels.NUTRIENT_SUGARS];
Notice : You should use getProduct() as much as possible to filter the fields in the result and speed up the API answer
String barcode = "0000000000000";
ProductResult result = await OpenFoodAPIClient.getProductRaw(
barcode, OpenFoodFactsLanguage.GERMAN,
user: TestConstants.TEST_USER);
if(result.status != 1) {
print("Error retrieving the product : ${result.status.errorVerbose}");
return;
}
String ingredientsT = result.product.ingredientsTextFR; // It is necessary to specify the language as the we are using getProductRaw()
List<Ingredient> ingredients = result.product.ingredients;
double energy_100g = result.product.nutriments.energy;
double fat_100g = result.product.nutriments.fat;
double salt_serving = result.product.nutriments.saltServing;
double fat_serving = result.product.nutriments.fatServing;
String firstAdditiveName = result.product.additives.names[0];
Level sugars_level = result.product.nutrientLevels.levels[NutrientLevels.NUTRIENT_SUGARS];
var parameters = <Parameter>[
const OutputFormat(format: Format.JSON),
const Page(page: 5),
const PageSize(size: 10),
const SearchSimple(active: true),
const SortBy(option: SortOption.PRODUCT_NAME),
const TagFilter(
tagType: "categories",
contains: true,
tagName: "breakfast_cereals"),
const TagFilter(
tagType: "nutrition_grades", contains: true, tagName: "A")
];
ProductSearchQueryConfiguration configuration =
ProductSearchQueryConfiguration(
parametersList: parameters,
fields: [ProductField.NUTRISCORE],
language: OpenFoodFactsLanguage.FRENCH);
SearchResult result = await OpenFoodAPIClient.searchProducts(
TestConstants.TEST_USER, configuration);
String nutriscore_first_product = result.products[0].nutriscore;
User myUser = User("Myself", "secret_password");
Product newProduct = Product(
barcode: "0000000000000",
productName: "Example Product",
quantity: "200g",
brands: "Example Brand",
lang: "fr",
ingredientsText: "Ingredient 1, Ingredient 2, Ingredient 3",
categories: "Category 1, Category 2"
...
);
Status result = await OpenFoodAPIClient.saveProduct(myUser, newProduct);
if(result.status != 1) {
print("An error occured while sending the product : ${result.statusVerbose}");
return;
}
print("Upload was successful");
User myUser = User("Myself", "secret_password");
String barcode = "0000000000000";
SendImage image = new SendImage(
lang: "fr",
barcode: barcode,
imageField: ProductImage.FIELD_FRONT,
imageUri: Uri.parse("path_to_my_image"),
);
Status status = await OpenFoodAPIClient.addProductImage(myUser, image);
if(result.status != 1) {
print("An error occured while sending the picture : ${result.statusVerbose}");
return;
}
print("Upload was successful");
TL;DR: use the "..InLanguages" fields if you intend to display products data in specific language(s). You must use the "..InLanguages" fields if you intend to update products. Otherwise you might accidentally corrupt products by overwriting proper-language data with improper-language data.
See detailed explanation and examples below.
Most of end-user facing apps want to display products data to a user in the language(s) the user understands. In order for a developer to properly implement multilingual Open Food Facts usage, the developer might want to understand how Open Food Facts works with languages.
Here are main concepts.
Products in Open Food Facts can have some of their fields in multiple languages. Such fields are of 2 types:
- Text fields with info taken directly from product packaging (like product name and ingredients list). Such fields generally are not touched by the backend and are stored in the DB exactly as they were sent.
- Lists of tags with meta-information (like list countries where the product is being sold, or like product's categories). Such fields are processed by the backend in different ways and often shouldn't be displayed to the user as-is.
In addition to that, fields of the [Product] class with multilingual support also are of 2 types:
- Fields with simple value (e.g. [Product.productName]).
- Fields with multilingual values (e.g. [Product.productNameInLanguages]).
Lastly, products in Open Food Facts have a main language. That's the language which will be used to fill fields of [Product] when no specific language settings were provided. Most likely you don't want to show the data to your users in the product's main language. Most likely you want to prefer data in some specific language.
Let's say you want a product to be displayed in German and so you've set the value of [AbstractQueryConfiguration.language] to German. Here's how the described above types of fields work in combinations:
- 1-1 (packaging info + simple value). Fields like [Product.productName] will be in German if the German versions of them are available in OFF. If the German version is not available, the fields will be in the main product's language.
- 1-2 (packaging info + multilingual value).
Fields like [Product.productNameInLanguages] will have the German value
inside if it's available.
If it's not available,
product.productNameInLanguages[GERMAN]
will benull
. - 2-1 (meta info + simple value).
Fields like [Product.countriesTags] will not be in German, ever.
They will have values like
["en:austria", "en:belgium", "en:canada"]
. - 2-2 (meta info + multilingual value).
Fields like [Product.countriesTagsInLanguages] will have German values
when available, if some of the values is not available in German it
will have a language prefix:
["Österreich ", "Belgien", "en:canada"]
.
Those 4 combinations can be observed "in the wild" by executing next request: https://world.openfoodfacts.org/api/v2/product/3017620422003?lc=de&fields=product_name,product_name_de,countries_tags,countries_tags_de
As you can see, the multilingual values should be prefered in most cases when product's data is displayed to a user.
final conf = ProductQueryConfiguration(
barcode,
language: OpenFoodFactsLanguage.GERMAN,
fields: [ProductField.NAME]);
final product = await OpenFoodAPIClient.getProduct(conf);
// The requested language is German, so `product.productName`
// either is in German, or in main product's language, or null.
var name = product.productName ?? 'No name';
_displayProductName(name);
final conf = ProductQueryConfiguration(
barcode,
language: OpenFoodFactsLanguage.GERMAN,
languages: [OpenFoodFactsLanguage.RUSSIAN],
fields: [ProductField.NAME, ProductField.NAME_IN_LANGUAGES]);
final product = await OpenFoodAPIClient.getProduct(conf);
var name = product.productNameInLanguages[OpenFoodFactsLanguage.GERMAN];
name ??= product.productNameInLanguages[OpenFoodFactsLanguage.RUSSIAN];
name ??= product.productName;
name ??= 'No name';
_displayProductName(name);
final conf = ProductQueryConfiguration(
barcode,
language: OpenFoodFactsLanguage.GERMAN,
fields: [ProductField.CATEGORIES_TAGS_IN_LANGUAGES]);
final product = await OpenFoodAPIClient.getProduct(conf);
var categories = product.categoriesTagsInLanguages[OpenFoodFactsLanguage.GERMAN];
// Erase not translated categories (e.g. "en:cake").
categories = categories.where((c) => !c.startsWith(RegExp('\w+:')));
_displayProductCategories(categories);
final conf = ProductQueryConfiguration(
barcode,
language: OpenFoodFactsLanguage.GERMAN,
fields: [ProductField.CATEGORIES_TAGS_IN_LANGUAGES]);
final product = await OpenFoodAPIClient.getProduct(conf);
var categories = product.categoriesTagsInLanguages[OpenFoodFactsLanguage.GERMAN];
// Will have only categories which start with
// lang prefixes (e.g. "en:cake").
final notTranslatedCategories = categories.where((c) => c.startsWith(RegExp('\w+:')));
// Translated categories only.
final translatedCategories = categories.where((c) => !c.startsWith(RegExp('\w+:')));
final updatedCategories = await _askUserToUpdateCategories(
translatedCategories);
// Set updated German categories AND avoid
// erasure of the not translated ones.
product.categoriesTagsInLanguages[OpenFoodFactsLanguage.GERMAN] =
updatedCategories + notTranslatedCategories;
await OpenFoodAPIClient.saveProduct(_user, product);