Compare commits

...

10 commits

94 changed files with 2584 additions and 939 deletions

View file

@ -1,32 +1,7 @@
# BUILD
FROM debian:stable-slim AS build-env
RUN apt-get update && apt-get install -yq curl file git unzip xz-utils zip && rm -rf /var/lib/apt/lists/*
RUN useradd -m flutter
RUN groupadd flutterusers
RUN usermod -aG flutterusers flutter
RUN mkdir /opt/flutter && chown -R flutter:flutter /opt/flutter
USER flutter
WORKDIR /home/flutter
RUN git clone https://github.com/flutter/flutter.git /opt/flutter
ENV PATH $PATH:/opt/flutter/bin
RUN flutter config --no-analytics --enable-web --no-enable-android --no-enable-ios
RUN flutter precache --web
RUN flutter create --platforms web dummy && rm -rf dummy
COPY . /home/flutter
USER root
RUN chown -R flutter:flutter /home/flutter
USER flutter
WORKDIR /home/flutter
RUN flutter build web
# DEPLOY
FROM node:16.14-alpine
COPY --from=build-env /home/flutter/build/web /app/
COPY ./build/web /app
WORKDIR /app/

17
Makefile Normal file
View file

@ -0,0 +1,17 @@
VERSION=0.`git rev-list --count HEAD`
.PHONY: black
DOCKER_BUILD=docker build
ifeq ($(shell uname -m), arm64)
DOCKER_BUILD=docker buildx build --platform linux/amd64
endif
.PHONY: build
build:
flutter build web
$(DOCKER_BUILD) -t registry.domandoman.xyz/fooder/app -f Dockerfile .
.PHONY: push
push:
docker push registry.domandoman.xyz/fooder/app

View file

@ -1,3 +1,3 @@
# Fooder web app
Very simple diary project that uses [fooder_api](https://github.com/ickyicky/fooder-api).
Very simple diary project that uses [fooder_api](https://gitea.domandoman.xyz/doman/fooder-api).

69
altstore/source.json Normal file
View file

@ -0,0 +1,69 @@
{
"name": "Doman",
"subtitle": "Doman source!",
"description": "For now it's just the fooder app",
"iconURL": "https://fooder.domandoman.xyz/assets/logo.png",
"headerURL": "https://fooder.domandoman.xyz/assets/logo.png",
"website": "https://fooder.domandoman.xyz",
"tintColor": "#FF8B9DC3",
"featuredApps": [
"com.example.fooderWeb",
],
"apps": [
{
"name": "Fooder",
"bundleIdentifier": "com.example.fooderWeb",
"developerName": "Doman",
"subtitle": "Count your calories",
"localizedDescription": "Awesome app to count your calories",
"iconURL": "https://fooder.domandoman.xyz/assets/logo.png",
"tintColor": "#FF8B9DC3",
"screenshots": [
"https://fooder.domandoman.xyz/assets/screenshot1.png",
"https://fooder.domandoman.xyz/assets/screenshot2.png",
],
"versions": [
{
"version": "1.0.0+1",
"date": "2024-05-10T07:00:00-08:00",
"size": 10000000,
"downloadURL": "https://example.com/myapp_v1.0.ipa",
"localizedDescription": "Initial release."
}
],
"appPermissions": {
"entitlements": [
"com.apple.security.application-groups",
"com.apple.developer.siri"
],
"privacy": {
"NSPhotoLibraryUsageDescription": "App saves photos to your Photo Library.",
"NSCameraUsageDescription": "App uses camera to take photos.",
"NSMicrophoneUsageDescription": "App uses microphone to record video."
}
}
},
],
"news": [
{
"title": "New Feature Announcement",
"identifier": "new_feature",
"caption": "Introducing a new feature in AltStore!",
"date": "2023-03-15",
"tintColor": "#5CA399",
"imageURL": "https://example.com/new_feature_image.png",
"notify": true,
"url": "https://example.com/new_feature_details"
},
{
"title": "App Update Available",
"identifier": "app_update",
"caption": "An update is available for My Example App.",
"date": "2023-03-10T10:00:00-07:00",
"tintColor": "#3660A2",
"imageURL": "https://example.com/app_update_image.png",
"notify": false,
"appID": "com.example.myapp"
}
]
}

View file

@ -1,8 +1,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />
<application
android:label="fooder_web"
android:label="Fooder"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
android:icon="@mipmap/launcher_icon">
<activity
android:name=".MainActivity"
android:exported="true"

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
<string>12.0</string>
</dict>
</plist>

View file

@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
# platform :ios, '12.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

41
ios/Podfile.lock Normal file
View file

@ -0,0 +1,41 @@
PODS:
- Flutter (1.0.0)
- flutter_barcode_scanner (2.0.0):
- Flutter
- flutter_secure_storage (6.0.0):
- Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- permission_handler_apple (9.3.0):
- Flutter
DEPENDENCIES:
- Flutter (from `Flutter`)
- flutter_barcode_scanner (from `.symlinks/plugins/flutter_barcode_scanner/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
EXTERNAL SOURCES:
Flutter:
:path: Flutter
flutter_barcode_scanner:
:path: ".symlinks/plugins/flutter_barcode_scanner/ios"
flutter_secure_storage:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
SPEC CHECKSUMS:
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_barcode_scanner: 7a1144744c28dc0c57a8de7218ffe5ec59a9e4bf
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796
COCOAPODS: 1.15.2

View file

@ -8,12 +8,14 @@
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
29C8E8B714ED94E557D46BFB /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5E968578F4C69854CF864C0 /* Pods_Runner.framework */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
E7AAC4F7DD7928627B2E33CF /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C30D6B3DD05EBC43545DEB8A /* Pods_RunnerTests.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -40,11 +42,16 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
0F1A42FF524A7E0FAE56F82F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
17C2E4C7353DD17838B2B616 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
76F6F6B11AE95BFC7130779E /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
@ -53,21 +60,64 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B51F7AB59BCC9B009252D2EA /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
C30D6B3DD05EBC43545DEB8A /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CE784006A2AE02B13BFDBF63 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
F084D738B0611C829C6F2BBF /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
F5E968578F4C69854CF864C0 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
08A8547522D611B14F2528AB /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
E7AAC4F7DD7928627B2E33CF /* Pods_RunnerTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
29C8E8B714ED94E557D46BFB /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C807B294A618700263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
};
3B7F475446216C505E738298 /* Pods */ = {
isa = PBXGroup;
children = (
17C2E4C7353DD17838B2B616 /* Pods-Runner.debug.xcconfig */,
CE784006A2AE02B13BFDBF63 /* Pods-Runner.release.xcconfig */,
0F1A42FF524A7E0FAE56F82F /* Pods-Runner.profile.xcconfig */,
76F6F6B11AE95BFC7130779E /* Pods-RunnerTests.debug.xcconfig */,
F084D738B0611C829C6F2BBF /* Pods-RunnerTests.release.xcconfig */,
B51F7AB59BCC9B009252D2EA /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
74C2F6500218C186F355BF0F /* Frameworks */ = {
isa = PBXGroup;
children = (
F5E968578F4C69854CF864C0 /* Pods_Runner.framework */,
C30D6B3DD05EBC43545DEB8A /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
@ -79,14 +129,6 @@
name = Flutter;
sourceTree = "<group>";
};
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C807B294A618700263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
@ -94,6 +136,8 @@
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
3B7F475446216C505E738298 /* Pods */,
74C2F6500218C186F355BF0F /* Frameworks */,
);
sourceTree = "<group>";
};
@ -128,9 +172,10 @@
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
BD433A59F73EC51680792A1B /* [CP] Check Pods Manifest.lock */,
331C807D294A63A400263BE5 /* Sources */,
331C807E294A63A400263BE5 /* Frameworks */,
331C807F294A63A400263BE5 /* Resources */,
08A8547522D611B14F2528AB /* Frameworks */,
);
buildRules = (
);
@ -146,12 +191,15 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
009EAABCB5CD7F8DB81D5667 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
32E2CACC130A6F356B3006E8 /* [CP] Embed Pods Frameworks */,
2126B9461F451C94849B0573 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -168,7 +216,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C8080294A63A400263BE5 = {
@ -222,6 +270,62 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
009EAABCB5CD7F8DB81D5667 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
2126B9461F451C94849B0573 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
32E2CACC130A6F356B3006E8 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@ -253,6 +357,28 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
BD433A59F73EC51680792A1B /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -344,7 +470,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -360,6 +486,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = SPB5V9QPZG;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@ -376,7 +503,7 @@
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = AE0B7B92F70575B8D7E0D07E /* Pods-RunnerTests.debug.xcconfig */;
baseConfigurationReference = 76F6F6B11AE95BFC7130779E /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
@ -394,7 +521,7 @@
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 89B67EB44CE7B6631473024E /* Pods-RunnerTests.release.xcconfig */;
baseConfigurationReference = F084D738B0611C829C6F2BBF /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
@ -410,7 +537,7 @@
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 640959BDD8F10B91D80A66BE /* Pods-RunnerTests.profile.xcconfig */;
baseConfigurationReference = B51F7AB59BCC9B009252D2EA /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
@ -471,7 +598,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -520,7 +647,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -538,6 +665,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = SPB5V9QPZG;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@ -560,6 +688,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = SPB5V9QPZG;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View file

@ -4,4 +4,7 @@
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 674 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 964 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
@ -14,13 +16,14 @@
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="102" y="-34"/>
</scene>
</scenes>
</document>

View file

@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Fooder Web</string>
<string>Fooder</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@ -47,5 +47,9 @@
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>NSPhotoLibraryUsageDescription</key>
<string>App needs access to photo lib for profile images</string>
<key>NSCameraUsageDescription</key>
<string>To capture profile photo please grant camera access</string>
</dict>
</plist>

View file

@ -1,23 +1,33 @@
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:html';
import 'package:intl/intl.dart';
import 'package:fooder/models/meal.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class ApiClient {
final String baseUrl;
String? token;
String? refreshToken;
http.Client httpClient = http.Client();
final FlutterSecureStorage storage = const FlutterSecureStorage();
ApiClient({
required this.baseUrl,
}) {
if (window.localStorage.containsKey('token')) {
token = window.localStorage['token'];
() async {
await loadToken();
}();
}
Future<void> loadToken() async {
Map<String, String> allValues = await storage.readAll();
if (allValues.containsKey('token')) {
token = allValues['token'];
}
if (window.localStorage.containsKey('refreshToken')) {
refreshToken = window.localStorage['refreshToken'];
if (allValues.containsKey('refreshToken')) {
refreshToken = allValues['refreshToken'];
}
}
@ -162,11 +172,11 @@ class ApiClient {
final token = _jsonDecode(response)['access_token'];
this.token = token;
window.localStorage['token'] = token;
await storage.write(key: 'token', value: token);
final refreshToken = _jsonDecode(response)['refresh_token'];
this.refreshToken = refreshToken;
window.localStorage['refreshToken'] = refreshToken;
await storage.write(key: 'refreshToken', value: refreshToken);
}
Future<void> refresh() async {
@ -179,9 +189,10 @@ class ApiClient {
});
token = response['access_token'] as String;
window.localStorage['token'] = token!;
await storage.write(key: 'token', value: token);
refreshToken = response['refresh_token'] as String;
window.localStorage['refreshToken'] = refreshToken!;
await storage.write(key: 'refreshToken', value: refreshToken);
}
Future<Map<String, dynamic>> getDiary({required DateTime date}) async {
@ -193,11 +204,11 @@ class ApiClient {
return response;
}
void logout() {
Future<void> logout() async {
token = null;
refreshToken = null;
window.localStorage.remove('token');
window.localStorage.remove('refreshToken');
await storage.deleteAll();
}
Future<Map<String, dynamic>> getProducts(String q) async {
@ -206,6 +217,13 @@ class ApiClient {
return response;
}
Future<Map<String, dynamic>> getProductByBarcode(String barcode) async {
var response = await get("/product/by_barcode?${Uri(queryParameters: {
"barcode": barcode
}).query}");
return response;
}
Future<Map<String, dynamic>> getPresets(String? q) async {
var response = await get("/preset?${Uri(queryParameters: {"q": q}).query}");
return response;
@ -265,21 +283,20 @@ class ApiClient {
}
}
Future<void> addMeal(
{required String name, required int diaryId, required int order}) async {
Future<void> addMeal({required String name, required int diaryId}) async {
await post("/meal", {
"name": name,
"diary_id": diaryId,
"order": order,
});
}
Future<void> addMealFromPreset(
{required String name, required int diaryId, required int order, required int presetId}) async {
{required String name,
required int diaryId,
required int presetId}) async {
await post("/meal/from_preset", {
"name": name,
"diary_id": diaryId,
"order": order,
"preset_id": presetId,
});
}

View file

@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
class FAppBar extends StatelessWidget implements PreferredSizeWidget {
final List<Widget> actions;
const FAppBar({super.key, required this.actions});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: AppBar(
backgroundColor: Colors.transparent,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
elevation: 0,
actions: actions,
));
}
@override
Size get preferredSize => const Size.fromHeight(56);
}

View file

@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
import 'package:blur/blur.dart';
class BlurContainer extends StatelessWidget {
final Widget? child;
final double? height;
final double? width;
const BlurContainer({super.key, this.height, this.width, this.child});
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var colorScheme = theme.colorScheme;
var blured = Blur(
blur: 10,
blurColor: colorScheme.surface.withOpacity(0.1),
child: Container(
height: height,
width: width,
color: colorScheme.surface.withOpacity(0.1),
),
);
if (child == null) {
return blured;
}
return Stack(
children: [
blured,
child!,
],
);
}
}

View file

@ -0,0 +1,46 @@
import 'package:flutter/material.dart';
class FButton extends StatelessWidget {
final String labelText;
final double padding;
final double insidePadding;
final double fontSize;
final Function()? onPressed;
const FButton(
{super.key,
required this.labelText,
this.padding = 8,
this.insidePadding = 24,
this.fontSize = 20,
this.onPressed});
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var colorScheme = theme.colorScheme;
return GestureDetector(
onTap: onPressed,
child: Padding(
padding: EdgeInsets.symmetric(vertical: padding, horizontal: padding),
child: Container(
padding: EdgeInsets.symmetric(vertical: insidePadding),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: colorScheme.surfaceTint.withOpacity(0.85),
),
child: Center(
child: Text(
labelText,
style: theme.textTheme.labelLarge!.copyWith(
fontWeight: FontWeight.bold,
fontSize: fontSize,
color: colorScheme.onPrimary,
),
),
),
),
));
}
}

