Compare commits
	
		
			10 commits
		
	
	
		
			985ed670e4
			...
			ebd001e6ce
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| ebd001e6ce | |||
| bf46057179 | |||
| c716de3e02 | |||
| ac803d3e71 | |||
| 66b88f64fe | |||
| 90fad4a0ac | |||
| 34786d5de3 | |||
| 8cbdf9e7d9 | |||
| 255a7b28b9 | |||
| 
							 | 
						8b7f8b7011 | 
							
								
								
									
										27
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						| 
						 | 
				
			
			@ -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
									
								
							
							
						
						| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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
									
								
							
							
						
						| 
						 | 
				
			
			@ -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"
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-hdpi/launcher_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.9 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-mdpi/launcher_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.4 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-xhdpi/launcher_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 3.3 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								assets/logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 2.9 KiB  | 
| 
						 | 
				
			
			@ -21,6 +21,6 @@
 | 
			
		|||
  <key>CFBundleVersion</key>
 | 
			
		||||
  <string>1.0</string>
 | 
			
		||||
  <key>MinimumOSVersion</key>
 | 
			
		||||
  <string>11.0</string>
 | 
			
		||||
  <string>12.0</string>
 | 
			
		||||
</dict>
 | 
			
		||||
</plist>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
									
								
							
							
						
						| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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 = (
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<Scheme
 | 
			
		||||
   LastUpgradeVersion = "1300"
 | 
			
		||||
   LastUpgradeVersion = "1510"
 | 
			
		||||
   version = "1.3">
 | 
			
		||||
   <BuildAction
 | 
			
		||||
      parallelizeBuildables = "YES"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										3
									
								
								ios/Runner.xcworkspace/contents.xcworkspacedata
									
									
									
										generated
									
									
									
								
							
							
						
						| 
						 | 
				
			
			@ -4,4 +4,7 @@
 | 
			
		|||
   <FileRef
 | 
			
		||||
      location = "group:Runner.xcodeproj">
 | 
			
		||||
   </FileRef>
 | 
			
		||||
   <FileRef
 | 
			
		||||
      location = "group:Pods/Pods.xcodeproj">
 | 
			
		||||
   </FileRef>
 | 
			
		||||
</Workspace>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
		 Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 30 KiB  | 
| 
		 Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 674 B  | 
| 
		 Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 1.7 KiB  | 
| 
		 Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 964 B  | 
| 
		 Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 1.6 KiB  | 
| 
		 Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 1.9 KiB  | 
| 
		 Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 586 B After Width: | Height: | Size: 2 KiB  | 
| 
		 Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 2.4 KiB  | 
| 
		 After Width: | Height: | Size: 1.4 KiB  | 
| 
		 After Width: | Height: | Size: 2.2 KiB  | 
| 
		 After Width: | Height: | Size: 1.6 KiB  | 
| 
		 After Width: | Height: | Size: 2.4 KiB  | 
| 
		 Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 2.4 KiB  | 
| 
		 Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 3.3 KiB  | 
| 
		 After Width: | Height: | Size: 1.9 KiB  | 
| 
		 After Width: | Height: | Size: 2.7 KiB  | 
| 
		 Before Width: | Height: | Size: 762 B After Width: | Height: | Size: 1.9 KiB  | 
| 
		 Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 2.8 KiB  | 
| 
		 Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.9 KiB  | 
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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();
 | 
			
		||||
    }();
 | 
			
		||||
  }
 | 
			
		||||
    if (window.localStorage.containsKey('refreshToken')) {
 | 
			
		||||
      refreshToken = window.localStorage['refreshToken'];
 | 
			
		||||
 | 
			
		||||
  Future<void> loadToken() async {
 | 
			
		||||
    Map<String, String> allValues = await storage.readAll();
 | 
			
		||||
 | 
			
		||||
    if (allValues.containsKey('token')) {
 | 
			
		||||
      token = allValues['token'];
 | 
			
		||||
    }
 | 
			
		||||
    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,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										23
									
								
								lib/components/app_bar.dart
									
									
									
									
									
										Normal 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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								lib/components/blur_container.dart
									
									
									
									
									
										Normal 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!,
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								lib/components/button.dart
									
									
									
									
									
										Normal 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,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										154
									
								
								lib/components/date_picker.dart
									
									
									
									
									
										Normal 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);
 | 
			
		||||
                    }
 | 
			
		||||
                  });
 | 
			
		||||
                },
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								lib/components/dropdown.dart
									
									
									
									
									
										Normal 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),
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								lib/components/floating_action_button.dart
									
									
									
									
									
										Normal 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,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								lib/components/navigation_bar.dart
									
									
									
									
									
										Normal 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
									
								
							
							
						
						| 
						 | 
				
			
			@ -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
									
								
							
							
						
						| 
						 | 
				
			
			@ -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,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(
 | 
			
		||||
              FTextInput(
 | 
			
		||||
                labelText: 'Product name',
 | 
			
		||||
                ),
 | 
			
		||||
                controller: productNameController,
 | 
			
		||||
                onChanged: (_) => _getProducts(),
 | 
			
		||||
                onFieldSubmitted: (_) => _addEntry(),
 | 
			
		||||
                autofocus: true,
 | 
			
		||||
              ),
 | 
			
		||||
              TextFormField(
 | 
			
		||||
                decoration: const InputDecoration(
 | 
			
		||||
              FTextInput(
 | 
			
		||||
                labelText: 'Grams',
 | 
			
		||||
                ),
 | 
			
		||||
                keyboardType:
 | 
			
		||||
                    const TextInputType.numberWithOptions(decimal: true),
 | 
			
		||||
                inputFormatters: <TextInputFormatter>[
 | 
			
		||||
| 
						 | 
				
			
			@ -185,9 +209,20 @@ class _AddEntryScreen extends State<AddEntryScreen> {
 | 
			
		|||
                ),
 | 
			
		||||
            ])),
 | 
			
		||||
      ),
 | 
			
		||||
      floatingActionButton: FloatingActionButton(
 | 
			
		||||
      floatingActionButton: Row(
 | 
			
		||||
        mainAxisAlignment: MainAxisAlignment.end,
 | 
			
		||||
        children: <Widget>[
 | 
			
		||||
          FActionButton(
 | 
			
		||||
            onPressed: _findProductByBarCode,
 | 
			
		||||
            icon: Icons.photo_camera,
 | 
			
		||||
          ),
 | 
			
		||||
          const SizedBox(width: 10),
 | 
			
		||||
          FActionButton(
 | 
			
		||||
            onPressed: _addEntry,
 | 
			
		||||
        child: const Icon(Icons.add),
 | 
			
		||||
            icon: Icons.library_add,
 | 
			
		||||
            tag: "fap2",
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(
 | 
			
		||||
            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,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(
 | 
			
		||||
              FTextInput(
 | 
			
		||||
                labelText: 'Product name',
 | 
			
		||||
                ),
 | 
			
		||||
                controller: nameController,
 | 
			
		||||
                onChanged: (String value) {
 | 
			
		||||
                  setState(() {});
 | 
			
		||||
                },
 | 
			
		||||
              ),
 | 
			
		||||
              TextFormField(
 | 
			
		||||
                decoration: const InputDecoration(
 | 
			
		||||
              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(
 | 
			
		||||
              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(
 | 
			
		||||
              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(
 | 
			
		||||
              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,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(
 | 
			
		||||
              FTextInput(
 | 
			
		||||
                labelText: 'Product name',
 | 
			
		||||
                ),
 | 
			
		||||
                controller: productNameController,
 | 
			
		||||
                onChanged: (_) => _getProducts(),
 | 
			
		||||
                autofocus: true,
 | 
			
		||||
              ),
 | 
			
		||||
              TextFormField(
 | 
			
		||||
                decoration: const InputDecoration(
 | 
			
		||||
              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,
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(
 | 
			
		||||
                FTextInput(
 | 
			
		||||
                  labelText: 'Password',
 | 
			
		||||
                  ),
 | 
			
		||||
                  controller: passwordController,
 | 
			
		||||
                  onFieldSubmitted: (_) => _login(),
 | 
			
		||||
                  autofillHints: const [AutofillHints.password],
 | 
			
		||||
                  obscureText: true,
 | 
			
		||||
                ),
 | 
			
		||||
                Padding(
 | 
			
		||||
                  padding: const EdgeInsets.symmetric(vertical: 10),
 | 
			
		||||
                  child: FilledButton(
 | 
			
		||||
                FButton(
 | 
			
		||||
                  labelText: 'Sign In',
 | 
			
		||||
                  onPressed: _login,
 | 
			
		||||
                    child: const Text('Login'),
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                Padding(
 | 
			
		||||
                  padding: const EdgeInsets.symmetric(vertical: 10),
 | 
			
		||||
                  padding: const EdgeInsets.symmetric(vertical: 8),
 | 
			
		||||
                  child: TextButton(
 | 
			
		||||
                    onPressed: () {
 | 
			
		||||
                      Navigator.push(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  Future<void> _addEntry() async {
 | 
			
		||||
    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(
 | 
			
		||||
      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),
 | 
			
		||||
      );
 | 
			
		||||
      title = Row(
 | 
			
		||||
        mainAxisAlignment: MainAxisAlignment.center,
 | 
			
		||||
        children: <Widget>[
 | 
			
		||||
          TextButton(
 | 
			
		||||
            child: Text(
 | 
			
		||||
              "🅵🅾🅾🅳🅴🆁",
 | 
			
		||||
              style: logoStyle(context),
 | 
			
		||||
                refreshParent: _asyncInitState,
 | 
			
		||||
              ),
 | 
			
		||||
            onPressed: () {
 | 
			
		||||
              Navigator.pushReplacement(
 | 
			
		||||
                context,
 | 
			
		||||
                MaterialPageRoute(
 | 
			
		||||
                    builder: (context) =>
 | 
			
		||||
                        MainScreen(apiClient: widget.apiClient)),
 | 
			
		||||
              ).then((_) => _asyncInitState());
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
          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,
 | 
			
		||||
              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),
 | 
			
		||||
            ],
 | 
			
		||||
      );
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ]);
 | 
			
		||||
    } 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: 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),
 | 
			
		||||
          ),
 | 
			
		||||
      body: Center(
 | 
			
		||||
        child: content,
 | 
			
		||||
      ),
 | 
			
		||||
      floatingActionButton: FloatingActionButton(
 | 
			
		||||
        onPressed: _addEntry,
 | 
			
		||||
        child: const Icon(Icons.add),
 | 
			
		||||
          const SizedBox(width: 10),
 | 
			
		||||
          FActionButton(
 | 
			
		||||
              icon: Icons.library_add, onPressed: _addEntry, tag: "fap2"),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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),
 | 
			
		||||
              ),
 | 
			
		||||
            ]))
 | 
			
		||||
          ]),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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(
 | 
			
		||||
                FTextInput(
 | 
			
		||||
                  labelText: 'Password',
 | 
			
		||||
                  ),
 | 
			
		||||
                  controller: passwordController,
 | 
			
		||||
                  autofillHints: const [AutofillHints.password],
 | 
			
		||||
                ),
 | 
			
		||||
                TextFormField(
 | 
			
		||||
                  obscureText: true,
 | 
			
		||||
                    decoration: const InputDecoration(
 | 
			
		||||
                      labelText: 'Confirm password',
 | 
			
		||||
                ),
 | 
			
		||||
                FTextInput(
 | 
			
		||||
                  labelText: 'Confirm password',
 | 
			
		||||
                  controller: passwordConfirmController,
 | 
			
		||||
                  autofillHints: const [AutofillHints.password],
 | 
			
		||||
                    onFieldSubmitted: (_) => _register()),
 | 
			
		||||
                Padding(
 | 
			
		||||
                    padding: const EdgeInsets.symmetric(vertical: 10),
 | 
			
		||||
                    child: FilledButton(
 | 
			
		||||
                  onFieldSubmitted: (_) => _register(),
 | 
			
		||||
                  obscureText: true,
 | 
			
		||||
                ),
 | 
			
		||||
                FButton(
 | 
			
		||||
                  labelText: 'Register account',
 | 
			
		||||
                  onPressed: _register,
 | 
			
		||||
                      child: const Text('Register'),
 | 
			
		||||
                    )),
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										51
									
								
								lib/theme.dart
									
									
									
									
									
										Normal 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,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
                  ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
    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(
 | 
			
		||||
          "C: ${carb.toStringAsFixed(1)}g",
 | 
			
		||||
          style: style,
 | 
			
		||||
              element,
 | 
			
		||||
              style: Theme.of(context).textTheme.bodyLarge!.copyWith(
 | 
			
		||||
                    color: Theme.of(context).colorScheme.onSurface,
 | 
			
		||||
                  ),
 | 
			
		||||
              textAlign: TextAlign.center,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
      Expanded(
 | 
			
		||||
        flex: 1,
 | 
			
		||||
        child: Text(
 | 
			
		||||
          "F: ${fat.toStringAsFixed(1)}g",
 | 
			
		||||
          style: style,
 | 
			
		||||
          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(
 | 
			
		||||
          "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,
 | 
			
		||||
        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(
 | 
			
		||||
            "${calories!.toStringAsFixed(1)} kcal",
 | 
			
		||||
            style: style,
 | 
			
		||||
              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,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,75 +3,122 @@ 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,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ).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(
 | 
			
		||||
    return Row(
 | 
			
		||||
      children: <Widget>[
 | 
			
		||||
        Expanded(
 | 
			
		||||
          child: Padding(
 | 
			
		||||
            padding: const EdgeInsets.all(8),
 | 
			
		||||
            child: Text(
 | 
			
		||||
              meal.name,
 | 
			
		||||
                      style: Theme.of(context).textTheme.titleLarge?.copyWith(
 | 
			
		||||
                            color: Theme.of(context).colorScheme.primary,
 | 
			
		||||
              overflow: TextOverflow.fade,
 | 
			
		||||
              style: Theme.of(context).textTheme.headlineSmall!.copyWith(
 | 
			
		||||
                    color: Theme.of(context).colorScheme.onSurface,
 | 
			
		||||
                    fontWeight: FontWeight.bold,
 | 
			
		||||
                  ),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
                  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,
 | 
			
		||||
                    ),
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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"),
 | 
			
		||||
          ),
 | 
			
		||||
          children: <Widget>[
 | 
			
		||||
            for (var entry in meal.entries)
 | 
			
		||||
              ListTile(
 | 
			
		||||
                title: EntryWidget(
 | 
			
		||||
                  entry: entry,
 | 
			
		||||
          actions: <Widget>[
 | 
			
		||||
            IconButton(
 | 
			
		||||
              icon: const Icon(Icons.cancel),
 | 
			
		||||
              onPressed: () {
 | 
			
		||||
                Navigator.pop(context);
 | 
			
		||||
              },
 | 
			
		||||
            ),
 | 
			
		||||
                onTap: () {
 | 
			
		||||
                  Navigator.push(
 | 
			
		||||
            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(
 | 
			
		||||
| 
						 | 
				
			
			@ -79,14 +126,80 @@ class MealWidget extends StatelessWidget {
 | 
			
		|||
          entry: entry,
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
                  ).then((_) {
 | 
			
		||||
                    refreshParent();
 | 
			
		||||
                  });
 | 
			
		||||
                },
 | 
			
		||||
              )
 | 
			
		||||
    ).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),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
          PresetHeader(
 | 
			
		||||
            title: preset.name,
 | 
			
		||||
          ),
 | 
			
		||||
          const MacroHeaderWidget(
 | 
			
		||||
            fiber: true,
 | 
			
		||||
            calories: true,
 | 
			
		||||
            alignment: Alignment.center,
 | 
			
		||||
          ),
 | 
			
		||||
              Text("${preset.calories.toStringAsFixed(1)} kcal"),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
          MacroWidget(
 | 
			
		||||
          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,
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
          ProductHeader(
 | 
			
		||||
            title: product.name,
 | 
			
		||||
          ),
 | 
			
		||||
          const MacroHeaderWidget(
 | 
			
		||||
            fiber: true,
 | 
			
		||||
            calories: true,
 | 
			
		||||
            alignment: Alignment.center,
 | 
			
		||||
          ),
 | 
			
		||||
              Text("${product.calories.toStringAsFixed(1)} kcal"),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
          MacroWidget(
 | 
			
		||||
          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
									
								
							
							
						
						| 
						 | 
				
			
			@ -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,
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@
 | 
			
		|||
#
 | 
			
		||||
 | 
			
		||||
list(APPEND FLUTTER_PLUGIN_LIST
 | 
			
		||||
  flutter_secure_storage_linux
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
									
								
							
							
						
						| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<Scheme
 | 
			
		||||
   LastUpgradeVersion = "1430"
 | 
			
		||||
   LastUpgradeVersion = "1510"
 | 
			
		||||
   version = "1.3">
 | 
			
		||||
   <BuildAction
 | 
			
		||||
      parallelizeBuildables = "YES"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,4 +4,7 @@
 | 
			
		|||
   <FileRef
 | 
			
		||||
      location = "group:Runner.xcodeproj">
 | 
			
		||||
   </FileRef>
 | 
			
		||||
   <FileRef
 | 
			
		||||
      location = "group:Pods/Pods.xcodeproj">
 | 
			
		||||
   </FileRef>
 | 
			
		||||
</Workspace>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,68 +1,68 @@
 | 
			
		|||
{
 | 
			
		||||
  "images" : [
 | 
			
		||||
    "info": {
 | 
			
		||||
        "version": 1,
 | 
			
		||||
        "author": "xcode"
 | 
			
		||||
    },
 | 
			
		||||
    "images": [
 | 
			
		||||
        {
 | 
			
		||||
      "size" : "16x16",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_16.png",
 | 
			
		||||
      "scale" : "1x"
 | 
			
		||||
            "size": "16x16",
 | 
			
		||||
            "idiom": "mac",
 | 
			
		||||
            "filename": "app_icon_16.png",
 | 
			
		||||
            "scale": "1x"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
      "size" : "16x16",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_32.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
            "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_32.png",
 | 
			
		||||
            "scale": "1x"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
      "size" : "32x32",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_64.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
            "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_128.png",
 | 
			
		||||
            "scale": "1x"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
      "size" : "128x128",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_256.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
            "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_256.png",
 | 
			
		||||
            "scale": "1x"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
      "size" : "256x256",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_512.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
            "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_512.png",
 | 
			
		||||
            "scale": "1x"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
      "size" : "512x512",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_1024.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "info" : {
 | 
			
		||||
    "version" : 1,
 | 
			
		||||
    "author" : "xcode"
 | 
			
		||||
            "size": "512x512",
 | 
			
		||||
            "idiom": "mac",
 | 
			
		||||
            "filename": "app_icon_1024.png",
 | 
			
		||||
            "scale": "2x"
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 30 KiB  | 
| 
		 Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 2.3 KiB  | 
| 
		 Before Width: | Height: | Size: 520 B After Width: | Height: | Size: 539 B  | 
| 
		 Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 3.5 KiB  | 
| 
		 Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 943 B  | 
| 
		 Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 10 KiB  | 
| 
		 Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.5 KiB  | 
							
								
								
									
										456
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						| 
						 | 
				
			
			@ -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"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										30
									
								
								pubspec.yaml
									
									
									
									
									
								
							
							
						
						| 
						 | 
				
			
			@ -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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										
											BIN
										
									
								
								web/favicon.png
									
									
									
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 458 B After Width: | Height: | Size: 539 B  | 
| 
		 Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 3.3 KiB  | 
| 
		 Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 10 KiB  | 
| 
		 Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 3.3 KiB  | 
| 
		 Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 10 KiB  | 
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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"));
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,9 @@
 | 
			
		|||
#
 | 
			
		||||
 | 
			
		||||
list(APPEND FLUTTER_PLUGIN_LIST
 | 
			
		||||
  flutter_secure_storage_windows
 | 
			
		||||
  permission_handler_windows
 | 
			
		||||
  webview_windows
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
		 Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 1.5 KiB  |