✨ Setup data
This commit is contained in:
parent
cb011ddcf9
commit
7797c1b635
File diff suppressed because one or more lines are too long
@ -3,20 +3,27 @@ PODS:
|
|||||||
- shared_preferences_foundation (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- sqflite (0.0.3):
|
||||||
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
|
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
Flutter:
|
Flutter:
|
||||||
:path: Flutter
|
:path: Flutter
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||||
|
sqflite:
|
||||||
|
:path: ".symlinks/plugins/sqflite/darwin"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||||
|
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
||||||
|
|
||||||
PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796
|
PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796
|
||||||
|
|
||||||
|
46
lib/controllers/food_data.dart
Normal file
46
lib/controllers/food_data.dart
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import 'package:dietary_guard/models/food_data.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
class FoodDataController extends GetxController {
|
||||||
|
RxBool isReady = false.obs;
|
||||||
|
|
||||||
|
late final SharedPreferences _prefs;
|
||||||
|
|
||||||
|
Future<void> initialize(BuildContext context) async {
|
||||||
|
if (isReady.value) return;
|
||||||
|
|
||||||
|
_prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
isReady.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? getApiKey() {
|
||||||
|
return _prefs.getString("data_fdc_api_key");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setApiKey(String value) async {
|
||||||
|
await _prefs.setString("data_fdc_api_key", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<FoodDataQueryResponse> searchFood(String probe) async {
|
||||||
|
final client = Dio();
|
||||||
|
final resp = await client.get(
|
||||||
|
'https://api.nal.usda.gov/fdc/v1/foods/search',
|
||||||
|
queryParameters: {
|
||||||
|
'query': probe,
|
||||||
|
'dataType': 'Foundation',
|
||||||
|
'pageSize': 25,
|
||||||
|
'pageNumber': 1,
|
||||||
|
'sortBy': 'dataType.keyword',
|
||||||
|
'sortOrder': 'asc',
|
||||||
|
'api_key': getApiKey(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final result = FoodDataQueryResponse.fromJson(resp.data);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
|
import 'package:dietary_guard/controllers/food_data.dart';
|
||||||
import 'package:dietary_guard/screens/query.dart';
|
import 'package:dietary_guard/screens/query.dart';
|
||||||
import 'package:dietary_guard/screens/settings.dart';
|
import 'package:dietary_guard/screens/settings.dart';
|
||||||
import 'package:dietary_guard/shells/nav_shell.dart';
|
import 'package:dietary_guard/shells/nav_shell.dart';
|
||||||
|
import 'package:dietary_guard/translations.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
@ -42,9 +44,24 @@ class MyApp extends StatelessWidget {
|
|||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
colorScheme: ColorScheme.fromSeed(
|
colorScheme: ColorScheme.fromSeed(
|
||||||
seedColor: Colors.green,
|
seedColor: Colors.green,
|
||||||
|
brightness: Brightness.light,
|
||||||
),
|
),
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
),
|
),
|
||||||
|
darkTheme: ThemeData(
|
||||||
|
colorScheme: ColorScheme.fromSeed(
|
||||||
|
seedColor: Colors.green,
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
),
|
||||||
|
useMaterial3: true,
|
||||||
|
),
|
||||||
|
themeMode: ThemeMode.system,
|
||||||
|
translations: AppTranslations(),
|
||||||
|
onInit: () => _initializeProviders(context),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _initializeProviders(BuildContext context) async {
|
||||||
|
Get.put(FoodDataController());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
402
lib/models/food_data.dart
Normal file
402
lib/models/food_data.dart
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
class FoodDataQueryResponse {
|
||||||
|
int totalHits;
|
||||||
|
int currentPage;
|
||||||
|
int totalPages;
|
||||||
|
List<int> pageList;
|
||||||
|
FoodSearchCriteria foodSearchCriteria;
|
||||||
|
List<FoodData> foods;
|
||||||
|
Aggregations aggregations;
|
||||||
|
|
||||||
|
FoodDataQueryResponse({
|
||||||
|
required this.totalHits,
|
||||||
|
required this.currentPage,
|
||||||
|
required this.totalPages,
|
||||||
|
required this.pageList,
|
||||||
|
required this.foodSearchCriteria,
|
||||||
|
required this.foods,
|
||||||
|
required this.aggregations,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory FoodDataQueryResponse.fromJson(Map<String, dynamic> json) =>
|
||||||
|
FoodDataQueryResponse(
|
||||||
|
totalHits: json["totalHits"],
|
||||||
|
currentPage: json["currentPage"],
|
||||||
|
totalPages: json["totalPages"],
|
||||||
|
pageList: List<int>.from(json["pageList"].map((x) => x)),
|
||||||
|
foodSearchCriteria:
|
||||||
|
FoodSearchCriteria.fromJson(json["foodSearchCriteria"]),
|
||||||
|
foods:
|
||||||
|
List<FoodData>.from(json["foods"].map((x) => FoodData.fromJson(x))),
|
||||||
|
aggregations: Aggregations.fromJson(json["aggregations"]),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"totalHits": totalHits,
|
||||||
|
"currentPage": currentPage,
|
||||||
|
"totalPages": totalPages,
|
||||||
|
"pageList": List<dynamic>.from(pageList.map((x) => x)),
|
||||||
|
"foodSearchCriteria": foodSearchCriteria.toJson(),
|
||||||
|
"foods": List<dynamic>.from(foods.map((x) => x.toJson())),
|
||||||
|
"aggregations": aggregations.toJson(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class Aggregations {
|
||||||
|
DataType dataType;
|
||||||
|
Nutrients nutrients;
|
||||||
|
|
||||||
|
Aggregations({
|
||||||
|
required this.dataType,
|
||||||
|
required this.nutrients,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Aggregations.fromJson(Map<String, dynamic> json) => Aggregations(
|
||||||
|
dataType: DataType.fromJson(json["dataType"]),
|
||||||
|
nutrients: Nutrients.fromJson(json["nutrients"]),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"dataType": dataType.toJson(),
|
||||||
|
"nutrients": nutrients.toJson(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class DataType {
|
||||||
|
int branded;
|
||||||
|
int surveyFndds;
|
||||||
|
int srLegacy;
|
||||||
|
int foundation;
|
||||||
|
|
||||||
|
DataType({
|
||||||
|
required this.branded,
|
||||||
|
required this.surveyFndds,
|
||||||
|
required this.srLegacy,
|
||||||
|
required this.foundation,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory DataType.fromJson(Map<String, dynamic> json) => DataType(
|
||||||
|
branded: json["Branded"],
|
||||||
|
surveyFndds: json["Survey (FNDDS)"],
|
||||||
|
srLegacy: json["SR Legacy"],
|
||||||
|
foundation: json["Foundation"],
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"Branded": branded,
|
||||||
|
"Survey (FNDDS)": surveyFndds,
|
||||||
|
"SR Legacy": srLegacy,
|
||||||
|
"Foundation": foundation,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class Nutrients {
|
||||||
|
Nutrients();
|
||||||
|
|
||||||
|
factory Nutrients.fromJson(Map<String, dynamic> json) => Nutrients();
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
class FoodSearchCriteria {
|
||||||
|
List<Type> dataType;
|
||||||
|
String query;
|
||||||
|
String generalSearchInput;
|
||||||
|
int pageNumber;
|
||||||
|
String sortBy;
|
||||||
|
String sortOrder;
|
||||||
|
int numberOfResultsPerPage;
|
||||||
|
int pageSize;
|
||||||
|
bool requireAllWords;
|
||||||
|
List<Type> foodTypes;
|
||||||
|
|
||||||
|
FoodSearchCriteria({
|
||||||
|
required this.dataType,
|
||||||
|
required this.query,
|
||||||
|
required this.generalSearchInput,
|
||||||
|
required this.pageNumber,
|
||||||
|
required this.sortBy,
|
||||||
|
required this.sortOrder,
|
||||||
|
required this.numberOfResultsPerPage,
|
||||||
|
required this.pageSize,
|
||||||
|
required this.requireAllWords,
|
||||||
|
required this.foodTypes,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory FoodSearchCriteria.fromJson(Map<String, dynamic> json) =>
|
||||||
|
FoodSearchCriteria(
|
||||||
|
dataType:
|
||||||
|
List<Type>.from(json["dataType"].map((x) => typeValues.map[x]!)),
|
||||||
|
query: json["query"],
|
||||||
|
generalSearchInput: json["generalSearchInput"],
|
||||||
|
pageNumber: json["pageNumber"],
|
||||||
|
sortBy: json["sortBy"],
|
||||||
|
sortOrder: json["sortOrder"],
|
||||||
|
numberOfResultsPerPage: json["numberOfResultsPerPage"],
|
||||||
|
pageSize: json["pageSize"],
|
||||||
|
requireAllWords: json["requireAllWords"],
|
||||||
|
foodTypes:
|
||||||
|
List<Type>.from(json["foodTypes"].map((x) => typeValues.map[x]!)),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"dataType":
|
||||||
|
List<dynamic>.from(dataType.map((x) => typeValues.reverse[x])),
|
||||||
|
"query": query,
|
||||||
|
"generalSearchInput": generalSearchInput,
|
||||||
|
"pageNumber": pageNumber,
|
||||||
|
"sortBy": sortBy,
|
||||||
|
"sortOrder": sortOrder,
|
||||||
|
"numberOfResultsPerPage": numberOfResultsPerPage,
|
||||||
|
"pageSize": pageSize,
|
||||||
|
"requireAllWords": requireAllWords,
|
||||||
|
"foodTypes":
|
||||||
|
List<dynamic>.from(foodTypes.map((x) => typeValues.reverse[x])),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Type { FOUNDATION, SR_LEGACY }
|
||||||
|
|
||||||
|
final typeValues =
|
||||||
|
EnumValues({"Foundation": Type.FOUNDATION, "SR Legacy": Type.SR_LEGACY});
|
||||||
|
|
||||||
|
class FoodData {
|
||||||
|
int fdcId;
|
||||||
|
String description;
|
||||||
|
String commonNames;
|
||||||
|
String additionalDescriptions;
|
||||||
|
Type dataType;
|
||||||
|
int ndbNumber;
|
||||||
|
DateTime publishedDate;
|
||||||
|
FoodCategory? foodCategory;
|
||||||
|
DateTime? mostRecentAcquisitionDate;
|
||||||
|
String allHighlightFields;
|
||||||
|
double score;
|
||||||
|
List<dynamic> microbes;
|
||||||
|
List<FoodNutrient> foodNutrients;
|
||||||
|
List<dynamic> finalFoodInputFoods;
|
||||||
|
List<dynamic> foodMeasures;
|
||||||
|
List<dynamic> foodAttributes;
|
||||||
|
List<dynamic> foodAttributeTypes;
|
||||||
|
List<dynamic> foodVersionIds;
|
||||||
|
|
||||||
|
FoodData({
|
||||||
|
required this.fdcId,
|
||||||
|
required this.description,
|
||||||
|
required this.commonNames,
|
||||||
|
required this.additionalDescriptions,
|
||||||
|
required this.dataType,
|
||||||
|
required this.ndbNumber,
|
||||||
|
required this.publishedDate,
|
||||||
|
required this.foodCategory,
|
||||||
|
this.mostRecentAcquisitionDate,
|
||||||
|
required this.allHighlightFields,
|
||||||
|
required this.score,
|
||||||
|
required this.microbes,
|
||||||
|
required this.foodNutrients,
|
||||||
|
required this.finalFoodInputFoods,
|
||||||
|
required this.foodMeasures,
|
||||||
|
required this.foodAttributes,
|
||||||
|
required this.foodAttributeTypes,
|
||||||
|
required this.foodVersionIds,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory FoodData.fromJson(Map<String, dynamic> json) => FoodData(
|
||||||
|
fdcId: json["fdcId"],
|
||||||
|
description: json["description"],
|
||||||
|
commonNames: json["commonNames"],
|
||||||
|
additionalDescriptions: json["additionalDescriptions"],
|
||||||
|
dataType: typeValues.map[json["dataType"]]!,
|
||||||
|
ndbNumber: json["ndbNumber"],
|
||||||
|
publishedDate: DateTime.parse(json["publishedDate"]),
|
||||||
|
foodCategory: foodCategoryValues.map[json["foodCategory"]],
|
||||||
|
mostRecentAcquisitionDate: json["mostRecentAcquisitionDate"] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json["mostRecentAcquisitionDate"]),
|
||||||
|
allHighlightFields: json["allHighlightFields"],
|
||||||
|
score: json["score"]?.toDouble(),
|
||||||
|
microbes: List<dynamic>.from(json["microbes"].map((x) => x)),
|
||||||
|
foodNutrients: List<FoodNutrient>.from(
|
||||||
|
json["foodNutrients"].map((x) => FoodNutrient.fromJson(x))),
|
||||||
|
finalFoodInputFoods:
|
||||||
|
List<dynamic>.from(json["finalFoodInputFoods"].map((x) => x)),
|
||||||
|
foodMeasures: List<dynamic>.from(json["foodMeasures"].map((x) => x)),
|
||||||
|
foodAttributes:
|
||||||
|
List<dynamic>.from(json["foodAttributes"].map((x) => x)),
|
||||||
|
foodAttributeTypes:
|
||||||
|
List<dynamic>.from(json["foodAttributeTypes"].map((x) => x)),
|
||||||
|
foodVersionIds:
|
||||||
|
List<dynamic>.from(json["foodVersionIds"].map((x) => x)),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"fdcId": fdcId,
|
||||||
|
"description": description,
|
||||||
|
"commonNames": commonNames,
|
||||||
|
"additionalDescriptions": additionalDescriptions,
|
||||||
|
"dataType": typeValues.reverse[dataType],
|
||||||
|
"ndbNumber": ndbNumber,
|
||||||
|
"publishedDate":
|
||||||
|
"${publishedDate.year.toString().padLeft(4, '0')}-${publishedDate.month.toString().padLeft(2, '0')}-${publishedDate.day.toString().padLeft(2, '0')}",
|
||||||
|
"foodCategory": foodCategoryValues.reverse[foodCategory],
|
||||||
|
"mostRecentAcquisitionDate":
|
||||||
|
"${mostRecentAcquisitionDate!.year.toString().padLeft(4, '0')}-${mostRecentAcquisitionDate!.month.toString().padLeft(2, '0')}-${mostRecentAcquisitionDate!.day.toString().padLeft(2, '0')}",
|
||||||
|
"allHighlightFields": allHighlightFields,
|
||||||
|
"score": score,
|
||||||
|
"microbes": List<dynamic>.from(microbes.map((x) => x)),
|
||||||
|
"foodNutrients":
|
||||||
|
List<dynamic>.from(foodNutrients.map((x) => x.toJson())),
|
||||||
|
"finalFoodInputFoods":
|
||||||
|
List<dynamic>.from(finalFoodInputFoods.map((x) => x)),
|
||||||
|
"foodMeasures": List<dynamic>.from(foodMeasures.map((x) => x)),
|
||||||
|
"foodAttributes": List<dynamic>.from(foodAttributes.map((x) => x)),
|
||||||
|
"foodAttributeTypes":
|
||||||
|
List<dynamic>.from(foodAttributeTypes.map((x) => x)),
|
||||||
|
"foodVersionIds": List<dynamic>.from(foodVersionIds.map((x) => x)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FoodCategory { DAIRY_AND_EGG_PRODUCTS }
|
||||||
|
|
||||||
|
final foodCategoryValues =
|
||||||
|
EnumValues({"Dairy and Egg Products": FoodCategory.DAIRY_AND_EGG_PRODUCTS});
|
||||||
|
|
||||||
|
class FoodNutrient {
|
||||||
|
int nutrientId;
|
||||||
|
String nutrientName;
|
||||||
|
String nutrientNumber;
|
||||||
|
UnitName unitName;
|
||||||
|
DerivationCode? derivationCode;
|
||||||
|
String? derivationDescription;
|
||||||
|
int? derivationId;
|
||||||
|
double? value;
|
||||||
|
int? foodNutrientSourceId;
|
||||||
|
String? foodNutrientSourceCode;
|
||||||
|
FoodNutrientSourceDescription? foodNutrientSourceDescription;
|
||||||
|
int rank;
|
||||||
|
int indentLevel;
|
||||||
|
int foodNutrientId;
|
||||||
|
int? dataPoints;
|
||||||
|
double? min;
|
||||||
|
double? max;
|
||||||
|
double? median;
|
||||||
|
|
||||||
|
FoodNutrient({
|
||||||
|
required this.nutrientId,
|
||||||
|
required this.nutrientName,
|
||||||
|
required this.nutrientNumber,
|
||||||
|
required this.unitName,
|
||||||
|
this.derivationCode,
|
||||||
|
this.derivationDescription,
|
||||||
|
this.derivationId,
|
||||||
|
this.value,
|
||||||
|
this.foodNutrientSourceId,
|
||||||
|
this.foodNutrientSourceCode,
|
||||||
|
this.foodNutrientSourceDescription,
|
||||||
|
required this.rank,
|
||||||
|
required this.indentLevel,
|
||||||
|
required this.foodNutrientId,
|
||||||
|
this.dataPoints,
|
||||||
|
this.min,
|
||||||
|
this.max,
|
||||||
|
this.median,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory FoodNutrient.fromJson(Map<String, dynamic> json) => FoodNutrient(
|
||||||
|
nutrientId: json["nutrientId"],
|
||||||
|
nutrientName: json["nutrientName"],
|
||||||
|
nutrientNumber: json["nutrientNumber"],
|
||||||
|
unitName: unitNameValues.map[json["unitName"]]!,
|
||||||
|
derivationCode: derivationCodeValues.map[json["derivationCode"]]!,
|
||||||
|
derivationDescription: json["derivationDescription"],
|
||||||
|
derivationId: json["derivationId"],
|
||||||
|
value: json["value"]?.toDouble(),
|
||||||
|
foodNutrientSourceId: json["foodNutrientSourceId"],
|
||||||
|
foodNutrientSourceCode: json["foodNutrientSourceCode"],
|
||||||
|
foodNutrientSourceDescription: foodNutrientSourceDescriptionValues
|
||||||
|
.map[json["foodNutrientSourceDescription"]]!,
|
||||||
|
rank: json["rank"],
|
||||||
|
indentLevel: json["indentLevel"],
|
||||||
|
foodNutrientId: json["foodNutrientId"],
|
||||||
|
dataPoints: json["dataPoints"],
|
||||||
|
min: json["min"]?.toDouble(),
|
||||||
|
max: json["max"]?.toDouble(),
|
||||||
|
median: json["median"]?.toDouble(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"nutrientId": nutrientId,
|
||||||
|
"nutrientName": nutrientName,
|
||||||
|
"nutrientNumber": nutrientNumber,
|
||||||
|
"unitName": unitNameValues.reverse[unitName],
|
||||||
|
"derivationCode": derivationCodeValues.reverse[derivationCode],
|
||||||
|
"derivationDescription": derivationDescription,
|
||||||
|
"derivationId": derivationId,
|
||||||
|
"value": value,
|
||||||
|
"foodNutrientSourceId": foodNutrientSourceId,
|
||||||
|
"foodNutrientSourceCode": foodNutrientSourceCode,
|
||||||
|
"foodNutrientSourceDescription": foodNutrientSourceDescriptionValues
|
||||||
|
.reverse[foodNutrientSourceDescription],
|
||||||
|
"rank": rank,
|
||||||
|
"indentLevel": indentLevel,
|
||||||
|
"foodNutrientId": foodNutrientId,
|
||||||
|
"dataPoints": dataPoints,
|
||||||
|
"min": min,
|
||||||
|
"max": max,
|
||||||
|
"median": median,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DerivationCode { A, AS, BFFN, BFNN, BFZN, CAZN, LC, NC, NR, T, Z }
|
||||||
|
|
||||||
|
final derivationCodeValues = EnumValues({
|
||||||
|
"A": DerivationCode.A,
|
||||||
|
"AS": DerivationCode.AS,
|
||||||
|
"BFFN": DerivationCode.BFFN,
|
||||||
|
"BFNN": DerivationCode.BFNN,
|
||||||
|
"BFZN": DerivationCode.BFZN,
|
||||||
|
"CAZN": DerivationCode.CAZN,
|
||||||
|
"LC": DerivationCode.LC,
|
||||||
|
"NC": DerivationCode.NC,
|
||||||
|
"NR": DerivationCode.NR,
|
||||||
|
"T": DerivationCode.T,
|
||||||
|
"Z": DerivationCode.Z
|
||||||
|
});
|
||||||
|
|
||||||
|
enum FoodNutrientSourceDescription {
|
||||||
|
ANALYTICAL_OR_DERIVED_FROM_ANALYTICAL,
|
||||||
|
ASSUMED_ZERO,
|
||||||
|
CALCULATED_FROM_NUTRIENT_LABEL_BY_NDL,
|
||||||
|
CALCULATED_OR_IMPUTED
|
||||||
|
}
|
||||||
|
|
||||||
|
final foodNutrientSourceDescriptionValues = EnumValues({
|
||||||
|
"Analytical or derived from analytical":
|
||||||
|
FoodNutrientSourceDescription.ANALYTICAL_OR_DERIVED_FROM_ANALYTICAL,
|
||||||
|
"Assumed zero": FoodNutrientSourceDescription.ASSUMED_ZERO,
|
||||||
|
"Calculated from nutrient label by NDL":
|
||||||
|
FoodNutrientSourceDescription.CALCULATED_FROM_NUTRIENT_LABEL_BY_NDL,
|
||||||
|
"Calculated or imputed": FoodNutrientSourceDescription.CALCULATED_OR_IMPUTED
|
||||||
|
});
|
||||||
|
|
||||||
|
enum UnitName { G, IU, KCAL, K_J, MG, UG }
|
||||||
|
|
||||||
|
final unitNameValues = EnumValues({
|
||||||
|
"G": UnitName.G,
|
||||||
|
"IU": UnitName.IU,
|
||||||
|
"KCAL": UnitName.KCAL,
|
||||||
|
"kJ": UnitName.K_J,
|
||||||
|
"MG": UnitName.MG,
|
||||||
|
"UG": UnitName.UG
|
||||||
|
});
|
||||||
|
|
||||||
|
class EnumValues<T> {
|
||||||
|
Map<String, T> map;
|
||||||
|
late Map<T, String> reverseMap;
|
||||||
|
|
||||||
|
EnumValues(this.map);
|
||||||
|
|
||||||
|
Map<T, String> get reverse {
|
||||||
|
reverseMap = map.map((k, v) => MapEntry(v, k));
|
||||||
|
return reverseMap;
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,84 @@
|
|||||||
|
import 'package:dietary_guard/controllers/food_data.dart';
|
||||||
|
import 'package:dietary_guard/models/food_data.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
class QueryScreen extends StatelessWidget {
|
class QueryScreen extends StatefulWidget {
|
||||||
const QueryScreen({super.key});
|
const QueryScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<QueryScreen> createState() => _QueryScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _QueryScreenState extends State<QueryScreen> {
|
||||||
|
bool _isLoading = false;
|
||||||
|
|
||||||
|
int _totalCount = 0;
|
||||||
|
List<FoodData> _foodData = List.empty();
|
||||||
|
|
||||||
|
Future<void> _searchFood(String probe) async {
|
||||||
|
if (_isLoading) return;
|
||||||
|
|
||||||
|
setState(() => _isLoading = true);
|
||||||
|
|
||||||
|
final FoodDataController data = Get.find();
|
||||||
|
if (data.getApiKey() == null) return;
|
||||||
|
|
||||||
|
final result = await data.searchFood(probe);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_totalCount = result.totalHits;
|
||||||
|
_foodData = result.foods;
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
final FoodDataController data = Get.find();
|
||||||
|
data.initialize(context);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return const SizedBox();
|
return SafeArea(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SearchBar(
|
||||||
|
padding: const WidgetStatePropertyAll<EdgeInsets>(
|
||||||
|
EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
|
),
|
||||||
|
onSubmitted: (value) {
|
||||||
|
_searchFood(value);
|
||||||
|
},
|
||||||
|
leading: const Icon(Icons.search),
|
||||||
|
).paddingSymmetric(horizontal: 24),
|
||||||
|
if (_isLoading)
|
||||||
|
const SizedBox(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
child: CircularProgressIndicator(strokeWidth: 3),
|
||||||
|
).paddingSymmetric(vertical: 16)
|
||||||
|
else
|
||||||
|
Expanded(
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: _foodData.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = _foodData[index];
|
||||||
|
return ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
title: Text(item.description),
|
||||||
|
subtitle: Text(
|
||||||
|
DateFormat("yyyy-MM-dd").format(item.publishedDate),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
).paddingOnly(top: 8),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,70 @@
|
|||||||
|
import 'package:dietary_guard/controllers/food_data.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
class SettingsScreen extends StatelessWidget {
|
class SettingsScreen extends StatefulWidget {
|
||||||
const SettingsScreen({super.key});
|
const SettingsScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SettingsScreen> createState() => _SettingsScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
|
final TextEditingController _fdcApiKeyController = TextEditingController();
|
||||||
|
|
||||||
|
Future<void> _applySettings() async {
|
||||||
|
final FoodDataController data = Get.find();
|
||||||
|
await data.setApiKey(_fdcApiKeyController.text);
|
||||||
|
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
|
content: Text('settingsApplied'.tr),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
final FoodDataController data = Get.find();
|
||||||
|
_fdcApiKeyController.text = data.getApiKey() ?? '';
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_fdcApiKeyController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return const Placeholder();
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('settings'.tr),
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextField(
|
||||||
|
controller: _fdcApiKeyController,
|
||||||
|
obscureText: true,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
label: Text("FDC API Key"),
|
||||||
|
isDense: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
ElevatedButton.icon(
|
||||||
|
icon: const Icon(Icons.save, size: 16),
|
||||||
|
label: Text('apply'.tr),
|
||||||
|
onPressed: () => _applySettings(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetric(vertical: 12),
|
||||||
|
],
|
||||||
|
).paddingSymmetric(horizontal: 24),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
class Destination {
|
class Destination {
|
||||||
@ -21,8 +22,8 @@ class _NavShellState extends State<NavShell> {
|
|||||||
int _focusDestination = 0;
|
int _focusDestination = 0;
|
||||||
|
|
||||||
final List<Destination> _allDestinations = <Destination>[
|
final List<Destination> _allDestinations = <Destination>[
|
||||||
const Destination('Query', 'query', Icons.search),
|
Destination('query'.tr, 'query', Icons.search),
|
||||||
const Destination('Settings', 'settings', Icons.settings)
|
Destination('settings'.tr, 'settings', Icons.settings)
|
||||||
];
|
];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
11
lib/translations.dart
Normal file
11
lib/translations.dart
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import 'package:dietary_guard/translations/en_us.dart';
|
||||||
|
import 'package:dietary_guard/translations/zh_cn.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class AppTranslations extends Translations {
|
||||||
|
@override
|
||||||
|
Map<String, Map<String, String>> get keys => {
|
||||||
|
'en_US': i18nEnglish,
|
||||||
|
'zh_CN': i18nSimplifiedChinese,
|
||||||
|
};
|
||||||
|
}
|
8
lib/translations/en_us.dart
Normal file
8
lib/translations/en_us.dart
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
const i18nEnglish = {
|
||||||
|
'query': 'Query',
|
||||||
|
'settings': 'Settings',
|
||||||
|
'preparingData': 'Preparing data...',
|
||||||
|
'settingsApplied': 'Settings Applied',
|
||||||
|
'apply': 'Apply',
|
||||||
|
'searchHistoryNotIncluded': 'Search History not Included, yet',
|
||||||
|
};
|
8
lib/translations/zh_cn.dart
Normal file
8
lib/translations/zh_cn.dart
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
const i18nSimplifiedChinese = {
|
||||||
|
'query': '查询',
|
||||||
|
'settings': '设置',
|
||||||
|
'preparingData': '准备数据中…',
|
||||||
|
'settingsApplied': '设置已应用',
|
||||||
|
'apply': '应用',
|
||||||
|
'searchHistoryNotIncluded': '搜索记录还没实现',
|
||||||
|
};
|
@ -6,7 +6,9 @@ import FlutterMacOS
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
|
import sqflite
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
|
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||||
}
|
}
|
||||||
|
64
pubspec.lock
64
pubspec.lock
@ -49,6 +49,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.8"
|
version: "1.0.8"
|
||||||
|
dio:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: dio
|
||||||
|
sha256: "0dfb6b6a1979dac1c1245e17cef824d7b452ea29bd33d3467269f9bef3715fb0"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.6.0"
|
||||||
|
dio_web_adapter:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dio_web_adapter
|
||||||
|
sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -112,6 +128,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.2.3"
|
version: "14.2.3"
|
||||||
|
http_parser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_parser
|
||||||
|
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.2"
|
||||||
|
intl:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: intl
|
||||||
|
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.19.0"
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -293,6 +325,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0"
|
version: "1.10.0"
|
||||||
|
sqflite:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: sqflite
|
||||||
|
sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.3+1"
|
||||||
|
sqflite_common:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_common
|
||||||
|
sha256: c5e5b2a142a893a752cb36ae5888680248686725a54afceff31f9a3a76bc53c2
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.5.4+1"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -317,6 +365,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.0"
|
||||||
|
synchronized:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: synchronized
|
||||||
|
sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0+1"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -333,6 +389,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.2"
|
version: "0.7.2"
|
||||||
|
typed_data:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: typed_data
|
||||||
|
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.2"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -38,6 +38,9 @@ dependencies:
|
|||||||
get: ^4.6.6
|
get: ^4.6.6
|
||||||
go_router: ^14.2.3
|
go_router: ^14.2.3
|
||||||
shared_preferences: ^2.3.1
|
shared_preferences: ^2.3.1
|
||||||
|
sqflite: ^2.3.3+1
|
||||||
|
dio: ^5.6.0
|
||||||
|
intl: ^0.19.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@ -62,8 +65,8 @@ flutter:
|
|||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
assets:
|
# assets:
|
||||||
- assets/data/
|
# - assets/data/
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
# https://flutter.dev/to/resolution-aware-images
|
# https://flutter.dev/to/resolution-aware-images
|
||||||
|
Loading…
Reference in New Issue
Block a user