View file

@ -0,0 +1,154 @@
import 'package:flutter/material.dart';
class FDateItemWidget extends StatelessWidget {
final DateTime date;
final bool picked;
final Function(DateTime) onDatePicked;
const FDateItemWidget(
{super.key,
required this.date,
required this.onDatePicked,
this.picked = false});
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var colorScheme = theme.colorScheme;
var dayOfTheWeekMap = {
1: 'Mon',
2: 'Tue',
3: 'Wed',
4: 'Thu',
5: 'Fri',
6: 'Sat',
7: 'Sun',
};
return GestureDetector(
onTap: () {
onDatePicked(date);
},
child: Container(
width: picked ? 100 : 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
border: Border.all(
// color: picked ? colorScheme.onPrimary : Colors.transparent,
color: Colors.transparent,
width: 2,
),
color: picked
? colorScheme.onSurfaceVariant.withOpacity(0.25)
: Colors.transparent,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
dayOfTheWeekMap[date.weekday]!,
style: TextStyle(
color: colorScheme.onSurfaceVariant,
fontSize: picked ? 24 : 12,
fontWeight: FontWeight.bold,
),
),
Text(
'${date.day}.${date.month}',
style: TextStyle(
color: colorScheme.onSurfaceVariant,
fontSize: picked ? 24 : 12,
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}
}
class FDatePickerWidget extends StatefulWidget {
final DateTime date;
final Function(DateTime) onDatePicked;
const FDatePickerWidget(
{super.key, required this.date, required this.onDatePicked});
@override
State<FDatePickerWidget> createState() => _FDatePickerWidgetState();
}
class _FDatePickerWidgetState extends State<FDatePickerWidget> {
DateTime date = DateTime.now();
@override
void initState() {
super.initState();
setState(() {
date = widget.date;
});
}
Future<void> onDatePicked(DateTime date) async {
setState(() {
this.date = date;
});
await widget.onDatePicked(date);
}
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var colorScheme = theme.colorScheme;
return SizedBox(
height: 100,
child: Center(
child: ListView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 20),
shrinkWrap: true,
children: <Widget>[
FDateItemWidget(
date: date.add(const Duration(days: -2)),
onDatePicked: onDatePicked),
FDateItemWidget(
date: date.add(const Duration(days: -1)),
onDatePicked: onDatePicked),
FDateItemWidget(
date: date, onDatePicked: onDatePicked, picked: true),
FDateItemWidget(
date: date.add(const Duration(days: 1)),
onDatePicked: onDatePicked),
SizedBox(
width: 50,
child: IconButton(
icon: Icon(
Icons.calendar_month,
color: colorScheme.onSurfaceVariant,
size: 20,
),
onPressed: () {
// open date picker
showDatePicker(
context: context,
initialDate: date,
firstDate: date.add(const Duration(days: -365)),
lastDate: date.add(const Duration(days: 365)),
).then((value) {
if (value != null) {
onDatePicked(value);
}
});
},
),
),
],
),
),
);
}
}

View file

@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
class FDropdown<T> extends StatelessWidget {
final String labelText;
final List<DropdownMenuItem<T>> items;
final Function(T?) onChanged;
final T? value;
final double padding;
const FDropdown(
{super.key,
required this.labelText,
this.padding = 8,
required this.items,
required this.onChanged,
this.value});
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var colorScheme = theme.colorScheme;
return Padding(
padding: EdgeInsets.symmetric(vertical: padding, horizontal: padding),
child: DropdownButtonFormField<T>(
onChanged: onChanged,
items: items,
value: value,
decoration: InputDecoration(
labelText: labelText,
floatingLabelStyle:
theme.textTheme.bodyLarge!.copyWith(color: colorScheme.onSurface),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: colorScheme.primary),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: colorScheme.onPrimary),
),
),
),
);
}
}

View file

@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:fooder/components/blur_container.dart';
class FActionButton extends StatelessWidget {
final IconData icon;
final Function() onPressed;
final String tag;
const FActionButton(
{super.key,
required this.icon,
required this.onPressed,
this.tag = 'fap'});
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var colorScheme = theme.colorScheme;
return Container(
height: 64,
width: 64,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(32),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(32),
child: BlurContainer(
height: 64,
width: 64,
child: SizedBox(
height: 64,
width: 64,
child: FloatingActionButton(
elevation: 0,
onPressed: onPressed,
heroTag: tag,
backgroundColor: Colors.transparent,
child: Icon(
icon,
color: colorScheme.onSurface,
),
),
),
),
),
);
}
}

View file

@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:fooder/components/blur_container.dart';
class FNavBar extends StatelessWidget {
static const maxWidth = 920.0;
final List<Widget> children;
final double height;
const FNavBar({super.key, required this.children, this.height = 78});
@override
Widget build(BuildContext context) {
var widthAvail = MediaQuery.of(context).size.width;
// var width = widthAvail > maxWidth ? maxWidth : widthAvail;
return SizedBox(
width: widthAvail,
height: height * children.length,
child: BlurContainer(
width: widthAvail,
height: height * children.length,
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
...children,
Container(
height: height / 3,
color: Colors.transparent,
),
]),
),
);
}
}

159
lib/components/sliver.dart Normal file
View file

@ -0,0 +1,159 @@
import 'package:flutter/material.dart';
import 'package:fooder/components/blur_container.dart';
class ClipShadowPath extends StatelessWidget {
final Shadow shadow;
final CustomClipper<Path> clipper;
final Widget child;
const ClipShadowPath({
super.key,
required this.shadow,
required this.clipper,
required this.child,
});
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: _ClipShadowShadowPainter(
clipper: clipper,
shadow: shadow,
),
child: ClipPath(clipper: clipper, child: child),
);
}
}
class _ClipShadowShadowPainter extends CustomPainter {
final Shadow shadow;
final CustomClipper<Path> clipper;
_ClipShadowShadowPainter({required this.shadow, required this.clipper});
@override
void paint(Canvas canvas, Size size) {
var paint = shadow.toPaint();
var clipPath = clipper.getClip(size).shift(shadow.offset);
canvas.drawPath(clipPath, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
class BackgroundWave extends StatelessWidget {
final double height;
const BackgroundWave({super.key, required this.height});
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var colorScheme = theme.colorScheme;
return SizedBox(
height: height,
child: ClipPath(
clipper: BackgroundWaveClipper(),
child: BlurContainer(
width: MediaQuery.of(context).size.width,
height: height,
),
),
);
}
}
class BackgroundWaveClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
var path = Path();
path.lineTo(0.0, size.height);
var firstCurve = Offset(0, size.height - 20);
var lastCurve = Offset(40, size.height - 20);
path.quadraticBezierTo(
firstCurve.dx,
firstCurve.dy,
lastCurve.dx,
lastCurve.dy,
);
firstCurve = Offset(0, size.height - 20);
lastCurve = Offset(size.width - 40, size.height - 20);
path.quadraticBezierTo(
firstCurve.dx,
firstCurve.dy,
lastCurve.dx,
lastCurve.dy,
);
firstCurve = Offset(size.width, size.height - 20);
lastCurve = Offset(size.width, size.height);
path.quadraticBezierTo(
firstCurve.dx,
firstCurve.dy,
lastCurve.dx,
lastCurve.dy,
);
path.lineTo(size.width, 0.0);
path.close();
return path;
}
@override
bool shouldReclip(BackgroundWaveClipper oldClipper) => oldClipper != this;
}
class FSliverAppBar extends SliverPersistentHeaderDelegate {
final double preferredHeight;
final Widget child;
const FSliverAppBar({required this.child, this.preferredHeight = 220});
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
var adjustedShrinkOffset =
shrinkOffset > minExtent ? minExtent : shrinkOffset;
double offset = (minExtent - adjustedShrinkOffset);
if (offset < 4) {
offset = 4;
}
return Stack(
children: [
// const BackgroundWave(
// height: preferredHeight,
// ),
BlurContainer(
height: preferredHeight,
),
Positioned(
top: offset,
left: 16,
right: 16,
child: child,
)
],
);
}
@override
double get maxExtent => preferredHeight;
@override
double get minExtent => preferredHeight / 2;
@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) =>
oldDelegate.maxExtent != maxExtent || oldDelegate.minExtent != minExtent;
}

59
lib/components/text.dart Normal file
View file

@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class FTextInput extends StatelessWidget {
final String labelText;
final double padding;
final TextEditingController controller;
final List<String>? autofillHints;
final bool autofocus;
final bool obscureText;
final Function(String)? onFieldSubmitted;
final TextInputType? keyboardType;
final List<TextInputFormatter>? inputFormatters;
final Function(String)? onChanged;
const FTextInput(
{super.key,
required this.labelText,
this.padding = 8,
required this.controller,
this.autofillHints,
this.autofocus = false,
this.onFieldSubmitted,
this.obscureText = false,
this.keyboardType,
this.inputFormatters,
this.onChanged});
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var colorScheme = theme.colorScheme;
return Padding(
padding: EdgeInsets.symmetric(vertical: padding, horizontal: padding),
child: TextFormField(
obscureText: obscureText,
decoration: InputDecoration(
labelText: labelText,
floatingLabelStyle:
theme.textTheme.bodyLarge!.copyWith(color: colorScheme.onSurface),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: colorScheme.primary),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: colorScheme.onPrimary),
),
),
controller: controller,
autofillHints: autofillHints,
autofocus: autofocus,
onFieldSubmitted: onFieldSubmitted,
keyboardType: keyboardType,
inputFormatters: inputFormatters,
onChanged: onChanged,
),
);
}
}

View file

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:fooder/screens/login.dart';
import 'package:fooder/client.dart';
import 'package:fooder/theme.dart';
class MyApp extends StatelessWidget {
const MyApp({super.key});
@ -9,13 +10,10 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'FOODER',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blueGrey,
brightness: Brightness.dark,
),
useMaterial3: true,
),
theme: MainTheme.light(),
darkTheme: MainTheme.dark(),
themeMode: ThemeMode.system,
debugShowCheckedModeBanner: false,
home: LoginScreen(
apiClient: ApiClient(
baseUrl: 'https://fooderapi.domandoman.xyz/api',

View file

@ -5,7 +5,11 @@ import 'package:fooder/models/product.dart';
import 'package:fooder/models/diary.dart';
import 'package:fooder/models/meal.dart';
import 'package:fooder/widgets/product.dart';
import 'package:fooder/components/text.dart';
import 'package:fooder/components/dropdown.dart';
import 'package:fooder/components/floating_action_button.dart';
import 'package:fooder/screens/add_product.dart';
import 'package:simple_barcode_scanner/simple_barcode_scanner.dart';
class AddEntryScreen extends BasedScreen {
final Diary diary;
@ -17,7 +21,7 @@ class AddEntryScreen extends BasedScreen {
State<AddEntryScreen> createState() => _AddEntryScreen();
}
class _AddEntryScreen extends State<AddEntryScreen> {
class _AddEntryScreen extends BasedState<AddEntryScreen> {
final gramsController = TextEditingController();
final productNameController = TextEditingController();
Meal? meal;
@ -55,6 +59,7 @@ class _AddEntryScreen extends State<AddEntryScreen> {
});
}
@override
void showError(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
@ -96,19 +101,42 @@ class _AddEntryScreen extends State<AddEntryScreen> {
popMeDaddy();
}
Future<void> _findProductByBarCode() async {
var res = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SimpleBarcodeScannerPage(),
),
);
if (res is String) {
try {
var productMap = await widget.apiClient.getProductByBarcode(res);
var product = Product.fromJson(productMap);
setState(() {
products = [product];
productNameController.text = product.name;
});
} catch (e) {
showError("Product not found");
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text("🅵🅾🅾🅳🅴🆁", style: logoStyle(context)),
),
extendBodyBehindAppBar: true,
appBar: appBar(),
body: Center(
child: Container(
constraints: const BoxConstraints(maxWidth: 720),
padding: const EdgeInsets.all(10),
child: ListView(children: <Widget>[
DropdownButton<Meal>(
FDropdown<Meal>(
labelText: 'Meal',
value: meal,
// Callback that sets the selected popup menu item.
onChanged: (Meal? meal) {
@ -127,19 +155,15 @@ class _AddEntryScreen extends State<AddEntryScreen> {
),
],
),
TextFormField(
decoration: const InputDecoration(
labelText: 'Product name',
),
FTextInput(
labelText: 'Product name',
controller: productNameController,
onChanged: (_) => _getProducts(),
onFieldSubmitted: (_) => _addEntry(),
autofocus: true,
),
TextFormField(
decoration: const InputDecoration(
labelText: 'Grams',
),
FTextInput(
labelText: 'Grams',
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
inputFormatters: <TextInputFormatter>[
@ -185,9 +209,20 @@ class _AddEntryScreen extends State<AddEntryScreen> {
),
])),
),
floatingActionButton: FloatingActionButton(
onPressed: _addEntry,
child: const Icon(Icons.add),
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FActionButton(
onPressed: _findProductByBarCode,
icon: Icons.photo_camera,
),
const SizedBox(width: 10),
FActionButton(
onPressed: _addEntry,
icon: Icons.library_add,
tag: "fap2",
),
],
),
);
}

View file

@ -3,6 +3,8 @@ import 'package:fooder/screens/based.dart';
import 'package:fooder/models/diary.dart';
import 'package:fooder/models/preset.dart';
import 'package:fooder/widgets/preset.dart';
import 'package:fooder/components/text.dart';
import 'package:fooder/components/floating_action_button.dart';
class AddMealScreen extends BasedScreen {
final Diary diary;
@ -14,7 +16,7 @@ class AddMealScreen extends BasedScreen {
State<AddMealScreen> createState() => _AddMealScreen();
}
class _AddMealScreen extends State<AddMealScreen> {
class _AddMealScreen extends BasedState<AddMealScreen> {
final nameController = TextEditingController();
final presetNameController = TextEditingController();
bool nameChanged = false;
@ -37,7 +39,7 @@ class _AddMealScreen extends State<AddMealScreen> {
void initState() {
super.initState();
setState(() {
nameController.text = "Meal ${widget.diary.meals.length}";
nameController.text = "Meal ${widget.diary.meals.length + 1}";
});
_getPresets();
}
@ -54,20 +56,10 @@ class _AddMealScreen extends State<AddMealScreen> {
);
}
void showError(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message, textAlign: TextAlign.center),
backgroundColor: Theme.of(context).colorScheme.error,
),
);
}
Future<void> _addMeal() async {
await widget.apiClient.addMeal(
name: nameController.text,
diaryId: widget.diary.id,
order: widget.diary.meals.length,
);
popMeDaddy();
}
@ -114,7 +106,6 @@ class _AddMealScreen extends State<AddMealScreen> {
await widget.apiClient.addMealFromPreset(
name: nameChanged ? nameController.text : selectedPreset!.name,
diaryId: widget.diary.id,
order: widget.diary.meals.length,
presetId: selectedPreset!.id,
);
popMeDaddy();
@ -123,28 +114,21 @@ class _AddMealScreen extends State<AddMealScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text("🅵🅾🅾🅳🅴🆁", style: logoStyle(context)),
),
appBar: appBar(),
body: Center(
child: Container(
constraints: const BoxConstraints(maxWidth: 720),
padding: const EdgeInsets.all(10),
child: ListView(children: <Widget>[
TextFormField(
decoration: const InputDecoration(
labelText: 'Meal name',
),
FTextInput(
labelText: 'Meal name',
controller: nameController,
onChanged: (_) => setState(() {
nameChanged = true;
}),
),
TextFormField(
decoration: const InputDecoration(
hintText: 'Search presets',
),
FTextInput(
labelText: 'Search presets',
controller: presetNameController,
onChanged: (_) => _getPresets(),
),
@ -168,9 +152,9 @@ class _AddMealScreen extends State<AddMealScreen> {
]),
),
),
floatingActionButton: FloatingActionButton(
floatingActionButton: FActionButton(
onPressed: _addMealFromPreset,
child: const Icon(Icons.add),
icon: Icons.playlist_add_rounded,
),
);
}

View file

@ -2,6 +2,9 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fooder/screens/based.dart';
import 'package:fooder/models/product.dart';
import 'package:fooder/widgets/product.dart';
import 'package:fooder/components/text.dart';
import 'package:fooder/components/floating_action_button.dart';
class AddProductScreen extends BasedScreen {
const AddProductScreen({super.key, required super.apiClient});
@ -10,7 +13,7 @@ class AddProductScreen extends BasedScreen {
State<AddProductScreen> createState() => _AddProductScreen();
}
class _AddProductScreen extends State<AddProductScreen> {
class _AddProductScreen extends BasedState<AddProductScreen> {
final nameController = TextEditingController();
final carbController = TextEditingController();
final fatController = TextEditingController();
@ -34,16 +37,8 @@ class _AddProductScreen extends State<AddProductScreen> {
);
}
void showError(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message, textAlign: TextAlign.center),
backgroundColor: Theme.of(context).colorScheme.error,
),
);
}
Future<double?> _parseDouble(String text, String name, {bool silent = false}) async {
Future<double?> _parseDouble(String text, String name,
{bool silent = false}) async {
try {
return double.parse(text.replaceAll(",", "."));
} catch (e) {
@ -58,7 +53,8 @@ class _AddProductScreen extends State<AddProductScreen> {
var carb = await _parseDouble(carbController.text, "Carbs");
var fat = await _parseDouble(fatController.text, "Fat");
var protein = await _parseDouble(proteinController.text, "Protein");
var fiber = await _parseDouble(fiberController.text, "Fiber", silent: true) ?? 0;
var fiber =
await _parseDouble(fiberController.text, "Fiber", silent: true) ?? 0;
if (carb == null || fat == null || protein == null) {
return;
@ -111,25 +107,21 @@ class _AddProductScreen extends State<AddProductScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text("🅵🅾🅾🅳🅴🆁", style: logoStyle(context)),
),
appBar: appBar(),
body: Center(
child: Container(
constraints: const BoxConstraints(maxWidth: 720),
padding: const EdgeInsets.all(10),
child: Column(children: <Widget>[
TextFormField(
decoration: const InputDecoration(
labelText: 'Product name',
),
FTextInput(
labelText: 'Product name',
controller: nameController,
onChanged: (String value) {
setState(() {});
},
),
TextFormField(
decoration: const InputDecoration(
labelText: 'Carbs',
),
FTextInput(
labelText: 'Carbs',
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
inputFormatters: <TextInputFormatter>[
@ -141,10 +133,8 @@ class _AddProductScreen extends State<AddProductScreen> {
setState(() {});
},
),
TextFormField(
decoration: const InputDecoration(
labelText: 'Fat',
),
FTextInput(
labelText: 'Fat',
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
inputFormatters: <TextInputFormatter>[
@ -156,10 +146,8 @@ class _AddProductScreen extends State<AddProductScreen> {
setState(() {});
},
),
TextFormField(
decoration: const InputDecoration(
labelText: 'Protein',
),
FTextInput(
labelText: 'Protein',
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
inputFormatters: <TextInputFormatter>[
@ -171,10 +159,8 @@ class _AddProductScreen extends State<AddProductScreen> {
setState(() {});
},
),
TextFormField(
decoration: const InputDecoration(
labelText: 'Fiber',
),
FTextInput(
labelText: 'Fiber',
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
inputFormatters: <TextInputFormatter>[
@ -186,15 +172,30 @@ class _AddProductScreen extends State<AddProductScreen> {
setState(() {});
},
),
Text(
"${calculateCalories().toStringAsFixed(2)} kcal",
textAlign: TextAlign.right,
),
ProductWidget(
product: Product(
id: 0,
name: nameController.text,
carb: double.tryParse(
carbController.text.replaceAll(",", ".")) ??
0.0,
fat: double.tryParse(
fatController.text.replaceAll(",", ".")) ??
0.0,
protein: double.tryParse(
proteinController.text.replaceAll(",", ".")) ??
0.0,
fiber: double.tryParse(
fiberController.text.replaceAll(",", ".")) ??
0.0,
calories: calculateCalories(),
),
)
])),
),
floatingActionButton: FloatingActionButton(
floatingActionButton: FActionButton(
onPressed: _addProduct,
child: const Icon(Icons.add),
icon: Icons.save,
),
);
}

View file

@ -1,5 +1,9 @@
import 'package:flutter/material.dart';
import 'package:fooder/client.dart';
import 'package:fooder/components/app_bar.dart';
import 'package:fooder/components/navigation_bar.dart';
import 'package:fooder/screens/login.dart';
import 'package:fooder/screens/main.dart';
TextStyle logoStyle(context) {
return Theme.of(context).textTheme.labelLarge!.copyWith(
@ -11,5 +15,111 @@ abstract class BasedScreen extends StatefulWidget {
final ApiClient apiClient;
const BasedScreen({super.key, required this.apiClient});
}
abstract class BasedState<T extends BasedScreen> extends State<T> {
void _logout() async {
await widget.apiClient.logout();
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => LoginScreen(apiClient: widget.apiClient),
),
);
}
void backToDiary() {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => MainScreen(apiClient: widget.apiClient),
),
);
}
FAppBar appBar() {
return FAppBar(
actions: [
IconButton(
icon: Icon(
Icons.logout,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
onPressed: _logout,
),
],
);
}
FNavBar navBar() {
return FNavBar(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
icon: Icon(
Icons.menu_book,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
onPressed: backToDiary,
),
IconButton(
icon: Icon(
Icons.dinner_dining,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
onPressed: () {},
),
IconButton(
icon: Icon(
Icons.lunch_dining,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
onPressed: () {},
),
IconButton(
icon: Icon(
Icons.person,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
onPressed: () {},
),
],
),
],
);
}
void showError(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
message,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.onError,
),
),
backgroundColor: Theme.of(context).colorScheme.error.withOpacity(0.8),
),
);
}
void showText(String text) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
text,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.onPrimary,
),
),
backgroundColor: Theme.of(context).colorScheme.primary.withOpacity(0.8),
),
);
}
}

View file

@ -5,6 +5,9 @@ import 'package:fooder/models/product.dart';
import 'package:fooder/models/entry.dart';
import 'package:fooder/widgets/product.dart';
import 'package:fooder/screens/add_product.dart';
import 'package:fooder/components/text.dart';
import 'package:fooder/components/floating_action_button.dart';
import 'package:simple_barcode_scanner/simple_barcode_scanner.dart';
class EditEntryScreen extends BasedScreen {
final Entry entry;
@ -16,7 +19,7 @@ class EditEntryScreen extends BasedScreen {
State<EditEntryScreen> createState() => _EditEntryScreen();
}
class _EditEntryScreen extends State<EditEntryScreen> {
class _EditEntryScreen extends BasedState<EditEntryScreen> {
final gramsController = TextEditingController();
final productNameController = TextEditingController();
List<Product> products = [];
@ -52,15 +55,6 @@ class _EditEntryScreen extends State<EditEntryScreen> {
});
}
void showError(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message, textAlign: TextAlign.center),
backgroundColor: Theme.of(context).colorScheme.error,
),
);
}
Future<double?> _parseDouble(String text, String name) async {
try {
return double.parse(text.replaceAll(",", "."));
@ -95,30 +89,47 @@ class _EditEntryScreen extends State<EditEntryScreen> {
popMeDaddy();
}
Future<void> _findProductByBarCode() async {
var res = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SimpleBarcodeScannerPage(),
),
);
if (res is String) {
try {
var productMap = await widget.apiClient.getProductByBarcode(res);
var product = Product.fromJson(productMap);
setState(() {
products = [product];
productNameController.text = product.name;
});
} catch (e) {
showError("Product not found");
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text("🅵🅾🅾🅳🅴🆁", style: logoStyle(context)),
),
appBar: appBar(),
body: Center(
child: Container(
constraints: const BoxConstraints(maxWidth: 720),
padding: const EdgeInsets.all(10),
child: ListView(children: <Widget>[
TextFormField(
decoration: const InputDecoration(
labelText: 'Product name',
),
FTextInput(
labelText: 'Product name',
controller: productNameController,
onChanged: (_) => _getProducts(),
autofocus: true,
),
TextFormField(
decoration: const InputDecoration(
labelText: 'Grams',
),
FTextInput(
labelText: 'Grams',
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
inputFormatters: <TextInputFormatter>[
@ -165,15 +176,21 @@ class _EditEntryScreen extends State<EditEntryScreen> {
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
onPressed: _deleteEntry,
heroTag: null,
child: const Icon(Icons.delete),
FActionButton(
onPressed: _findProductByBarCode,
icon: Icons.photo_camera,
),
FloatingActionButton(
const SizedBox(width: 10),
FActionButton(
onPressed: _deleteEntry,
tag: "fap1",
icon: Icons.delete,
),
const SizedBox(width: 10),
FActionButton(
onPressed: _saveEntry,
heroTag: null,
child: const Icon(Icons.save),
tag: "fap2",
icon: Icons.save,
),
],
),

View file

@ -3,6 +3,8 @@ import 'package:flutter/services.dart';
import 'package:fooder/screens/based.dart';
import 'package:fooder/screens/main.dart';
import 'package:fooder/screens/register.dart';
import 'package:fooder/components/text.dart';
import 'package:fooder/components/button.dart';
class LoginScreen extends BasedScreen {
const LoginScreen({super.key, required super.apiClient});
@ -11,7 +13,7 @@ class LoginScreen extends BasedScreen {
State<LoginScreen> createState() => _LoginScreen();
}
class _LoginScreen extends State<LoginScreen> {
class _LoginScreen extends BasedState<LoginScreen> {
final usernameController = TextEditingController();
final passwordController = TextEditingController();
@ -22,24 +24,6 @@ class _LoginScreen extends State<LoginScreen> {
super.dispose();
}
void showError(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message, textAlign: TextAlign.center),
backgroundColor: Theme.of(context).colorScheme.error,
),
);
}
void showText(String text) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(text, textAlign: TextAlign.center),
backgroundColor: Theme.of(context).colorScheme.primary,
),
);
}
void popMeDaddy() {
Navigator.pushReplacement(
context,
@ -78,6 +62,8 @@ class _LoginScreen extends State<LoginScreen> {
}
Future<void> _asyncInitState() async {
await widget.apiClient.loadToken();
if (widget.apiClient.refreshToken == null) {
return;
}
@ -93,11 +79,10 @@ class _LoginScreen extends State<LoginScreen> {
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var colorScheme = theme.colorScheme;
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text("🅵🅾🅾🅳🅴🆁", style: logoStyle(context)),
),
body: Center(
child: Container(
constraints: const BoxConstraints(maxWidth: 600),
@ -106,32 +91,30 @@ class _LoginScreen extends State<LoginScreen> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
decoration: const InputDecoration(
labelText: 'Username',
),
Icon(
Icons.lock,
size: 100,
color: colorScheme.primary.withOpacity(0.85),
),
FTextInput(
labelText: 'Username',
controller: usernameController,
autofillHints: const [AutofillHints.username],
autofocus: true,
),
TextFormField(
obscureText: true,
decoration: const InputDecoration(
labelText: 'Password',
),
FTextInput(
labelText: 'Password',
controller: passwordController,
onFieldSubmitted: (_) => _login(),
autofillHints: const [AutofillHints.password],
obscureText: true,
),
FButton(
labelText: 'Sign In',
onPressed: _login,
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: FilledButton(
onPressed: _login,
child: const Text('Login'),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
padding: const EdgeInsets.symmetric(vertical: 8),
child: TextButton(
onPressed: () {
Navigator.push(

View file

@ -1,9 +1,13 @@
import 'package:flutter/material.dart';
import 'package:fooder/screens/based.dart';
import 'package:fooder/screens/login.dart';
import 'package:fooder/screens/add_entry.dart';
import 'package:fooder/screens/add_meal.dart';
import 'package:fooder/models/diary.dart';
import 'package:fooder/widgets/diary.dart';
import 'package:fooder/widgets/summary.dart';
import 'package:fooder/widgets/meal.dart';
import 'package:fooder/components/sliver.dart';
import 'package:fooder/components/date_picker.dart';
import 'package:fooder/components/floating_action_button.dart';
class MainScreen extends BasedScreen {
const MainScreen({super.key, required super.apiClient});
@ -12,7 +16,7 @@ class MainScreen extends BasedScreen {
State<MainScreen> createState() => _MainScreen();
}
class _MainScreen extends State<MainScreen> {
class _MainScreen extends BasedState<MainScreen> {
Diary? diary;
DateTime date = DateTime.now();
@ -24,33 +28,26 @@ class _MainScreen extends State<MainScreen> {
Future<void> _asyncInitState() async {
var diaryMap = await widget.apiClient.getDiary(date: date);
setState(() {
diary = Diary.fromJson(diaryMap);
date = date;
});
}
Future<void> _pickDate() async {
date = (await showDatePicker(
context: context,
initialDate: date,
firstDate: DateTime(2020),
lastDate: DateTime(DateTime.now().year + 1),
))!;
Future<void> _pickDate(DateTime date) async {
setState(() {
this.date = date;
});
await _asyncInitState();
}
void _logout() async {
widget.apiClient.logout();
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => LoginScreen(apiClient: widget.apiClient),
),
);
}
Future<void> _addEntry() async {
if (diary == null) {
return;
}
await Navigator.push(
context,
MaterialPageRoute(
@ -60,69 +57,76 @@ class _MainScreen extends State<MainScreen> {
).then((_) => _asyncInitState());
}
Future<void> _addMeal(context) async {
if (diary == null) {
return;
}
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddMealScreen(
apiClient: widget.apiClient,
diary: diary!,
),
),
).then((_) => _asyncInitState());
}
@override
Widget build(BuildContext context) {
Widget content;
Widget title;
if (diary != null) {
content = Container(
constraints: const BoxConstraints(maxWidth: 720),
padding: const EdgeInsets.all(10),
child: DiaryWidget(
diary: diary!,
apiClient: widget.apiClient,
refreshParent: _asyncInitState),
);
title = Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextButton(
child: Text(
"🅵🅾🅾🅳🅴🆁",
style: logoStyle(context),
),
onPressed: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) =>
MainScreen(apiClient: widget.apiClient)),
).then((_) => _asyncInitState());
},
content = CustomScrollView(slivers: <Widget>[
SliverPersistentHeader(
delegate: FSliverAppBar(
child: FDatePickerWidget(date: date, onDatePicked: _pickDate)),
pinned: true,
),
SliverList(
delegate: SliverChildListDelegate(
[
SummaryWidget(
diary: diary!,
apiClient: widget.apiClient,
refreshParent: _asyncInitState,
),
for (var (i, meal) in diary!.meals.indexed)
MealWidget(
meal: meal,
apiClient: widget.apiClient,
refreshParent: _asyncInitState,
initiallyExpanded: i == 0,
showText: showText,
),
const SizedBox(height: 200),
],
),
const Spacer(),
Text(
"${date.year}-${date.month}-${date.day}",
style: const TextStyle(fontSize: 20),
),
IconButton(
icon: const Icon(Icons.calendar_month),
onPressed: _pickDate,
),
const Spacer(),
IconButton(
icon: const Icon(Icons.logout),
onPressed: _logout,
),
],
);
),
]);
} else {
content = const CircularProgressIndicator();
title = Text("🅵🅾🅾🅳🅴🆁", style: logoStyle(context));
content = const Center(child: CircularProgressIndicator());
}
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: title,
),
body: Center(
child: content,
),
floatingActionButton: FloatingActionButton(
onPressed: _addEntry,
child: const Icon(Icons.add),
body: content,
extendBodyBehindAppBar: true,
extendBody: true,
appBar: appBar(),
bottomNavigationBar: navBar(),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FActionButton(
icon: Icons.playlist_add,
onPressed: () => _addMeal(context),
),
const SizedBox(width: 10),
FActionButton(
icon: Icons.library_add, onPressed: _addEntry, tag: "fap2"),
],
),
);
}

View file

@ -1,174 +0,0 @@
import 'package:flutter/material.dart';
import 'package:fooder/screens/based.dart';
import 'package:fooder/models/meal.dart';
import 'package:fooder/widgets/macro.dart';
class MealScreen extends BasedScreen {
final Meal meal;
const MealScreen({super.key, required super.apiClient, required this.meal});
@override
State<MealScreen> createState() => _AddMealScreen();
}
class _AddMealScreen extends State<MealScreen> {
Future<void> saveMeal(context) async {
TextEditingController textFieldController = TextEditingController();
textFieldController.text = widget.meal.name;
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Save Meal'),
content: TextField(
controller: textFieldController,
decoration: const InputDecoration(hintText: "Meal template name"),
),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.cancel),
onPressed: () {
Navigator.pop(context);
},
),
IconButton(
icon: const Icon(Icons.save),
onPressed: () {
widget.apiClient
.saveMeal(widget.meal, textFieldController.text);
Navigator.pop(context);
},
),
],
);
},
);
}
Future<void> _deleteMeal(Meal meal) async {
await widget.apiClient.deleteMeal(meal.id);
}
Future<void> deleteMeal(context) async {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Confirm deletion of the meal'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.cancel),
onPressed: () {
Navigator.pop(context);
},
),
IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
_deleteMeal(widget.meal);
Navigator.pop(context);
},
),
],
);
},
);
}
Widget buildButton(Icon icon, String text, Function() onPressed) {
return Card(
child: ListTile(
onTap: onPressed,
title: Container(
padding: const EdgeInsets.all(8),
child: Row(
children: <Widget>[
IconButton(
icon: icon,
onPressed: onPressed,
),
const Spacer(),
Text(text),
],
),
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text("🅵🅾🅾🅳🅴🆁", style: logoStyle(context)),
),
body: Center(
child: Container(
constraints: const BoxConstraints(maxWidth: 720),
padding: const EdgeInsets.all(10),
child: CustomScrollView(slivers: <Widget>[
SliverAppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
widget.meal.name,
style: Theme.of(context)
.textTheme
.headlineLarge!
.copyWith(
color: Theme.of(context).colorScheme.onSecondary,
fontWeight: FontWeight.bold,
),
),
const Spacer(),
Text(
"${widget.meal.calories.toStringAsFixed(1)} kcal",
style: Theme.of(context)
.textTheme
.headlineLarge!
.copyWith(
color: Theme.of(context).colorScheme.onSecondary,
fontWeight: FontWeight.bold,
),
),
]),
expandedHeight: 150,
backgroundColor: Theme.of(context).colorScheme.secondary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
floating: true,
flexibleSpace: FlexibleSpaceBar(
title: MacroWidget(
protein: widget.meal.protein,
carb: widget.meal.carb,
fat: widget.meal.fat,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onSecondary,
fontWeight: FontWeight.bold,
),
),
)),
SliverList(
delegate: SliverChildListDelegate([
buildButton(
const Icon(Icons.save),
"Save as preset",
() => saveMeal(context),
),
buildButton(
const Icon(Icons.delete),
"Delete",
() => deleteMeal(context),
),
]))
]),
),
),
);
}
}

View file

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fooder/screens/based.dart';
import 'package:fooder/components/text.dart';
import 'package:fooder/components/button.dart';
class RegisterScreen extends BasedScreen {
const RegisterScreen({super.key, required super.apiClient});
@ -9,7 +11,7 @@ class RegisterScreen extends BasedScreen {
State<RegisterScreen> createState() => _RegisterScreen();
}
class _RegisterScreen extends State<RegisterScreen> {
class _RegisterScreen extends BasedState<RegisterScreen> {
final usernameController = TextEditingController();
final passwordController = TextEditingController();
final passwordConfirmController = TextEditingController();
@ -35,24 +37,6 @@ class _RegisterScreen extends State<RegisterScreen> {
});
}
void showError(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message, textAlign: TextAlign.center),
backgroundColor: Theme.of(context).colorScheme.error,
),
);
}
void showText(String text) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(text, textAlign: TextAlign.center),
backgroundColor: Theme.of(context).colorScheme.primary,
),
);
}
void popMeDaddy() {
Navigator.pop(context);
}
@ -78,11 +62,10 @@ class _RegisterScreen extends State<RegisterScreen> {
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var colorScheme = theme.colorScheme;
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text("🅵🅾🅾🅳🅴🆁", style: logoStyle(context)),
),
body: Center(
child: Container(
constraints: const BoxConstraints(maxWidth: 600),
@ -91,35 +74,34 @@ class _RegisterScreen extends State<RegisterScreen> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
decoration: const InputDecoration(
labelText: 'Username',
),
Icon(
Icons.group_add,
size: 100,
color: colorScheme.primary.withOpacity(0.85),
),
FTextInput(
labelText: 'Username',
controller: usernameController,
autofillHints: const [AutofillHints.username],
autofocus: true,
),
TextFormField(
obscureText: true,
decoration: const InputDecoration(
labelText: 'Password',
),
FTextInput(
labelText: 'Password',
controller: passwordController,
autofillHints: const [AutofillHints.password],
obscureText: true,
),
FTextInput(
labelText: 'Confirm password',
controller: passwordConfirmController,
autofillHints: const [AutofillHints.password],
onFieldSubmitted: (_) => _register(),
obscureText: true,
),
FButton(
labelText: 'Register account',
onPressed: _register,
),
TextFormField(
obscureText: true,
decoration: const InputDecoration(
labelText: 'Confirm password',
),
controller: passwordConfirmController,
autofillHints: const [AutofillHints.password],
onFieldSubmitted: (_) => _register()),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: FilledButton(
onPressed: _register,
child: const Text('Register'),
)),
],
),
),

51
lib/theme.dart Normal file
View file

@ -0,0 +1,51 @@
import 'package:flex_color_scheme/flex_color_scheme.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class MainTheme {
static ThemeData light() {
return FlexThemeData.light(
scheme: FlexScheme.brandBlue,
surfaceMode: FlexSurfaceMode.levelSurfacesLowScaffold,
blendLevel: 40,
subThemesData: const FlexSubThemesData(
blendOnLevel: 40,
useTextTheme: true,
useM2StyleDividerInM3: true,
alignedDropdown: true,
useInputDecoratorThemeInDialogs: true,
),
keyColors: const FlexKeyColors(
useSecondary: true,
useTertiary: true,
),
visualDensity: FlexColorScheme.comfortablePlatformDensity,
useMaterial3: true,
swapLegacyOnMaterial3: true,
fontFamily: GoogleFonts.notoSans().fontFamily,
).copyWith(
dividerColor: Colors.transparent,
);
}
static ThemeData dark() {
return FlexThemeData.dark(
scheme: FlexScheme.brandBlue,
surfaceMode: FlexSurfaceMode.levelSurfacesLowScaffold,
blendLevel: 40,
subThemesData: const FlexSubThemesData(
blendOnLevel: 20,
useTextTheme: true,
useM2StyleDividerInM3: true,
alignedDropdown: true,
useInputDecoratorThemeInDialogs: true,
),
visualDensity: FlexColorScheme.comfortablePlatformDensity,
useMaterial3: true,
swapLegacyOnMaterial3: true,
fontFamily: GoogleFonts.notoSans().fontFamily,
).copyWith(
dividerColor: Colors.transparent,
);
}
}

View file

@ -1,96 +0,0 @@
import 'package:flutter/material.dart';
import 'package:fooder/models/diary.dart';
import 'package:fooder/widgets/meal.dart';
import 'package:fooder/widgets/macro.dart';
import 'package:fooder/client.dart';
import 'package:fooder/screens/add_meal.dart';
import 'dart:core';
class DiaryWidget extends StatelessWidget {
final Diary diary;
final ApiClient apiClient;
final Function() refreshParent;
const DiaryWidget(
{super.key,
required this.diary,
required this.apiClient,
required this.refreshParent});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(8),
child: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
const Spacer(),
Text(
"${diary.calories.toStringAsFixed(1)} kcal",
style: Theme.of(context)
.textTheme
.headlineLarge!
.copyWith(
color: Theme.of(context).colorScheme.onSecondary,
fontWeight: FontWeight.bold,
),
),
]),
expandedHeight: 150,
backgroundColor: Theme.of(context).colorScheme.secondary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
floating: true,
flexibleSpace: FlexibleSpaceBar(
title: MacroWidget(
protein: diary.protein,
carb: diary.carb,
fat: diary.fat,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onSecondary,
fontWeight: FontWeight.bold,
),
),
)),
SliverToBoxAdapter(
child: SizedBox(
height: 30,
child: TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddMealScreen(
apiClient: apiClient,
diary: diary,
),
),
).then((_) {
refreshParent();
});
},
child: const Text("Add Meal"),
),
),
),
SliverList(
delegate: SliverChildListDelegate(
[
for (var meal in diary.meals)
MealWidget(
meal: meal,
apiClient: apiClient,
refreshParent: refreshParent,
),
],
),
),
],
),
);
}
}

View file

@ -3,6 +3,43 @@ import 'package:fooder/models/entry.dart';
import 'package:fooder/widgets/macro.dart';
import 'dart:core';
class EntryHeader extends StatelessWidget {
final Entry entry;
const EntryHeader({super.key, required this.entry});
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(8),
child: Text(
entry.product.name,
overflow: TextOverflow.fade,
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.bold,
),
),
),
),
const Spacer(),
Padding(
padding: const EdgeInsets.all(8),
child: Text(
"${entry.grams.toStringAsFixed(0)} g",
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onSurface,
),
),
),
],
);
}
}
class EntryWidget extends StatelessWidget {
final Entry entry;
@ -14,25 +51,12 @@ class EntryWidget extends StatelessWidget {
padding: const EdgeInsets.all(8),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Text(
entry.product.name,
style: Theme.of(context).textTheme.titleLarge,
),
),
Text("${entry.calories.toStringAsFixed(1)} kcal"),
],
),
MacroWidget(
EntryHeader(entry: entry),
MacroEntryWidget(
protein: entry.protein,
carb: entry.carb,
fat: entry.fat,
amount: entry.grams,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.secondary,
),
calories: entry.calories,
),
],
),

View file

@ -1,122 +1,163 @@
import 'package:flutter/material.dart';
import 'dart:core';
class MacroWidget extends StatelessWidget {
final double? amount;
final double? calories;
final double? fiber;
final double protein;
final double carb;
final double fat;
final TextStyle style;
final Widget? child;
class MacroHeaderWidget extends StatelessWidget {
static const double padY = 4;
static const double padX = 8;
const MacroWidget({
Key? key,
this.calories,
this.amount,
this.child,
this.fiber,
required this.protein,
required this.carb,
required this.fat,
required this.style,
}) : super(key: key);
final bool? fiber;
final bool? calories;
final Alignment alignment;
const MacroHeaderWidget({
super.key,
this.fiber = false,
this.calories = false,
this.alignment = Alignment.centerLeft,
});
@override
Widget build(BuildContext context) {
var elements = <Widget>[
Expanded(
flex: 1,
child: Text(
"C: ${carb.toStringAsFixed(1)}g",
style: style,
textAlign: TextAlign.center,
var elements = <String>[
"C(g)",
"F(g)",
"P(g)",
];
if (fiber == true) {
elements.add(
"f(g)",
);
}
if (calories == true) {
elements.add(
"kcal",
);
}
var children = <Widget>[];
if (alignment == Alignment.centerRight) {
children.add(const Spacer());
}
for (var element in elements) {
children.add(
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 2,
),
child: SizedBox(
width: 55,
child: Text(
element,
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
color: Theme.of(context).colorScheme.onSurface,
),
textAlign: TextAlign.center,
),
),
),
);
}
if (alignment == Alignment.centerLeft) {
children.add(const Spacer());
}
return Padding(
padding: const EdgeInsets.symmetric(
vertical: padY,
horizontal: padX,
),
Expanded(
flex: 1,
child: Text(
"F: ${fat.toStringAsFixed(1)}g",
style: style,
textAlign: TextAlign.center,
),
),
Expanded(
flex: 1,
child: Text(
"P: ${protein.toStringAsFixed(1)}g",
style: style,
textAlign: TextAlign.center,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: children,
),
);
}
}
class MacroEntryWidget extends StatelessWidget {
static const double padY = 4;
static const double padX = 8;
final double protein;
final double carb;
final double fat;
final double? fiber;
final double? calories;
final Alignment alignment;
const MacroEntryWidget({
super.key,
required this.protein,
required this.carb,
required this.fat,
this.fiber,
this.calories,
this.alignment = Alignment.centerLeft,
});
@override
Widget build(BuildContext context) {
var elements = <String>[
(carb.toStringAsFixed(1)),
(fat.toStringAsFixed(1)),
(protein.toStringAsFixed(1)),
];
if (fiber != null) {
elements.add(
Expanded(
flex: 1,
child: Text(
"f: ${fiber!.toStringAsFixed(1)}g",
style: style,
textAlign: TextAlign.center,
),
),
fiber!.toStringAsFixed(1),
);
}
if (calories != null) {
elements.add(
Expanded(
flex: 1,
child: Text(
"${calories!.toStringAsFixed(1)} kcal",
style: style,
textAlign: TextAlign.center,
calories!.toStringAsFixed(0),
);
}
var children = <Widget>[];
if (alignment == Alignment.centerRight) {
children.add(const Spacer());
}
for (var element in elements) {
children.add(
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 2,
),
child: SizedBox(
width: 55,
child: Text(
element,
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
color: Theme.of(context).colorScheme.onSurface,
),
textAlign: TextAlign.center,
),
),
),
);
}
if (amount != null) {
elements.add(
Expanded(
flex: 1,
child: Text(
"${amount!.toStringAsFixed(1)}g",
style: style,
textAlign: TextAlign.center,
),
),
);
}
if (child != null) {
elements.add(
Expanded(
flex: 1,
child: child!,
),
);
}
if (elements.length < 4) {
elements.add(
const Expanded(
flex: 1,
child: Text(""),
),
);
if (alignment == Alignment.centerLeft) {
children.add(const Spacer());
}
return Padding(
padding: const EdgeInsets.only(
top: 4.0,
bottom: 4.0,
left: 8.0,
right: 8.0,
padding: const EdgeInsets.symmetric(
vertical: padY,
horizontal: padX,
),
child: Row(
children: elements,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: children,
),
);
}

View file

@ -3,90 +3,203 @@ import 'package:fooder/models/meal.dart';
import 'package:fooder/widgets/entry.dart';
import 'package:fooder/widgets/macro.dart';
import 'package:fooder/screens/edit_entry.dart';
import 'package:fooder/screens/meal.dart';
import 'package:fooder/client.dart';
import 'dart:core';
class MealWidget extends StatelessWidget {
class MealHeader extends StatelessWidget {
final Meal meal;
final ApiClient apiClient;
final Function() refreshParent;
const MealWidget(
{super.key,
required this.meal,
required this.apiClient,
required this.refreshParent});
const MealHeader({super.key, required this.meal});
@override
Widget build(BuildContext context) {
return Card(
child: GestureDetector(
onLongPress: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MealScreen(
apiClient: apiClient,
meal: meal,
return Row(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(8),
child: Text(
meal.name,
overflow: TextOverflow.fade,
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.bold,
),
),
),
).then((_) {
refreshParent();
});
},
child: Padding(
padding: const EdgeInsets.only(
top: 36.0, left: 6.0, right: 6.0, bottom: 6.0),
child: ExpansionTile(
title: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Text(
meal.name,
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: Theme.of(context).colorScheme.primary,
),
),
),
Text("${meal.calories.toStringAsFixed(1)} kcal"),
],
),
MacroWidget(
protein: meal.protein,
carb: meal.carb,
fat: meal.fat,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.secondary,
),
),
],
),
children: <Widget>[
for (var entry in meal.entries)
ListTile(
title: EntryWidget(
entry: entry,
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EditEntryScreen(
apiClient: apiClient,
entry: entry,
),
),
).then((_) {
refreshParent();
});
},
)
],
),
),
));
],
);
}
}
class MealWidget extends StatelessWidget {
static const maxWidth = 920.0;
final Meal meal;
final ApiClient apiClient;
final Function() refreshParent;
final Function(String) showText;
final bool initiallyExpanded;
const MealWidget({
super.key,
required this.meal,
required this.apiClient,
required this.refreshParent,
required this.initiallyExpanded,
required this.showText,
});
Future<void> saveMeal(context) async {
TextEditingController textFieldController = TextEditingController();
textFieldController.text = meal.name;
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Save Meal'),
content: TextField(
controller: textFieldController,
decoration: const InputDecoration(hintText: "Meal template name"),
),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.cancel),
onPressed: () {
Navigator.pop(context);
},
),
IconButton(
icon: const Icon(Icons.save),
onPressed: () {
apiClient.saveMeal(meal, textFieldController.text);
Navigator.pop(context);
showText("Meal saved");
},
),
],
);
},
);
}
Future<void> _deleteMeal(Meal meal) async {
await apiClient.deleteMeal(meal.id);
}
Future<void> deleteMeal(context) async {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
title: const Text('Confirm deletion of the meal'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.cancel),
onPressed: () {
Navigator.pop(context);
},
),
IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
_deleteMeal(meal).then((_) => refreshParent());
Navigator.pop(context);
showText("Meal deleted");
},
),
],
);
},
);
}
Future<void> _editEntry(context, entry) async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EditEntryScreen(
apiClient: apiClient,
entry: entry,
),
),
).then((_) => refreshParent());
}
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var colorScheme = theme.colorScheme;
var widthAvail = MediaQuery.of(context).size.width;
var width = widthAvail > maxWidth ? maxWidth : widthAvail;
return Center(
child: Padding(
padding: const EdgeInsets.all(8),
child: Card(
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
child: Container(
constraints: BoxConstraints(maxWidth: width),
color: colorScheme.surface.withOpacity(0.2),
child: ExpansionTile(
iconColor: colorScheme.onSurface,
collapsedIconColor: colorScheme.onSurface,
initiallyExpanded: initiallyExpanded,
enableFeedback: true,
title: Padding(
padding: const EdgeInsets.all(8),
child: Column(
children: <Widget>[
MealHeader(meal: meal),
const MacroHeaderWidget(
calories: true,
),
MacroEntryWidget(
protein: meal.protein,
carb: meal.carb,
fat: meal.fat,
calories: meal.calories,
),
],
),
),
children: <Widget>[
for (var (i, entry) in meal.entries.indexed)
ListTile(
title: EntryWidget(
entry: entry,
),
tileColor: i % 2 == 0
? colorScheme.surfaceVariant.withOpacity(0.1)
: Colors.transparent,
onTap: () => _editEntry(context, entry),
enableFeedback: true,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(
icon: const Icon(Icons.save),
onPressed: () => saveMeal(context),
),
IconButton(
icon: const Icon(Icons.delete),
onPressed: () => deleteMeal(context),
),
],
),
],
),
),
),
),
);
}
}

View file

@ -3,6 +3,33 @@ import 'package:fooder/models/preset.dart';
import 'package:fooder/widgets/macro.dart';
import 'dart:core';
class PresetHeader extends StatelessWidget {
final String title;
const PresetHeader({super.key, required this.title});
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
title,
overflow: TextOverflow.fade,
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.bold,
),
),
),
),
],
);
}
}
class PresetWidget extends StatelessWidget {
final Preset preset;
@ -14,24 +41,21 @@ class PresetWidget extends StatelessWidget {
padding: const EdgeInsets.all(8),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Text(
preset.name,
style: Theme.of(context).textTheme.titleLarge,
),
),
Text("${preset.calories.toStringAsFixed(1)} kcal"),
],
PresetHeader(
title: preset.name,
),
MacroWidget(
const MacroHeaderWidget(
fiber: true,
calories: true,
alignment: Alignment.center,
),
MacroEntryWidget(
protein: preset.protein,
carb: preset.carb,
fat: preset.fat,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.secondary,
),
fiber: preset.fiber,
calories: preset.calories,
alignment: Alignment.center,
),
],
),

View file

@ -3,6 +3,33 @@ import 'package:fooder/models/product.dart';
import 'package:fooder/widgets/macro.dart';
import 'dart:core';
class ProductHeader extends StatelessWidget {
final String title;
const ProductHeader({super.key, required this.title});
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
title,
overflow: TextOverflow.fade,
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.bold,
),
),
),
),
],
);
}
}
class ProductWidget extends StatelessWidget {
final Product product;
@ -14,26 +41,21 @@ class ProductWidget extends StatelessWidget {
padding: const EdgeInsets.all(8),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Text(
product.name,
style: Theme.of(context).textTheme.titleLarge,
),
),
Text("${product.calories.toStringAsFixed(1)} kcal"),
],
ProductHeader(
title: product.name,
),
MacroWidget(
const MacroHeaderWidget(
fiber: true,
calories: true,
alignment: Alignment.center,
),
MacroEntryWidget(
protein: product.protein,
carb: product.carb,
fat: product.fat,
fiber: product.fiber,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.secondary,
fontWeight: FontWeight.bold,
),
calories: product.calories,
alignment: Alignment.center,
),
],
),

86
lib/widgets/summary.dart Normal file
View file

@ -0,0 +1,86 @@
import 'package:flutter/material.dart';
import 'package:fooder/models/diary.dart';
import 'package:fooder/widgets/macro.dart';
import 'package:fooder/client.dart';
import 'dart:core';
class SummaryHeader extends StatelessWidget {
final Diary diary;
const SummaryHeader({super.key, required this.diary});
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
"Summary",
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.bold,
),
),
),
const Spacer(),
],
);
}
}
class SummaryWidget extends StatelessWidget {
static const maxWidth = 920.0;
final Diary diary;
final ApiClient apiClient;
final Function() refreshParent;
const SummaryWidget(
{super.key,
required this.diary,
required this.apiClient,
required this.refreshParent});
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
var colorScheme = theme.colorScheme;
var widthAvail = MediaQuery.of(context).size.width;
var width = widthAvail > maxWidth ? maxWidth : widthAvail;
return Center(
child: Padding(
padding: const EdgeInsets.all(8),
child: Card(
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
child: Container(
constraints: BoxConstraints(maxWidth: width),
color: colorScheme.surface.withOpacity(0.2),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24),
child: Column(
children: <Widget>[
SummaryHeader(diary: diary),
const MacroHeaderWidget(
calories: true,
),
MacroEntryWidget(
protein: diary.protein,
carb: diary.carb,
fat: diary.fat,
calories: diary.calories,
),
],
),
),
),
),
),
);
}
}

View file

@ -6,6 +6,10 @@
#include "generated_plugin_registrant.h"
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
}

View file

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
flutter_secure_storage_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST

View file

@ -5,6 +5,10 @@
import FlutterMacOS
import Foundation
import flutter_secure_storage_macos
import path_provider_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
}

29
macos/Podfile.lock Normal file
View file

@ -0,0 +1,29 @@
PODS:
- flutter_secure_storage_macos (6.1.1):
- FlutterMacOS
- FlutterMacOS (1.0.0)
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
DEPENDENCIES:
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
EXTERNAL SOURCES:
flutter_secure_storage_macos:
:path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos
FlutterMacOS:
:path: Flutter/ephemeral
path_provider_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
SPEC CHECKSUMS:
flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
COCOAPODS: 1.15.2

View file

@ -27,6 +27,8 @@
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
47A87A2D935C9FD21BC18BA6 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17BEB3BB725731AD7E5B80B1 /* Pods_Runner.framework */; };
DC768C2964C710CDD24D0DB4 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 465D02A290608CA511C1F4CA /* Pods_RunnerTests.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -60,11 +62,14 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
0834B9429B6D5467EDC30594 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
17BEB3BB725731AD7E5B80B1 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
19DEDDAC7F8C48D43A9E76CF /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
33CC10ED2044A3C60003C045 /* fooder_web.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "fooder_web.app"; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10ED2044A3C60003C045 /* fooder_web.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = fooder_web.app; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
@ -76,8 +81,13 @@
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
465D02A290608CA511C1F4CA /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
A4E1FEE05E55FE57D150055B /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
A7C614E4C8DC6AE860A60034 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
D32BEBE6FA5B351434300418 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
F971A3061D014BCFE5A477A9 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -85,6 +95,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
DC768C2964C710CDD24D0DB4 /* Pods_RunnerTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -92,12 +103,27 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
47A87A2D935C9FD21BC18BA6 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
076AC79CB527EDDF03C6C1BF /* Pods */ = {
isa = PBXGroup;
children = (
0834B9429B6D5467EDC30594 /* Pods-Runner.debug.xcconfig */,
A7C614E4C8DC6AE860A60034 /* Pods-Runner.release.xcconfig */,
A4E1FEE05E55FE57D150055B /* Pods-Runner.profile.xcconfig */,
F971A3061D014BCFE5A477A9 /* Pods-RunnerTests.debug.xcconfig */,
D32BEBE6FA5B351434300418 /* Pods-RunnerTests.release.xcconfig */,
19DEDDAC7F8C48D43A9E76CF /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
331C80D6294CF71000263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
@ -125,6 +151,7 @@
331C80D6294CF71000263BE5 /* RunnerTests */,
33CC10EE2044A3C60003C045 /* Products */,
D73912EC22F37F3D000D13A0 /* Frameworks */,
076AC79CB527EDDF03C6C1BF /* Pods */,
);
sourceTree = "<group>";
};
@ -175,6 +202,8 @@
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
isa = PBXGroup;
children = (
17BEB3BB725731AD7E5B80B1 /* Pods_Runner.framework */,
465D02A290608CA511C1F4CA /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -186,6 +215,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
EBBB44C74E6664565AAD7743 /* [CP] Check Pods Manifest.lock */,
331C80D1294CF70F00263BE5 /* Sources */,
331C80D2294CF70F00263BE5 /* Frameworks */,
331C80D3294CF70F00263BE5 /* Resources */,
@ -204,11 +234,13 @@
isa = PBXNativeTarget;
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
DD4A3E0D06F0EB3D92595679 /* [CP] Check Pods Manifest.lock */,
33CC10E92044A3C60003C045 /* Sources */,
33CC10EA2044A3C60003C045 /* Frameworks */,
33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */,
3020AFE6AB38B106E5D2E587 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -227,7 +259,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1430;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C80D4294CF70F00263BE5 = {
@ -290,6 +322,23 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3020AFE6AB38B106E5D2E587 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
3399D490228B24CF009A79C7 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@ -328,6 +377,50 @@
shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
};
DD4A3E0D06F0EB3D92595679 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
EBBB44C74E6664565AAD7743 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -379,6 +472,7 @@
/* Begin XCBuildConfiguration section */
331C80DB294CF71000263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F971A3061D014BCFE5A477A9 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
@ -393,6 +487,7 @@
};
331C80DC294CF71000263BE5 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D32BEBE6FA5B351434300418 /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
@ -407,6 +502,7 @@
};
331C80DD294CF71000263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 19DEDDAC7F8C48D43A9E76CF /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View file

@ -4,4 +4,7 @@
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View file

@ -1,68 +1,68 @@
{
"images" : [
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "app_icon_16.png",
"scale" : "1x"
"info": {
"version": 1,
"author": "xcode"
},
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "app_icon_32.png",
"scale" : "2x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "app_icon_32.png",
"scale" : "1x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "app_icon_64.png",
"scale" : "2x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "app_icon_128.png",
"scale" : "1x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "app_icon_256.png",
"scale" : "2x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "app_icon_256.png",
"scale" : "1x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "app_icon_512.png",
"scale" : "2x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "app_icon_512.png",
"scale" : "1x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "app_icon_1024.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
"images": [
{
"size": "16x16",
"idiom": "mac",
"filename": "app_icon_16.png",
"scale": "1x"
},
{
"size": "16x16",
"idiom": "mac",
"filename": "app_icon_32.png",
"scale": "2x"
},
{
"size": "32x32",
"idiom": "mac",
"filename": "app_icon_32.png",
"scale": "1x"
},
{
"size": "32x32",
"idiom": "mac",
"filename": "app_icon_64.png",
"scale": "2x"
},
{
"size": "128x128",
"idiom": "mac",
"filename": "app_icon_128.png",
"scale": "1x"
},
{
"size": "128x128",
"idiom": "mac",
"filename": "app_icon_256.png",
"scale": "2x"
},
{
"size": "256x256",
"idiom": "mac",
"filename": "app_icon_256.png",
"scale": "1x"
},
{
"size": "256x256",
"idiom": "mac",
"filename": "app_icon_512.png",
"scale": "2x"
},
{
"size": "512x512",
"idiom": "mac",
"filename": "app_icon_512.png",
"scale": "1x"
},
{
"size": "512x512",
"idiom": "mac",
"filename": "app_icon_1024.png",
"scale": "2x"
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 520 B

After

Width:  |  Height:  |  Size: 539 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 943 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -1,6 +1,22 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
archive:
dependency: transitive
description:
name: archive
sha256: ecf4273855368121b1caed0d10d4513c7241dfc813f7d3c8933b36622ae9b265
url: "https://pub.dev"
source: hosted
version: "3.5.1"
args:
dependency: transitive
description:
name: args
sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
url: "https://pub.dev"
source: hosted
version: "2.5.0"
async:
dependency: transitive
description:
@ -9,6 +25,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.11.0"
blur:
dependency: "direct main"
description:
name: blur
sha256: fd23f1247faee4a7d1a3efb6b7c3cea134f3b939d72e5f8d45233deb0776259f
url: "https://pub.dev"
source: hosted
version: "3.1.0"
boolean_selector:
dependency: transitive
description:
@ -25,6 +49,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
url: "https://pub.dev"
source: hosted
version: "2.0.3"
cli_util:
dependency: transitive
description:
name: cli_util
sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19
url: "https://pub.dev"
source: hosted
version: "0.4.1"
clock:
dependency: transitive
description:
@ -37,18 +77,34 @@ packages:
dependency: transitive
description:
name: collection
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev"
source: hosted
version: "1.17.2"
version: "1.18.0"
crypto:
dependency: transitive
description:
name: crypto
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted
version: "3.0.3"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
url: "https://pub.dev"
source: hosted
version: "1.0.5"
version: "1.0.8"
fading_edge_scrollview:
dependency: transitive
description:
name: fading_edge_scrollview
sha256: c25c2231652ce774cc31824d0112f11f653881f43d7f5302c05af11942052031
url: "https://pub.dev"
source: hosted
version: "3.0.0"
fake_async:
dependency: transitive
description:
@ -57,32 +113,141 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.1"
ffi:
dependency: transitive
description:
name: ffi
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
flex_color_scheme:
dependency: "direct main"
description:
name: flex_color_scheme
sha256: "32914024a4f404d90ff449f58d279191675b28e7c08824046baf06826e99d984"
url: "https://pub.dev"
source: hosted
version: "7.3.1"
flex_seed_scheme:
dependency: transitive
description:
name: flex_seed_scheme
sha256: "4cee2f1d07259f77e8b36f4ec5f35499d19e74e17c7dce5b819554914082bc01"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_barcode_scanner:
dependency: transitive
description:
name: flutter_barcode_scanner
sha256: a4ba37daf9933f451a5e812c753ddd045d6354e4a3280342d895b07fecaab3fa
url: "https://pub.dev"
source: hosted
version: "2.0.0"
flutter_launcher_icons:
dependency: "direct main"
description:
name: flutter_launcher_icons
sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea"
url: "https://pub.dev"
source: hosted
version: "0.13.1"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4"
sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
version: "4.0.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f"
url: "https://pub.dev"
source: hosted
version: "2.0.19"
flutter_secure_storage:
dependency: "direct main"
description:
name: flutter_secure_storage
sha256: c0f402067fb0498934faa6bddd670de0a3db45222e2ca9a068c6177c9a2360a4
url: "https://pub.dev"
source: hosted
version: "9.1.1"
flutter_secure_storage_linux:
dependency: transitive
description:
name: flutter_secure_storage_linux
sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
flutter_secure_storage_macos:
dependency: transitive
description:
name: flutter_secure_storage_macos
sha256: "8cfa53010a294ff095d7be8fa5bb15f2252c50018d69c5104851303f3ff92510"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
flutter_secure_storage_platform_interface:
dependency: transitive
description:
name: flutter_secure_storage_platform_interface
sha256: "301f67ee9b87f04aef227f57f13f126fa7b13543c8e7a93f25c5d2d534c28a4a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
flutter_secure_storage_web:
dependency: transitive
description:
name: flutter_secure_storage_web
sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9
url: "https://pub.dev"
source: hosted
version: "1.2.1"
flutter_secure_storage_windows:
dependency: transitive
description:
name: flutter_secure_storage_windows
sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709
url: "https://pub.dev"
source: hosted
version: "3.1.2"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
google_fonts:
dependency: "direct main"
description:
name: google_fonts
sha256: b1ac0fe2832c9cc95e5e88b57d627c5e68c223b9657f4b96e1487aa9098c7b82
url: "https://pub.dev"
source: hosted
version: "6.2.1"
http:
dependency: "direct main"
description:
name: http
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.2.1"
http_parser:
dependency: transitive
description:
@ -91,54 +256,238 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.2"
image:
dependency: transitive
description:
name: image
sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e"
url: "https://pub.dev"
source: hosted
version: "4.1.7"
intl:
dependency: "direct main"
description:
name: intl
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev"
source: hosted
version: "0.18.1"
version: "0.19.0"
js:
dependency: transitive
description:
name: js
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev"
source: hosted
version: "0.6.7"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
url: "https://pub.dev"
source: hosted
version: "4.9.0"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
url: "https://pub.dev"
source: hosted
version: "10.0.0"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
url: "https://pub.dev"
source: hosted
version: "2.0.1"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
url: "https://pub.dev"
source: hosted
version: "2.0.1"
lints:
dependency: transitive
description:
name: lints
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "4.0.0"
marquee:
dependency: "direct main"
description:
name: marquee
sha256: "4b5243d2804373bdc25fc93d42c3b402d6ec1f4ee8d0bb72276edd04ae7addb8"
url: "https://pub.dev"
source: hosted
version: "2.2.3"
matcher:
dependency: transitive
description:
name: matcher
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev"
source: hosted
version: "0.12.16"
version: "0.12.16+1"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
url: "https://pub.dev"
source: hosted
version: "0.5.0"
version: "0.8.0"
meta:
dependency: transitive
description:
name: meta
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
url: "https://pub.dev"
source: hosted
version: "1.9.1"
version: "1.11.0"
path:
dependency: transitive
description:
name: path
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev"
source: hosted
version: "1.8.3"
version: "1.9.0"
path_provider:
dependency: transitive
description:
name: path_provider
sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
url: "https://pub.dev"
source: hosted
version: "2.1.3"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d
url: "https://pub.dev"
source: hosted
version: "2.2.4"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
url: "https://pub.dev"
source: hosted
version: "2.4.0"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
permission_handler:
dependency: transitive
description:
name: permission_handler
sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
url: "https://pub.dev"
source: hosted
version: "11.3.1"
permission_handler_android:
dependency: transitive
description:
name: permission_handler_android
sha256: "8bb852cd759488893805c3161d0b2b5db55db52f773dbb014420b304055ba2c5"
url: "https://pub.dev"
source: hosted
version: "12.0.6"
permission_handler_apple:
dependency: transitive
description:
name: permission_handler_apple
sha256: e9ad66020b89ff1b63908f247c2c6f931c6e62699b756ef8b3c4569350cd8662
url: "https://pub.dev"
source: hosted
version: "9.4.4"
permission_handler_html:
dependency: transitive
description:
name: permission_handler_html
sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d"
url: "https://pub.dev"
source: hosted
version: "0.1.1"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
sha256: "48d4fcf201a1dad93ee869ab0d4101d084f49136ec82a8a06ed9cfeacab9fd20"
url: "https://pub.dev"
source: hosted
version: "4.2.1"
permission_handler_windows:
dependency: transitive
description:
name: permission_handler_windows
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
url: "https://pub.dev"
source: hosted
version: "0.2.1"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
url: "https://pub.dev"
source: hosted
version: "6.0.2"
platform:
dependency: transitive
description:
name: platform
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
url: "https://pub.dev"
source: hosted
version: "3.1.4"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
version: "2.1.8"
simple_barcode_scanner:
dependency: "direct main"
description:
name: simple_barcode_scanner
sha256: e14c0b302ffe76f0b61004aa47c36365b5d884fcd745494309e21042667902d3
url: "https://pub.dev"
source: hosted
version: "0.1.1"
sky_engine:
dependency: transitive
description: flutter
@ -156,18 +505,18 @@ packages:
dependency: transitive
description:
name: stack_trace
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev"
source: hosted
version: "1.11.0"
version: "1.11.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.1.2"
string_scanner:
dependency: transitive
description:
@ -188,10 +537,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
url: "https://pub.dev"
source: hosted
version: "0.6.0"
version: "0.6.1"
typed_data:
dependency: transitive
description:
@ -208,13 +557,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
url: "https://pub.dev"
source: hosted
version: "13.0.0"
web:
dependency: transitive
description:
name: web
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
url: "https://pub.dev"
source: hosted
version: "0.1.4-beta"
version: "0.5.1"
webview_windows:
dependency: transitive
description:
name: webview_windows
sha256: "7572089e5d6fe09e790093c499fcb6d76a552250187dbcb89790987b1fb723db"
url: "https://pub.dev"
source: hosted
version: "0.3.0"
win32:
dependency: transitive
description:
name: win32
sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
url: "https://pub.dev"
source: hosted
version: "5.5.0"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
url: "https://pub.dev"
source: hosted
version: "1.0.4"
xml:
dependency: transitive
description:
name: xml
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
url: "https://pub.dev"
source: hosted
version: "6.5.0"
yaml:
dependency: transitive
description:
name: yaml
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
url: "https://pub.dev"
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.1.0-185.0.dev <4.0.0"
dart: ">=3.3.0 <4.0.0"
flutter: ">=3.19.2"

View file

@ -36,9 +36,17 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
http: ^1.1.0
intl: ^0.18.1
intl: ^0.19.0
flutter_secure_storage: ^9.0.0
simple_barcode_scanner: ^0.1.1
google_fonts: ^6.2.1
flex_color_scheme: ^7.3.1
blur: ^3.1.0
marquee: ^2.2.3
flutter_launcher_icons: ^0.13.1
dev_dependencies:
flutter_launcher_icons: ^0.13.1
flutter_test:
sdk: flutter
@ -47,10 +55,28 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^2.0.0
flutter_lints: ^4.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
#
flutter_launcher_icons:
android: "launcher_icon"
ios: true
image_path: "assets/logo.png"
min_sdk_android: 21 # android min sdk min:16, default 21
web:
generate: true
image_path: "assets/logo.png"
background_color: "#FF8B9DC3"
theme_color: "#FF8B9DC3"
windows:
generate: true
image_path: "assets/logo.png"
icon_size: 48 # min:48, max:256, default: 48
macos:
generate: true
image_path: "assets/logo.png"
# The following section is specific to Flutter packages.
flutter:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 458 B

After

Width:  |  Height:  |  Size: 539 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -3,8 +3,8 @@
"short_name": "fooder_web",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"background_color": "#FF8B9DC3",
"theme_color": "#FF8B9DC3",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,

View file

@ -6,6 +6,15 @@
#include "generated_plugin_registrant.h"
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <webview_windows/webview_windows_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
WebviewWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("WebviewWindowsPlugin"));
}

View file

@ -3,6 +3,9 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
flutter_secure_storage_windows
permission_handler_windows
webview_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB