Account Management mostly implemented

This commit is contained in:
Alexander Laevens
2022-11-25 18:50:30 -07:00
parent 35fc396050
commit 839147e0b0
175 changed files with 7508 additions and 72 deletions

44
grocery_helper/.gitignore vendored Normal file
View File

@@ -0,0 +1,44 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

45
grocery_helper/.metadata Normal file
View File

@@ -0,0 +1,45 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
version:
revision: 2921ca0c482401e557ebb20d16180e2e14e47ee9
channel: master
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 2921ca0c482401e557ebb20d16180e2e14e47ee9
base_revision: 2921ca0c482401e557ebb20d16180e2e14e47ee9
- platform: android
create_revision: 2921ca0c482401e557ebb20d16180e2e14e47ee9
base_revision: 2921ca0c482401e557ebb20d16180e2e14e47ee9
- platform: ios
create_revision: 2921ca0c482401e557ebb20d16180e2e14e47ee9
base_revision: 2921ca0c482401e557ebb20d16180e2e14e47ee9
- platform: linux
create_revision: 2921ca0c482401e557ebb20d16180e2e14e47ee9
base_revision: 2921ca0c482401e557ebb20d16180e2e14e47ee9
- platform: macos
create_revision: 2921ca0c482401e557ebb20d16180e2e14e47ee9
base_revision: 2921ca0c482401e557ebb20d16180e2e14e47ee9
- platform: web
create_revision: 2921ca0c482401e557ebb20d16180e2e14e47ee9
base_revision: 2921ca0c482401e557ebb20d16180e2e14e47ee9
- platform: windows
create_revision: 2921ca0c482401e557ebb20d16180e2e14e47ee9
base_revision: 2921ca0c482401e557ebb20d16180e2e14e47ee9
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

3
grocery_helper/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"cmake.configureOnOpen": false
}

16
grocery_helper/README.md Normal file
View File

@@ -0,0 +1,16 @@
# grocery_helper
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View File

@@ -0,0 +1,29 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

13
grocery_helper/android/.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
**/*.keystore
**/*.jks

View File

@@ -0,0 +1,72 @@
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.grocery_helper"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 18
compileSdkVersion 33
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

View File

@@ -0,0 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.grocery_helper">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@@ -0,0 +1,34 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.grocery_helper">
<application
android:label="One Trip"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>

View File

@@ -0,0 +1,6 @@
package com.example.grocery_helper
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -0,0 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.grocery_helper">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@@ -0,0 +1,31 @@
buildscript {
ext.kotlin_version = '1.7.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip

View File

@@ -0,0 +1,11 @@
include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"

View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="512"
height="512"
viewBox="0 0 512 512"
version="1.1"
id="svg5"
sodipodi:docname="android.svg"
inkscape:version="1.2.1 (8a69933317, 2022-10-28, custom)"
inkscape:export-filename="../f2688c88/icon.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="1.4142136"
inkscape:cx="185.96908"
inkscape:cy="224.85996"
inkscape:window-width="1920"
inkscape:window-height="1003"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer2"
showguides="false">
<inkscape:grid
type="xygrid"
id="grid312"
spacingx="16"
spacingy="16"
empspacing="4" />
</sodipodi:namedview>
<defs
id="defs2" />
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Layer 2">
<rect
style="fill:#10599e;stroke:#e5e5e5;stroke-width:0;stroke-linecap:square;stroke-linejoin:miter;stroke-dasharray:none;fill-opacity:1"
id="rect421"
width="512"
height="512"
x="0"
y="0"
ry="0" />
<path
style="fill:#020202;fill-opacity:0.22088718;stroke:#e5e5e5;stroke-width:0;stroke-linecap:square;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 112.42105,396.37248 192.86499,512 H 512 V 382.84435 L 432,229.63563 H 98.234154 Z"
id="path5195" />
</g>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
id="path5148"
style="fill:#e5e5e5;fill-opacity:1;stroke:#e5e5e5;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="Basket"
d="M 89.263157,243.53037 112.42105,396.37248 H 279.1579 399.57894 l 23.15788,-152.8421 z m 13.894733,23.15789 h 13.89473 l 18.52632,115.78947 H 121.6842 Z m 32.42105,0 h 13.89474 l 13.89474,115.78947 h -13.89474 z m 32.42106,0 h 13.89474 l 1.11085,13.89473 h -13.89474 z m 32.42105,0 h 13.89473 l 0.55543,13.89473 h -13.89473 z m 32.42105,0 h 13.89473 v 13.89473 H 232.8421 Z m 32.42105,0 h 13.89475 v 13.89473 h -13.89475 z m 32.42106,0 h 13.89473 l -0.55542,13.89473 h -13.89474 z m 32.42104,0 h 13.89474 l -1.11085,13.89473 H 328.9944 Z m 32.42105,0 h 13.89475 L 362.5263,382.47773 h -13.89474 z m 32.42107,0 h 13.89473 l -18.52631,115.78947 h -13.89474 z m -225.09474,23.15789 h 13.89474 17.6 13.89474 l 2.77894,69.47369 h -13.89474 -14.82104 -13.89474 z m 62.98947,0 h 13.89473 l 18.52632,37.05262 v -37.05262 h 13.89475 v 69.47369 H 265.26315 L 246.73683,322.2672 v 37.05264 H 232.8421 Z m 63.91578,0 h 13.89474 28.71579 2.77895 l -1.11085,13.89473 h -13.89474 -0.27862 -16.76595 l -0.55543,13.89474 h 16.11646 l -1.11087,13.89474 h -15.56281 l -0.55543,13.89475 h 15.19194 0.18453 13.89473 l -1.11086,13.89473 h -1.85262 -26.86316 -13.89474 z m -111.89966,13.89473 3.33438,41.68423 h 15.37828 l -1.66808,-41.68423 z m -8.70592,64.84211 h 13.89474 l 1.11085,13.89474 h -13.89473 z m 28.3449,0 h 13.89474 l 0.55542,13.89474 h -13.89473 z m 28.3449,0 h 13.89473 v 13.89474 H 232.8421 Z m 32.42105,0 h 13.89475 v 13.89474 h -13.89475 z m 28.34491,0 h 13.89473 l -0.55543,13.89474 h -13.89473 z m 28.34489,0 h 13.89473 l -1.11085,13.89474 H 320.8421 Z" />
<path
style="fill:#111111;fill-opacity:1;stroke:#e5e5e5;stroke-width:18.5263;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 163.36843,238.89881 46.31578,-111.1579"
id="path10618"
inkscape:label="LHandle" />
<path
style="fill:#111111;fill-opacity:1;stroke:#e5e5e5;stroke-width:18.5263;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 348.63158,238.89881 302.3158,127.74091"
id="path10620"
inkscape:label="RHandle" />
<path
style="fill:#898989;fill-opacity:1;stroke:#233459;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 80,252.79353 v -23.1579 h 352 v 23.1579 z"
id="path8930"
sodipodi:nodetypes="ccccc"
inkscape:label="Rim" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="400"
height="400"
viewBox="0 0 105.83333 105.83333"
version="1.1"
id="svg5"
inkscape:version="1.2.1 (8a69933317, 2022-10-28, custom)"
sodipodi:docname="base.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="2.8284271"
inkscape:cx="111.01576"
inkscape:cy="189.50462"
inkscape:window-width="1920"
inkscape:window-height="1003"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid2501"
spacingx="1.3229166"
spacingy="1.3229166"
originx="0"
originy="0" />
</sodipodi:namedview>
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:#292929;fill-opacity:1;stroke:#292929;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 59.531252,103.1875 34.395831,0 6.614587,-43.65625 H 5.2916667 L 11.90625,103.1875 Z"
id="path5148"
sodipodi:nodetypes="cccccc"
inkscape:label="Basket" />
<path
id="path10336"
style="display:inline;fill:#111111;fill-opacity:1;stroke:#111111;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 9.2604164 66.145831 L 14.552083 99.218747 L 18.520833 99.218747 L 13.229166 66.145831 L 9.2604164 66.145831 z M 18.520833 66.145831 L 22.489583 99.218747 L 26.458332 99.218747 L 22.489583 66.145831 L 18.520833 66.145831 z M 27.781249 66.145831 L 28.098542 70.114581 L 32.067292 70.114581 L 31.749999 66.145831 L 27.781249 66.145831 z M 37.041666 66.145831 L 37.200312 70.114581 L 41.169062 70.114581 L 41.010415 66.145831 L 37.041666 66.145831 z M 46.302082 66.145831 L 46.302082 70.114581 L 50.270832 70.114581 L 50.270832 66.145831 L 46.302082 66.145831 z M 55.562498 66.145831 L 55.562498 70.114581 L 59.531248 70.114581 L 59.531248 66.145831 L 55.562498 66.145831 z M 64.822915 66.145831 L 64.664268 70.114581 L 68.633018 70.114581 L 68.791664 66.145831 L 64.822915 66.145831 z M 74.083331 66.145831 L 73.766038 70.114581 L 77.734788 70.114581 L 78.052081 66.145831 L 74.083331 66.145831 z M 83.343747 66.145831 L 79.374997 99.218747 L 83.343747 99.218747 L 87.312497 66.145831 L 83.343747 66.145831 z M 92.604164 66.145831 L 87.312497 99.218747 L 91.281247 99.218747 L 96.572914 66.145831 L 92.604164 66.145831 z M 30.109789 95.249997 L 30.427082 99.218747 L 34.395832 99.218747 L 34.078539 95.249997 L 30.109789 95.249997 z M 38.205935 95.249997 L 38.364582 99.218747 L 42.333332 99.218747 L 42.174685 95.249997 L 38.205935 95.249997 z M 46.302082 95.249997 L 46.302082 99.218747 L 50.270832 99.218747 L 50.270832 95.249997 L 46.302082 95.249997 z M 55.562498 95.249997 L 55.562498 99.218747 L 59.531248 99.218747 L 59.531248 95.249997 L 55.562498 95.249997 z M 63.658645 95.249997 L 63.499998 99.218747 L 67.468748 99.218747 L 67.627395 95.249997 L 63.658645 95.249997 z M 71.754791 95.249997 L 71.437498 99.218747 L 75.406248 99.218747 L 75.723541 95.249997 L 71.754791 95.249997 z "
inkscape:label="extra holes" />
<path
id="path15900"
style="fill:#111111;fill-opacity:1;stroke:#111111;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 64.558331 72.760414 L 63.764581 92.604164 L 67.733331 92.604164 L 75.406248 92.604164 L 75.935414 92.604164 L 76.252708 88.635414 L 72.283958 88.635414 L 72.231248 88.635414 L 67.891978 88.635414 L 68.050624 84.666664 L 72.495831 84.666664 L 72.813124 80.697914 L 68.209788 80.697914 L 68.368435 76.729164 L 73.157289 76.729164 L 73.236871 76.729164 L 77.205621 76.729164 L 77.522914 72.760414 L 76.729164 72.760414 L 68.527081 72.760414 L 64.558331 72.760414 z "
inkscape:label="E" />
<path
style="fill:#111111;fill-opacity:1;stroke:#111111;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 46.302082,72.760414 v 19.84375 h 3.96875 V 82.020831 l 5.291666,10.583333 h 3.96875 v -19.84375 h -3.96875 V 83.343747 L 50.270832,72.760414 Z"
id="path15898"
inkscape:label="N" />
<path
style="fill:#111111;fill-opacity:1;stroke:#111111;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 28.310416,72.760414 1.5875,19.84375 h 3.96875 4.233333 3.96875 l -0.79375,-19.84375 h -3.96875 -5.027083 z m 4.286043,3.96875 h 4.868436 l 0.476457,11.90625 h -4.392496 z"
id="path15896"
inkscape:label="O" />
<path
style="fill:#111111;fill-opacity:1;stroke:#292929;stroke-width:5.2916665;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 26.458333,58.208335 39.6875,26.458333"
id="path10618"
inkscape:label="LHandle" />
<path
style="fill:#111111;fill-opacity:1;stroke:#292929;stroke-width:5.2916665;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 79.375002,58.208335 66.145835,26.458333"
id="path10620"
inkscape:label="RHandle" />
<path
style="fill:#111111;fill-opacity:1;stroke:#233459;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 2.6458333,62.177083 0,-6.614583 H 103.1875 v 6.614583 z"
id="path8930"
sodipodi:nodetypes="ccccc"
inkscape:label="Rim" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="400"
height="400"
viewBox="0 0 105.83333 105.83333"
version="1.1"
id="svg5"
sodipodi:docname="holes-dark.svg"
inkscape:version="1.2.1 (8a69933317, 2022-10-28, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#000000"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="2"
inkscape:cx="205.75"
inkscape:cy="203"
inkscape:window-width="1920"
inkscape:window-height="1003"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid2501"
spacingx="1.3229166"
spacingy="1.3229166"
originx="0"
originy="0" />
</sodipodi:namedview>
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
id="path5148"
style="fill:#e5e5e5;fill-opacity:1;stroke:#292929;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="Basket"
d="M 5.2916665,49.222674 11.90625,92.878926 H 59.531248 93.92708 l 6.61458,-43.656252 z m 3.9687499,6.614583 h 3.9687496 l 5.291667,33.072916 h -3.96875 z m 9.2604166,0 h 3.96875 l 3.968749,33.072916 h -3.968749 z m 9.260416,0 h 3.96875 l 0.317293,3.96875 h -3.96875 z m 9.260417,0 h 3.968749 l 0.158647,3.96875 h -3.96875 z m 9.260416,0 h 3.96875 v 3.96875 h -3.96875 z m 9.260416,0 h 3.96875 v 3.96875 h -3.96875 z m 9.260417,0 h 3.968749 l -0.158646,3.96875 h -3.96875 z m 9.260416,0 h 3.96875 l -0.317293,3.96875 h -3.96875 z m 9.260416,0 h 3.96875 l -3.96875,33.072916 h -3.96875 z m 9.260417,0 h 3.96875 l -5.291667,33.072916 h -3.96875 z M 28.310416,62.45184 h 3.96875 5.027083 3.96875 l 0.79375,19.84375 h -3.96875 -4.233333 -3.96875 z m 17.991666,0 h 3.96875 l 5.291666,10.583333 V 62.45184 h 3.96875 v 19.84375 h -3.96875 L 50.270832,71.712257 V 82.29559 h -3.96875 z m 18.256249,0 h 3.96875 8.202083 0.79375 l -0.317293,3.96875 h -3.96875 -0.07958 -4.788854 l -0.158647,3.96875 h 4.603336 l -0.317293,3.96875 h -4.445207 l -0.158646,3.96875 h 4.33927 0.05271 3.96875 l -0.317294,3.96875 h -0.529166 -7.672917 -3.96875 z m -31.961872,3.96875 0.952397,11.90625 h 4.392496 L 37.464895,66.42059 Z m -2.48667,18.520833 h 3.96875 l 0.317293,3.96875 h -3.96875 z m 8.096146,0 h 3.96875 l 0.158647,3.96875 h -3.96875 z m 8.096147,0 h 3.96875 v 3.96875 h -3.96875 z m 9.260416,0 h 3.96875 v 3.96875 h -3.96875 z m 8.096147,0 h 3.96875 l -0.158647,3.96875 h -3.96875 z m 8.096146,0 h 3.96875 l -0.317293,3.96875 h -3.96875 z" />
<path
style="fill:#111111;fill-opacity:1;stroke:#e5e5e5;stroke-width:5.2916665;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 26.458333,47.899761 39.6875,16.149759"
id="path10618"
inkscape:label="LHandle" />
<path
style="fill:#111111;fill-opacity:1;stroke:#e5e5e5;stroke-width:5.2916665;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 79.375002,47.899761 66.145835,16.149759"
id="path10620"
inkscape:label="RHandle" />
<path
style="fill:#898989;fill-opacity:1;stroke:#233459;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 2.6458333,51.868509 V 45.253926 H 103.1875 v 6.614583 z"
id="path8930"
sodipodi:nodetypes="ccccc"
inkscape:label="Rim" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="400"
height="400"
viewBox="0 0 105.83333 105.83333"
version="1.1"
id="svg5"
sodipodi:docname="holes.svg"
inkscape:version="1.2.1 (8a69933317, 2022-10-28, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="2"
inkscape:cx="221.5"
inkscape:cy="203"
inkscape:window-width="1920"
inkscape:window-height="1003"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
type="xygrid"
id="grid2501"
spacingx="1.3229166"
spacingy="1.3229166"
originx="0"
originy="0" />
</sodipodi:namedview>
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
id="path5148"
style="fill:#292929;fill-opacity:1;stroke:#292929;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
inkscape:label="Basket"
d="M 5.2916665,49.222674 11.90625,92.878926 H 59.531248 93.92708 l 6.61458,-43.656252 z m 3.9687499,6.614583 h 3.9687496 l 5.291667,33.072916 h -3.96875 z m 9.2604166,0 h 3.96875 l 3.968749,33.072916 h -3.968749 z m 9.260416,0 h 3.96875 l 0.317293,3.96875 h -3.96875 z m 9.260417,0 h 3.968749 l 0.158647,3.96875 h -3.96875 z m 9.260416,0 h 3.96875 v 3.96875 h -3.96875 z m 9.260416,0 h 3.96875 v 3.96875 h -3.96875 z m 9.260417,0 h 3.968749 l -0.158646,3.96875 h -3.96875 z m 9.260416,0 h 3.96875 l -0.317293,3.96875 h -3.96875 z m 9.260416,0 h 3.96875 l -3.96875,33.072916 h -3.96875 z m 9.260417,0 h 3.96875 l -5.291667,33.072916 h -3.96875 z M 28.310416,62.45184 h 3.96875 5.027083 3.96875 l 0.79375,19.84375 h -3.96875 -4.233333 -3.96875 z m 17.991666,0 h 3.96875 l 5.291666,10.583333 V 62.45184 h 3.96875 v 19.84375 h -3.96875 L 50.270832,71.712257 V 82.29559 h -3.96875 z m 18.256249,0 h 3.96875 8.202083 0.79375 l -0.317293,3.96875 h -3.96875 -0.07958 -4.788854 l -0.158647,3.96875 h 4.603336 l -0.317293,3.96875 h -4.445207 l -0.158646,3.96875 h 4.33927 0.05271 3.96875 l -0.317294,3.96875 h -0.529166 -7.672917 -3.96875 z m -31.961872,3.96875 0.952397,11.90625 h 4.392496 L 37.464895,66.42059 Z m -2.48667,18.520833 h 3.96875 l 0.317293,3.96875 h -3.96875 z m 8.096146,0 h 3.96875 l 0.158647,3.96875 h -3.96875 z m 8.096147,0 h 3.96875 v 3.96875 h -3.96875 z m 9.260416,0 h 3.96875 v 3.96875 h -3.96875 z m 8.096147,0 h 3.96875 l -0.158647,3.96875 h -3.96875 z m 8.096146,0 h 3.96875 l -0.317293,3.96875 h -3.96875 z" />
<path
style="fill:#111111;fill-opacity:1;stroke:#292929;stroke-width:5.29167;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 26.458333,47.899761 39.6875,16.149759"
id="path10618"
inkscape:label="LHandle" />
<path
style="fill:#111111;fill-opacity:1;stroke:#292929;stroke-width:5.29167;stroke-linecap:square;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 79.375002,47.899761 66.145835,16.149759"
id="path10620"
inkscape:label="RHandle" />
<path
style="fill:#111111;fill-opacity:1;stroke:#233459;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 2.6458333,51.868509 V 45.253926 H 103.1875 v 6.614583 z"
id="path8930"
sodipodi:nodetypes="ccccc"
inkscape:label="Rim" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M24 23.95q-3.3 0-5.4-2.1-2.1-2.1-2.1-5.4 0-3.3 2.1-5.4 2.1-2.1 5.4-2.1 3.3 0 5.4 2.1 2.1 2.1 2.1 5.4 0 3.3-2.1 5.4-2.1 2.1-5.4 2.1ZM8 40v-4.7q0-1.9.95-3.25T11.4 30q3.35-1.5 6.425-2.25Q20.9 27 24 27q3.1 0 6.15.775 3.05.775 6.4 2.225 1.55.7 2.5 2.05.95 1.35.95 3.25V40Zm3-3h26v-1.7q0-.8-.475-1.525-.475-.725-1.175-1.075-3.2-1.55-5.85-2.125Q26.85 30 24 30t-5.55.575q-2.7.575-5.85 2.125-.7.35-1.15 1.075Q11 34.5 11 35.3Zm13-16.05q1.95 0 3.225-1.275Q28.5 18.4 28.5 16.45q0-1.95-1.275-3.225Q25.95 11.95 24 11.95q-1.95 0-3.225 1.275Q19.5 14.5 19.5 16.45q0 1.95 1.275 3.225Q22.05 20.95 24 20.95Zm0-4.5ZM24 37Z"/></svg>

After

Width:  |  Height:  |  Size: 682 B

34
grocery_helper/ios/.gitignore vendored Normal file
View File

@@ -0,0 +1,34 @@
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
</dict>
</plist>

View File

@@ -0,0 +1 @@
#include "Generated.xcconfig"

View File

@@ -0,0 +1 @@
#include "Generated.xcconfig"

View File

@@ -0,0 +1,483 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
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 */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
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>"; };
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>"; };
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>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
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>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.groceryHelper;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.groceryHelper;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.groceryHelper;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@@ -0,0 +1,13 @@
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}

View File

@@ -0,0 +1,122 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 624 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 981 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View File

@@ -0,0 +1,5 @@
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>

View File

@@ -0,0 +1,26 @@
<?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">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<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"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>One Trip</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>grocery_helper</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>So you may take a photo for your profile picture</string>
<string>
</dict>
</plist>

View File

@@ -0,0 +1 @@
#import "GeneratedPluginRegistrant.h"

View File

@@ -0,0 +1,86 @@
import 'dart:convert';
import 'consts.dart';
import 'package:http/http.dart' as http;
class TokenSingleton {
static final TokenSingleton _instance = TokenSingleton._internal();
String token = "";
factory TokenSingleton() {
return _instance;
}
void setToken(String tok) {
token = tok;
}
String getToken() {
return token;
}
TokenSingleton._internal();
}
Future<String> getToken(String username, String password) async {
const String requestURL = "$baseURL/auth/token";
final http.Response response = await http.post(Uri.parse(requestURL),
body: {"username": username, "password": password});
if (response.statusCode == 200) {
Map<String, dynamic> json = jsonDecode(response.body);
final TokenSingleton s = TokenSingleton();
s.setToken(json["token"]);
return json["token"];
} else {
return "";
}
}
Future<bool> testToken(String token) async {
const String requestURL = "$baseURL/auth/users/me";
final http.Response response = await http.get(
Uri.parse(requestURL),
headers: {
"Authorization": "Token $token",
},
);
return response.statusCode == 200;
}
Future<String> signup(
String firstName,
String lastName,
String username,
String password,
) async {
const String requestURL = "$baseURL/auth/users/";
final http.Response response = await http.post(
Uri.parse(requestURL),
body: {
"first_name": firstName,
"last_name": lastName,
"username": username,
"password": password,
},
);
if (response.statusCode == 201) {
return "";
}
Map<String, dynamic> errorBody = jsonDecode(response.body);
List<String> errors = [];
errorBody.forEach((key, value) {
errors.add("$key: ${value[0]}");
});
return errors.join(", ");
}

View File

@@ -0,0 +1,3 @@
const String baseURL = "http://192.168.0.16:8000";
const int resultsPerPage = 4;

View File

@@ -0,0 +1,72 @@
import 'dart:convert';
import 'package:grocery_helper/api/auth.dart';
import 'package:grocery_helper/api/consts.dart';
import 'package:http/http.dart' as http;
class Homegroup {
int id;
String name;
List<int> recipes;
List<int> users;
List<int> inviteIDs;
Homegroup({
required this.id,
required this.name,
required this.recipes,
required this.users,
required this.inviteIDs,
});
factory Homegroup.fromJson(Map<String, dynamic> json) {
List<int> recipes =
(json["recipes"] as List<dynamic>).map(((e) => e as int)).toList();
List<int> users =
(json["users"] as List<dynamic>).map(((e) => e as int)).toList();
List<int> inviteIDs =
(json["invites"] as List<dynamic>).map(((e) => e as int)).toList();
return Homegroup(
id: json["id"] as int,
name: json["name"] as String,
recipes: recipes,
users: users,
inviteIDs: inviteIDs,
);
}
static Future<Homegroup?> fetchHomegroup(int id) async {
String requestURL = "$baseURL/api/homegroups/$id/";
String token = TokenSingleton().getToken();
final http.Response response = await http.get(
Uri.parse(requestURL),
headers: {"Authorization": "Token $token"},
);
if (response.statusCode == 200) {
Homegroup hg = Homegroup.fromJson(jsonDecode(response.body));
return hg;
} else {
return null;
}
}
static Future<Homegroup?> createHomegroup(String title) async {
String requestURL = "$baseURL/api/homegroups/";
String token = TokenSingleton().getToken();
final http.Response response = await http.post(
Uri.parse(requestURL),
headers: {"Authorization": "Token $token"},
body: {"name": title},
);
if (response.statusCode == 201) {
Homegroup hg = Homegroup.fromJson(jsonDecode(response.body));
return hg;
}
return null;
}
}

View File

@@ -0,0 +1,71 @@
import 'dart:convert';
import 'package:grocery_helper/api/auth.dart';
import 'package:grocery_helper/api/consts.dart';
import 'package:http/http.dart' as http;
class HomegroupInvite {
int id;
int homegroupID;
int userID;
HomegroupInvite({
required this.id,
required this.homegroupID,
required this.userID,
});
factory HomegroupInvite.fromJson(Map<String, dynamic> json) {
return HomegroupInvite(
id: json["id"] as int,
homegroupID: json["homegroup"] as int,
userID: json["user"] as int,
);
}
static Future<HomegroupInvite?> get(int id) async {
String requestURL = "$baseURL/api/groupinvites/$id/";
String token = TokenSingleton().getToken();
final http.Response response = await http.get(
Uri.parse(requestURL),
headers: {"Authorization": "Token $token"},
);
if (response.statusCode == 200) {
return HomegroupInvite.fromJson(jsonDecode(response.body));
}
return null;
}
static Future<HomegroupInvite?> createInvite(
int homegroupID, int userID) async {
String requestURL = "$baseURL/api/groupinvites/";
String token = TokenSingleton().getToken();
final http.Response response = await http.post(
Uri.parse(requestURL),
headers: {"Authorization": "Token $token"},
body: {"homegroup": "$homegroupID", "user": "$userID"},
);
if (response.statusCode == 201) {
return HomegroupInvite.fromJson(jsonDecode(response.body));
}
return null;
}
Future<bool> deleteInvite() async {
String requestURL = "$baseURL/api/groupinvites/$id/";
String token = TokenSingleton().getToken();
final http.Response response = await http.delete(
Uri.parse(requestURL),
headers: {"Authorization": "Token $token"},
);
if (response.statusCode == 204) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,28 @@
import 'package:grocery_helper/api/auth.dart';
import 'package:grocery_helper/api/consts.dart';
import 'package:http/http.dart' as http;
class Ingredient {
int id;
String name;
bool inStock;
int contentType;
int objectID;
Ingredient({
required this.id,
required this.name,
required this.inStock,
required this.contentType,
required this.objectID,
});
factory Ingredient.fromJson(Map<String, dynamic> json) {
return Ingredient(
id: json["id"] as int,
name: json["name"] as String,
inStock: json["in_stock"] as bool,
contentType: json["content_type"] as int,
objectID: json["object_id"] as int);
}
}

View File

@@ -0,0 +1,59 @@
import 'dart:convert';
import 'package:grocery_helper/api/auth.dart';
import 'package:grocery_helper/api/consts.dart';
import 'package:grocery_helper/api/models/homegroup.dart';
import 'package:grocery_helper/api/models/ingredient.dart';
import 'package:http/http.dart' as http;
class Recipe {
int id;
String name;
List<Ingredient> ingredients;
Recipe({required this.id, required this.name, required this.ingredients});
factory Recipe.fromJson(Map<String, dynamic> json) {
List<Ingredient> ingredients = [];
for (dynamic ingredient in json["ingredients"]) {
ingredients.add(Ingredient.fromJson(ingredient));
}
return Recipe(
id: json["id"] as int,
name: json["name"] as String,
ingredients: ingredients);
}
static Future<Recipe?> fetch(int id) async {
String requestURL = "$baseURL/api/recipes/$id/";
String token = TokenSingleton().getToken();
final http.Response response = await http.get(
Uri.parse(requestURL),
headers: {"Authorization": "Token $token"},
);
if (response.statusCode == 200) {
return Recipe.fromJson(jsonDecode(response.body));
}
return null;
}
static Future<List<Recipe>> fetchList(int groupID) async {
Homegroup? group = await Homegroup.fetchHomegroup(groupID);
if (group == null) {
return [];
}
List<Recipe> recipes = [];
for (int recipeID in group.recipes) {
Recipe? recipe = await Recipe.fetch(recipeID);
if (recipe != null) {
recipes.add(recipe);
}
}
return recipes;
}
}

View File

@@ -0,0 +1,89 @@
import 'dart:convert';
import 'package:grocery_helper/api/auth.dart';
import 'package:grocery_helper/api/consts.dart';
import 'package:http/http.dart' as http;
class SearchResult {
List<SimpleUser> users;
String? next;
SearchResult({required this.users, required this.next});
}
class SimpleUser {
int id;
String username;
String firstName;
String lastName;
String? imageUrl;
int? invite;
SimpleUser({
required this.id,
required this.username,
required this.firstName,
required this.lastName,
this.imageUrl,
});
factory SimpleUser.fromJson(Map<String, dynamic> json) {
return SimpleUser(
id: json["id"] as int,
username: json["username"] as String,
firstName: json["first_name"] as String,
lastName: json["last_name"] as String,
imageUrl: json["image"] as String?,
);
}
static Future<SimpleUser?> fetchUser({int? id}) async {
String requestURL = "$baseURL/auth/users/${id ?? 'me'}";
String token = TokenSingleton().getToken();
final http.Response response = await http.get(
Uri.parse(requestURL),
headers: {"Authorization": "Token $token"},
);
if (response.statusCode == 200) {
SimpleUser u = SimpleUser.fromJson(jsonDecode(response.body));
return u;
} else {
return null;
}
}
static Future<SearchResult> search(String query, int page) async {
// String requestURL = "";
// if (url != null) {
// requestURL = url;
// } else if (query != null) {
// requestURL = "$baseURL/auth/users/?search=$query";
// } else {
// return SearchResult(users: [], next: null);
// }
String requestURL = "$baseURL/auth/users/?page=$page&search=$query";
requestURL = requestURL.replaceAll(RegExp(r"\s+"), "+");
String token = TokenSingleton().getToken();
final http.Response response = await http.get(
Uri.parse(requestURL),
headers: {"Authorization": "Token $token"},
);
if (response.statusCode == 200) {
Map<String, dynamic> json = jsonDecode(response.body);
List<SimpleUser> users = [];
for (var userObject in json["results"]) {
SimpleUser u = SimpleUser.fromJson(userObject);
users.add(u);
}
return SearchResult(users: users, next: json["next"] as String?);
}
return SearchResult(users: [], next: null);
}
}

View File

@@ -0,0 +1,115 @@
import 'dart:convert';
import 'package:grocery_helper/api/auth.dart';
import 'package:grocery_helper/api/consts.dart';
import 'package:http/http.dart' as http;
import 'dart:io' show File;
class User {
int id;
String username;
String firstName;
String lastName;
List<int> homegroupInvites;
int? homegroup;
String? imageUrl;
User({
required this.id,
required this.username,
required this.firstName,
required this.lastName,
required this.homegroupInvites,
this.homegroup,
this.imageUrl,
});
factory User.fromJson(Map<String, dynamic> json) {
List<dynamic> invitesDynamic = json["homegroup_invites"];
List<int> invites = invitesDynamic.map((e) => e as int).toList();
return User(
id: json["id"] as int,
username: json["username"] as String,
firstName: json["first_name"] as String,
lastName: json["last_name"] as String,
homegroup: json["homegroup"] as int?,
imageUrl: json["image"] as String?,
homegroupInvites: invites,
);
}
static Future<User?> fetchUser() async {
String requestURL = "$baseURL/auth/users/me";
String token = TokenSingleton().getToken();
final http.Response response =
await http.get(Uri.parse(requestURL), headers: {
"Authorization": "Token $token",
});
if (response.statusCode == 200) {
User u = User.fromJson(jsonDecode(response.body));
return u;
} else {
return null;
}
}
Future<User?> patch({
String? firstName,
String? lastName,
int? homegroup,
}) async {
String requestURL = "$baseURL/auth/users/me";
Map<String, String> body = {};
if (firstName != null) {
body["first_name"] = firstName;
}
if (lastName != null) {
body["last_name"] = lastName;
}
if (homegroup != null) {
body["homegroup"] = "$homegroup";
}
String token = TokenSingleton().getToken();
final http.Response response = await http.patch(
Uri.parse(requestURL),
headers: {
"Authorization": "Token $token",
},
body: body,
);
if (response.statusCode == 200) {
return User.fromJson(jsonDecode(response.body));
}
return null;
}
Future<User?> uploadImage(File file) async {
String requestURL = "$baseURL/auth/users/me";
String token = TokenSingleton().getToken();
http.MultipartRequest request =
http.MultipartRequest("PATCH", Uri.parse(requestURL));
request.headers.addAll({"Authorization": "Token $token"});
request.files.add(http.MultipartFile.fromBytes(
"image", file.readAsBytesSync(),
filename: file.path));
var multiresponse = await request.send();
http.Response response = await http.Response.fromStream(multiresponse);
if (response.statusCode == 200) {
return User.fromJson(jsonDecode(response.body));
}
return null;
}
}

View File

@@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:grocery_helper/screens/home_screen.dart';
import 'package:grocery_helper/screens/login_screen.dart';
import 'package:grocery_helper/theme.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Grocery Helper',
theme: lightTheme,
darkTheme: darkTheme,
themeMode: ThemeMode.system,
debugShowCheckedModeBanner: false,
initialRoute: "/login",
routes: {
"/login": (context) => const LoginScreen(),
"/home": (context) => ScrollConfiguration(
behavior: MyBehavior(), child: const HomeScreen())
},
);
}
}

View File

@@ -0,0 +1,165 @@
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:grocery_helper/api/models/homegroup.dart';
import 'package:grocery_helper/widgets/text_entry_dialog.dart';
import 'package:grocery_helper/pages/profile_page/widgets/homegroup_card_widget.dart';
import 'package:grocery_helper/pages/profile_page/widgets/profile_card_widget.dart';
import 'package:grocery_helper/api/models/user.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io' show Platform, File;
class ProfilePage extends StatefulWidget {
const ProfilePage({super.key});
@override
State<ProfilePage> createState() => _ProfilePageState();
}
class _ProfilePageState extends State<ProfilePage> {
late User _userInfo;
late Future<bool> _isLoaded;
Future<bool> _loadProfile() async {
User? userInfo = await User.fetchUser();
if (userInfo != null) {
_userInfo = userInfo;
return true;
}
return false;
}
void showError(String text) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Theme.of(context).colorScheme.error,
content: Text(text),
action: SnackBarAction(
textColor: Theme.of(context).colorScheme.onError,
label: "Dismiss",
onPressed: () => ScaffoldMessenger.of(context).hideCurrentSnackBar(),
),
),
);
}
@override
void initState() {
super.initState();
_isLoaded = _loadProfile();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _isLoaded,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text(snapshot.error.toString());
} else if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return Scrollbar(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
ProfileCard(
userInfo: _userInfo,
onTapPhoto: () async {
if (Platform.isAndroid) {
final ImagePicker picker = ImagePicker();
final XFile? photo = await picker.pickImage(
source: ImageSource.camera,
imageQuality: 70,
maxWidth: 400,
maxHeight: 400);
if (photo == null) {
return;
}
File file = File(photo.path);
User? response = await _userInfo.uploadImage(file);
if (response != null) {
setState(() {
_userInfo = response;
});
}
} else {
showError(
"This feature is only available on Android");
}
},
onLogout: () async {
const storage = FlutterSecureStorage();
await storage.delete(key: "token");
if (mounted) {
Navigator.pushReplacementNamed(context, "/login");
}
},
),
const SizedBox(height: 12),
_userInfo.homegroup == null
? CreateJoinHomegroup(
invites: _userInfo.homegroupInvites,
onJoin: (id) async {
User? response =
await _userInfo.patch(homegroup: id);
if (response != null) {
setState(() {
_userInfo = response;
});
}
},
onCreate: () async {
String? name = await createHomegroupDialog(
context,
"Create Homegroup",
"Homegroup Name",
defaultValue: "${_userInfo.username}'s Kitchen",
);
if (name == null) {
return;
}
if (name == "") {
showError("Homegroup name must not be empty");
return;
}
Homegroup? hg =
await Homegroup.createHomegroup(name);
if (hg == null) {
return;
}
User? response =
await _userInfo.patch(homegroup: hg.id);
if (response != null) {
setState(() {
_userInfo = response;
});
}
},
)
: EditHomegroup(
homegroupID: _userInfo.homegroup!,
)
],
),
),
),
);
} else {
return const CircularProgressIndicator();
}
},
);
}
}

View File

@@ -0,0 +1,360 @@
import 'package:flutter/material.dart';
import 'package:grocery_helper/api/models/homegroup.dart';
import 'package:grocery_helper/api/models/homegroupinvite.dart';
import 'package:grocery_helper/api/models/simpleuser.dart';
import 'package:grocery_helper/pages/profile_page/widgets/invite_homegroup_dialog.dart';
import 'package:grocery_helper/pages/profile_page/widgets/user_chip.dart';
import 'package:grocery_helper/theme.dart';
class CreateJoinHomegroup extends StatefulWidget {
final List<int> invites;
final Function(int id) onJoin;
final Function() onCreate;
const CreateJoinHomegroup({
super.key,
required this.invites,
required this.onJoin,
required this.onCreate,
});
@override
State<CreateJoinHomegroup> createState() => _CreateJoinHomegroupState();
}
class _CreateJoinHomegroupState extends State<CreateJoinHomegroup> {
late Future<bool> _isLoaded;
final List<Homegroup> _invitedGroups = [];
Future<bool> _loadInvites() async {
for (int id in widget.invites) {
Homegroup? hg = await Homegroup.fetchHomegroup(id);
if (hg != null) {
_invitedGroups.add(hg);
}
}
return true;
}
@override
void initState() {
super.initState();
_isLoaded = _loadInvites();
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(
elevation: 10,
margin: EdgeInsets.zero,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(10), bottom: Radius.zero),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Create or Join a Homegroup",
style: Theme.of(context).textTheme.titleMedium,
),
const Divider(),
widget.invites.isEmpty
? const Text(
"You have not been invited to join any homegroups")
: Center(
child: FutureBuilder(
future: _isLoaded,
builder: ((context, snapshot) {
if (snapshot.hasError) {
return Text(snapshot.error.toString());
} else if (snapshot.hasData &&
snapshot.connectionState ==
ConnectionState.done) {
return ListView.separated(
shrinkWrap: true,
itemBuilder: (context, index) {
return JoinRow(
hg: _invitedGroups[index],
onJoin: (id) => widget.onJoin(id),
);
},
separatorBuilder: (context, index) =>
const Divider(),
itemCount: _invitedGroups.length);
} else {
return const CircularProgressIndicator();
}
}),
),
),
],
),
),
),
ElevatedButton(
style: bottomButtonStyle,
onPressed: () => widget.onCreate(),
child: const Text("Create Homegroup"),
)
],
);
}
}
class JoinRow extends StatelessWidget {
final Homegroup hg;
final Function(int id) onJoin;
const JoinRow({super.key, required this.hg, required this.onJoin});
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(hg.name),
ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStatePropertyAll(
Theme.of(context).colorScheme.secondary),
foregroundColor: MaterialStatePropertyAll(
Theme.of(context).colorScheme.onSecondary)),
onPressed: () => onJoin(hg.id),
child: Row(
children: const [
Text("Join"),
SizedBox(width: 4),
Icon(Icons.check)
],
),
)
],
);
}
}
class EditHomegroup extends StatefulWidget {
final int homegroupID;
const EditHomegroup({super.key, required this.homegroupID});
@override
State<EditHomegroup> createState() => _EditHomegroupState();
}
class _EditHomegroupState extends State<EditHomegroup> {
late Future<bool> _isLoaded;
Homegroup? _homegroup;
List<SimpleUser> _groupUsers = [];
List<HomegroupInvite> _groupInvites = [];
Map<HomegroupInvite, SimpleUser> _groupInviteUsers = {};
Future<bool> _loadHomegroup() async {
Homegroup? hg = await Homegroup.fetchHomegroup(widget.homegroupID);
if (hg == null) {
return false;
}
_homegroup = hg;
_groupUsers = [];
_groupInvites = [];
_groupInviteUsers = {};
for (int id in hg.users) {
SimpleUser? u = await SimpleUser.fetchUser(id: id);
if (u != null) {
_groupUsers.add(u);
}
}
for (int id in hg.inviteIDs) {
HomegroupInvite? invite = await HomegroupInvite.get(id);
if (invite == null || hg.users.contains(invite.userID)) {
continue;
}
SimpleUser? u = await SimpleUser.fetchUser(id: invite.userID);
if (u != null) {
_groupInvites.add(invite);
_groupInviteUsers[invite] = u;
}
}
return true;
}
@override
void initState() {
super.initState();
_isLoaded = _loadHomegroup();
}
@override
Widget build(BuildContext context) {
final TextStyle textstylebase = Theme.of(context).textTheme.titleSmall!;
final TextStyle textstyle = TextStyle(
fontSize: textstylebase.fontSize,
fontWeight: textstylebase.fontWeight,
color: textstylebase.color,
decorationColor: textstylebase.color,
decorationStyle: TextDecorationStyle.solid,
decoration: TextDecoration.underline);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(
elevation: 10,
margin: EdgeInsets.zero,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(10), bottom: Radius.zero),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: FutureBuilder(
future: _isLoaded,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(child: Text(snapshot.error.toString()));
} else if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_homegroup!.name,
style: Theme.of(context).textTheme.titleLarge,
),
const Divider(),
Text(
"Users",
style: textstyle,
),
const SizedBox(height: 8),
ListView.separated(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: _groupUsers.length,
separatorBuilder: (context, index) => const Divider(),
itemBuilder: (context, index) {
return UserRow(
user: _groupUsers[index],
onButton: (id) {},
buttonText: "Kick",
);
},
),
const Divider(),
Text(
"Pending Invites",
style: textstyle,
),
const SizedBox(height: 8),
_groupInvites.isEmpty
? const Text("None")
: ListView.separated(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (context, index) {
return UserRow(
user:
_groupInviteUsers[_groupInvites[index]]!,
onButton: (id) async {
bool success = await _groupInvites[index]
.deleteInvite();
if (success) {
setState(() {
_isLoaded = _loadHomegroup();
});
}
},
buttonText: "Cancel",
);
},
separatorBuilder: (context, index) =>
const Divider(),
itemCount: _groupInvites.length)
],
);
} else {
return const Center(child: CircularProgressIndicator());
}
},
),
),
),
ElevatedButton(
style: bottomButtonStyle,
onPressed: () async {
List<int>? selectedIDs = await inviteHomegroupDialog(context);
if (selectedIDs == null) {
return;
}
for (int id in selectedIDs) {
await HomegroupInvite.createInvite(_homegroup!.id, id);
}
setState(() {
_isLoaded = _loadHomegroup();
});
},
child: const Text("Invite"),
)
],
);
}
}
class UserRow extends StatelessWidget {
final SimpleUser user;
final Function(int id) onButton;
final String buttonText;
const UserRow(
{super.key,
required this.user,
required this.onButton,
required this.buttonText});
@override
Widget build(BuildContext context) {
final ButtonStyle buttonStyle = ButtonStyle(
padding: const MaterialStatePropertyAll(
EdgeInsets.symmetric(vertical: 8, horizontal: 4)),
backgroundColor:
MaterialStatePropertyAll(Theme.of(context).colorScheme.secondary),
foregroundColor: MaterialStatePropertyAll(
Theme.of(context).colorScheme.onSecondary));
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SmallUserChip(user: user),
ElevatedButton(
style: buttonStyle,
onPressed: () => onButton(user.id),
child: Row(
children: [
Text(buttonText),
const SizedBox(width: 4),
const Icon(Icons.close)
],
),
)
],
);
}
}

View File

@@ -0,0 +1,163 @@
import 'package:flutter/material.dart';
import 'package:grocery_helper/api/models/simpleuser.dart';
import 'package:grocery_helper/pages/profile_page/widgets/user_chip.dart';
import 'package:grocery_helper/theme.dart';
import 'package:grocery_helper/widgets/pagination_listview.dart';
class InviteHomegroupDialog extends StatefulWidget {
const InviteHomegroupDialog({super.key});
@override
State<InviteHomegroupDialog> createState() => _InviteHomegroupDialogState();
}
class _InviteHomegroupDialogState extends State<InviteHomegroupDialog> {
final TextEditingController _searchController = TextEditingController();
ListViewState _listState = ListViewState.inactive;
List<int> selectedIDs = [];
@override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.zero,
child: Padding(
padding: const EdgeInsets.all(8),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Invite to Homegroup",
style: Theme.of(context).textTheme.titleMedium,
),
const Divider(),
TextFormField(
controller: _searchController,
textInputAction: TextInputAction.search,
onFieldSubmitted: (value) {
setState(() {
_listState = ListViewState.changed;
});
},
onChanged: (value) {
setState(() {
_listState = ListViewState.inactive;
});
},
decoration: InputDecoration(
label: const Text("Search"),
isDense: true,
suffix: IconButton(
onPressed: () {
setState(() {
_listState = ListViewState.changed;
});
// https://flutterigniter.com/dismiss-keyboard-form-lose-focus/
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
},
icon: const Icon(Icons.search),
),
),
),
const SizedBox(height: 8),
LayoutBuilder(
builder: (builder, constraints) {
return ConstrainedBox(
constraints: BoxConstraints.expand(
width: constraints.maxWidth - 8,
height: 160,
),
child: PaginationListView(
state: _listState,
shrinkWrap: true,
itemBuilder: (context, data) {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
setState(() {
// _ready = false;
_listState = ListViewState.inUse;
if (selectedIDs.contains(data.id)) {
selectedIDs.remove(data.id);
} else {
selectedIDs.add(data.id);
}
});
},
child: Container(
padding: const EdgeInsets.all(4),
color: selectedIDs.contains(data.id)
? Theme.of(context).colorScheme.secondary
: null,
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
SmallUserChip(
user: data,
radius: 20,
textColor: selectedIDs.contains(data.id)
? Theme.of(context).colorScheme.onSecondary
: null,
),
],
),
),
);
},
seperatorBuilder: (context, data) {
return const Divider();
},
dataProvider: (int page) async {
SearchResult result =
await SimpleUser.search(_searchController.text, page);
List<dynamic> users = List<dynamic>.from(result.users);
if (result.next == null) {
users.add(null);
}
return users;
},
),
);
},
),
Padding(
padding: const EdgeInsets.only(top: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text("Cancel"),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, selectedIDs),
child: const Text("Done")),
],
),
)
],
),
),
);
}
}
Future<List<int>?> inviteHomegroupDialog(BuildContext context) async {
List<int>? selectedIDs = await showDialog(
context: context,
builder: (context) {
return Dialog(
child: ScrollConfiguration(
behavior: MyBehavior(), child: const InviteHomegroupDialog()),
);
},
);
return selectedIDs;
}

View File

@@ -0,0 +1,86 @@
import 'package:flutter/material.dart';
import 'package:grocery_helper/api/models/user.dart';
import 'package:flutter_svg_provider/flutter_svg_provider.dart';
import 'package:grocery_helper/theme.dart';
class ProfileCard extends StatelessWidget {
final User userInfo;
final Function() onTapPhoto;
final Function() onLogout;
const ProfileCard(
{super.key,
required this.userInfo,
required this.onTapPhoto,
required this.onLogout});
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(
elevation: 10,
margin: EdgeInsets.zero,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(10), bottom: Radius.zero),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
CircleAvatar(
backgroundColor: Theme.of(context).colorScheme.primary,
radius: 42,
child: CircleAvatar(
radius: 40,
backgroundImage: userInfo.imageUrl != null
? NetworkImage(userInfo.imageUrl!)
: Image(
image: Svg('assets/images/person.svg',
color: Theme.of(context)
.colorScheme
.onPrimaryContainer),
).image,
backgroundColor:
Theme.of(context).colorScheme.primaryContainer,
// https://github.com/flutter/flutter/issues/42901#issuecomment-708050484
child: Material(
shape: const CircleBorder(),
clipBehavior: Clip.hardEdge,
color: Colors.transparent,
child: InkWell(
onTap: () => onTapPhoto(),
),
),
),
),
Padding(
padding: const EdgeInsets.only(left: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${userInfo.firstName} ${userInfo.lastName}",
style: Theme.of(context).textTheme.titleMedium,
),
Text("@${userInfo.username}",
style: Theme.of(context).textTheme.titleLarge),
],
),
)
],
),
),
),
ElevatedButton(
style: bottomButtonStyle,
onPressed: () => onLogout(),
child: const Text("Log out"),
)
],
);
}
}

View File

@@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg_provider/flutter_svg_provider.dart';
import 'package:grocery_helper/api/models/simpleuser.dart';
class SmallUserChip extends StatelessWidget {
final SimpleUser user;
final double? radius;
final Color? textColor;
const SmallUserChip(
{super.key, required this.user, this.radius, this.textColor});
@override
Widget build(BuildContext context) {
double baseRadius = radius ?? 30;
return Row(
children: [
CircleAvatar(
radius: baseRadius,
backgroundColor: Theme.of(context).colorScheme.primary,
child: CircleAvatar(
radius: baseRadius - 2,
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
backgroundImage: user.imageUrl != null
? NetworkImage(user.imageUrl!)
: Image(
image: Svg('assets/images/person.svg',
color:
Theme.of(context).colorScheme.onPrimaryContainer),
).image,
),
),
const SizedBox(width: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${user.firstName} ${user.lastName}",
style: TextStyle(
color: textColor ?? Theme.of(context).colorScheme.onSurface),
),
Text(
"@${user.username}",
style: TextStyle(
color: textColor ?? Theme.of(context).colorScheme.onSurface),
),
],
)
],
);
}
}

View File

@@ -0,0 +1,158 @@
import 'package:flutter/material.dart';
import 'package:grocery_helper/api/models/homegroup.dart';
import 'package:grocery_helper/api/models/ingredient.dart';
import 'package:grocery_helper/api/models/recipe.dart';
import 'package:grocery_helper/api/models/user.dart';
class RecipesPage extends StatefulWidget {
const RecipesPage({super.key});
@override
State<RecipesPage> createState() => _RecipesPageState();
}
class _RecipesPageState extends State<RecipesPage> {
late Future<List<Recipe>> _recipes;
late User _userInfo;
late Homegroup _homegroup;
Future<List<Recipe>> _fetchList() async {
User? userInfo = await User.fetchUser();
if (userInfo == null || userInfo.homegroup == null) {
return [];
}
_userInfo = userInfo;
List<Recipe> recipes = await Recipe.fetchList(_userInfo.homegroup!);
return recipes;
}
@override
void initState() {
super.initState();
_recipes = _fetchList();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _recipes,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text(snapshot.error.toString());
} else if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return RecipeList(recipes: snapshot.data!);
} else {
return const CircularProgressIndicator();
}
},
);
}
}
class RecipeList extends StatelessWidget {
final List<Recipe> recipes;
const RecipeList({super.key, required this.recipes});
@override
Widget build(BuildContext context) {
return Stack(
children: [
ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: recipes.length,
itemBuilder: (context, index) =>
RecipeCard(recipe: recipes[index])),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: FloatingActionButton.extended(
onPressed: () {},
label: Row(
children: const [Icon(Icons.note_add), Text("New Recipe")],
),
// child: const Icon(Icons.note_add),
),
),
)
],
);
}
}
class RecipeCard extends StatelessWidget {
final Recipe recipe;
const RecipeCard({super.key, required this.recipe});
@override
Widget build(BuildContext context) {
final TextStyle ingredientStyle = Theme.of(context)
.textTheme
.titleLarge!
.copyWith(color: Theme.of(context).colorScheme.onSurface);
return Card(
elevation: 10,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(10), bottom: Radius.zero),
),
margin: EdgeInsets.zero,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
recipe.name,
style: Theme.of(context).textTheme.titleLarge,
),
),
const Divider(),
Container(
color: Theme.of(context).colorScheme.surface,
child: ListView.separated(
padding: const EdgeInsets.all(8),
itemCount: (recipe.ingredients.length / 2).ceil(),
shrinkWrap: true,
separatorBuilder: (context, index) => Divider(
color: Theme.of(context).colorScheme.onSurface,
),
itemBuilder: (context, index) {
if (recipe.ingredients.length % 2 == 0 ||
index * 2 <= recipe.ingredients.length - 2) {
return Row(
children: [
Expanded(
child: Text(
recipe.ingredients[index * 2].name,
style: ingredientStyle,
),
),
Expanded(
child: Text(recipe.ingredients[index * 2 + 1].name,
style: ingredientStyle),
)
],
);
} else {
return Row(
children: [
Text(recipe.ingredients[index * 2].name,
style: ingredientStyle),
],
);
}
},
),
)
],
),
),
);
}
}

View File

@@ -0,0 +1,125 @@
import 'package:flutter/material.dart';
class ColorPage extends StatelessWidget {
const ColorPage({super.key});
@override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 3,
children: [
// FIRST ROW
Container(
padding: const EdgeInsets.all(8),
color: Theme.of(context).colorScheme.primary,
child: Center(
child: Text(
"Primary",
style: TextStyle(color: Theme.of(context).colorScheme.onPrimary),
)),
),
Container(
padding: const EdgeInsets.all(8),
color: Theme.of(context).colorScheme.secondary,
child: Center(
child: Text(
"Secondary",
style: TextStyle(color: Theme.of(context).colorScheme.onSecondary),
)),
),
Container(
padding: const EdgeInsets.all(8),
color: Theme.of(context).colorScheme.tertiary,
child: Center(
child: Text(
"Tertiary",
style: TextStyle(color: Theme.of(context).colorScheme.onTertiary),
)),
),
// SECOND ROW
Container(
padding: const EdgeInsets.all(8),
color: Theme.of(context).colorScheme.primaryContainer,
child: Center(
child: Text(
"Primary Container",
style: TextStyle(
color: Theme.of(context).colorScheme.onPrimaryContainer),
)),
),
Container(
padding: const EdgeInsets.all(8),
color: Theme.of(context).colorScheme.secondaryContainer,
child: Center(
child: Text(
"Secondary Container",
style: TextStyle(
color: Theme.of(context).colorScheme.onSecondaryContainer),
)),
),
Container(
padding: const EdgeInsets.all(8),
color: Theme.of(context).colorScheme.tertiaryContainer,
child: Center(
child: Text(
"Tertiary Container",
style: TextStyle(
color: Theme.of(context).colorScheme.onTertiaryContainer),
)),
),
// THIRD ROW
Container(
padding: const EdgeInsets.all(8),
color: Theme.of(context).colorScheme.error,
child: Center(
child: Text(
"Error",
style: TextStyle(color: Theme.of(context).colorScheme.onError),
)),
),
Container(
padding: const EdgeInsets.all(8),
color: Theme.of(context).colorScheme.background,
child: Center(
child: Text(
"Background",
style: TextStyle(color: Theme.of(context).colorScheme.onBackground),
)),
),
Container(
padding: const EdgeInsets.all(8),
color: Theme.of(context).colorScheme.surface,
child: Center(
child: Text(
"Surface",
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
)),
),
// FOURTH ROW
Container(
padding: const EdgeInsets.all(8),
color: Theme.of(context).colorScheme.errorContainer,
child: Center(
child: Text(
"Error Container",
style: TextStyle(
color: Theme.of(context).colorScheme.onErrorContainer),
)),
),
Container(
padding: const EdgeInsets.all(8),
color: Theme.of(context).colorScheme.surfaceVariant,
child: Center(
child: Text(
"Surface Variant",
style: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant),
)),
),
],
);
}
}

View File

@@ -0,0 +1,75 @@
import 'package:flutter/material.dart';
import 'package:grocery_helper/pages/profile_page/profile_page.dart';
import 'package:grocery_helper/pages/recipes_page/recipes_page.dart';
import 'package:grocery_helper/pages/themetest.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
int _selectedPage = 0;
late List<Widget> _pages;
final List<String> _pageNames = [
"Shopping List",
"Saved Recipes",
"Your Profile",
"Color Debug"
];
@override
void initState() {
_pages = <Widget>[
Container(),
const RecipesPage(),
const ProfilePage(),
const ColorPage()
];
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(child: _pages[_selectedPage]),
appBar: AppBar(title: Text(_pageNames[_selectedPage])),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.shifting,
selectedItemColor: Theme.of(context).colorScheme.primary,
unselectedItemColor: Theme.of(context).colorScheme.secondary,
landscapeLayout: BottomNavigationBarLandscapeLayout.centered,
items: [
BottomNavigationBarItem(
icon: const Icon(Icons.list_alt),
label: "List",
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
),
BottomNavigationBarItem(
icon: const Icon(Icons.menu_book),
label: "Recipes",
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
),
BottomNavigationBarItem(
icon: const Icon(Icons.person),
label: "Profile",
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
),
BottomNavigationBarItem(
icon: const Icon(Icons.grid_3x3),
label: "Colors",
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
)
],
currentIndex: _selectedPage,
onTap: (value) {
setState(() {
_selectedPage = value;
});
},
),
);
}
}

View File

@@ -0,0 +1,338 @@
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:grocery_helper/api/auth.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final List<bool> _isSelected = [true, false];
@override
Widget build(BuildContext context) {
final Brightness brightness = Theme.of(context).colorScheme.brightness;
return Scaffold(
body: Center(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
SizedBox.square(
dimension: 200,
child: SvgPicture.asset(
brightness == Brightness.light
? "assets/images/holes.svg"
: "assets/images/holes-dark.svg",
fit: BoxFit.contain,
),
),
FractionallySizedBox(
widthFactor: 0.9,
child: Card(
elevation: 10,
child: Padding(
padding: const EdgeInsets.all(8),
child: Column(
children: [
LayoutBuilder(builder: (builder, constraints) {
return ToggleButtons(
isSelected: _isSelected,
constraints: BoxConstraints.expand(
width: constraints.maxWidth / 2 - 8,
height: 30),
selectedBorderColor:
Theme.of(context).colorScheme.primary,
onPressed: (index) {
setState(() {
for (int i = 0; i < _isSelected.length; i++) {
_isSelected[i] = (i == index);
}
});
},
children: const [Text("Log In"), Text("Sign Up")],
);
}),
_isSelected[0] ? const LoginForm() : const SignupForm()
],
),
),
),
),
],
),
),
),
);
}
}
class LoginForm extends StatefulWidget {
const LoginForm({super.key});
@override
State<LoginForm> createState() => _LoginFormState();
}
class _LoginFormState extends State<LoginForm> {
bool _stayLoggedIn = false;
bool _tryingLogin = false;
final _usernameController = TextEditingController(text: "");
final _passwordController = TextEditingController(text: "");
void tryLogIn(bool save) async {
setState(() {
_tryingLogin = true;
});
String tok =
await getToken(_usernameController.text, _passwordController.text);
if (tok == "") {
showError("That Username / Password combination does not exist.");
setState(() {
_tryingLogin = false;
});
return;
}
if (save) {
const storage = FlutterSecureStorage();
storage.write(key: "token", value: tok);
}
if (mounted) {
await Navigator.pushReplacementNamed(context, "/home");
}
}
void trySavedToken() async {
setState(() {
_tryingLogin = true;
});
// get token from storage
const storage = FlutterSecureStorage();
final String? token = await storage.read(key: "token");
if (token == null) {
setState(() {
_tryingLogin = false;
});
return;
}
// check the stored token still works
final bool stillValid = await testToken(token);
if (!stillValid) {
storage.delete(key: "token");
setState(() {
_tryingLogin = false;
});
return;
}
// store the token in a singleton to avoid repeated storage accesses
final TokenSingleton s = TokenSingleton();
s.setToken(token);
if (mounted) {
await Navigator.pushReplacementNamed(context, "/home");
}
}
void showError(String text) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Theme.of(context).colorScheme.error,
content: Text(text),
action: SnackBarAction(
textColor: Theme.of(context).colorScheme.onError,
label: "Dismiss",
onPressed: () => ScaffoldMessenger.of(context).hideCurrentSnackBar(),
),
),
);
}
@override
void initState() {
super.initState();
trySavedToken();
}
@override
Widget build(BuildContext context) {
return Form(
child: Column(
children: [
TextFormField(
controller: _usernameController,
textInputAction: TextInputAction.next,
decoration: const InputDecoration(hintText: "Username"),
),
TextFormField(
controller: _passwordController,
textInputAction: TextInputAction.done,
obscureText: true,
decoration: const InputDecoration(hintText: "Password"),
onFieldSubmitted: (value) => tryLogIn(_stayLoggedIn),
),
Row(
children: [
const Text("Stay Logged In:"),
Checkbox(
value: _stayLoggedIn,
onChanged: (value) {
setState(() {
_stayLoggedIn = value!;
});
},
)
],
),
ElevatedButton(
onPressed: !_tryingLogin ? () => tryLogIn(_stayLoggedIn) : null,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: _tryingLogin
? [
const Text("Log In"),
const Padding(
padding: EdgeInsets.only(left: 12),
child: Center(
child: SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(),
),
),
)
]
: const [
Text("Log In"),
],
),
)
],
),
);
}
}
class SignupForm extends StatefulWidget {
const SignupForm({super.key});
@override
State<SignupForm> createState() => _SignupFormState();
}
class _SignupFormState extends State<SignupForm> {
final _usernameController = TextEditingController(text: "");
final _passwordController = TextEditingController(text: "");
final _firstnameController = TextEditingController(text: "");
final _lastnameController = TextEditingController(text: "");
void showError(String text) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Theme.of(context).colorScheme.error,
content: Text(text),
action: SnackBarAction(
textColor: Theme.of(context).colorScheme.onError,
label: "Dismiss",
onPressed: () => ScaffoldMessenger.of(context).hideCurrentSnackBar(),
),
),
);
}
void trySignUpLogin() async {
String firstName = _firstnameController.text;
String lastName = _lastnameController.text;
String userName = _usernameController.text;
String password = _passwordController.text;
if (firstName == "" || lastName == "" || userName == "" || password == "") {
showError("All fields must be populated");
return;
}
String errors = await signup(firstName, lastName, userName, password);
if (errors != "") {
showError(errors);
return;
}
String tok = await getToken(userName, password);
if (tok == "") {
showError("That Username / Password combination does not exist.");
return;
}
if (mounted) {
await Navigator.pushReplacementNamed(context, "/home");
}
}
@override
Widget build(BuildContext context) {
return Form(
child: Column(
children: [
Row(
children: [
Flexible(
child: TextFormField(
controller: _firstnameController,
textInputAction: TextInputAction.next,
decoration: const InputDecoration(hintText: "First Name"),
),
),
const SizedBox(width: 8),
Flexible(
child: TextFormField(
controller: _lastnameController,
textInputAction: TextInputAction.next,
decoration: const InputDecoration(hintText: "Last Name"),
),
)
],
),
TextFormField(
controller: _usernameController,
textInputAction: TextInputAction.next,
decoration: const InputDecoration(hintText: "Username"),
),
TextFormField(
controller: _passwordController,
textInputAction: TextInputAction.done,
obscureText: true,
decoration: const InputDecoration(hintText: "Password"),
onFieldSubmitted: (value) async => trySignUpLogin(),
),
const SizedBox(height: 8),
ElevatedButton(
onPressed: () async {
trySignUpLogin();
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: const [Text("Sign up & Log in")],
),
)
],
),
);
}
}

View File

@@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
// const Color _seed = Color.fromARGB(255, 8, 150, 255);
const Color _seed = Color.fromARGB(255, 50, 110, 160);
final _darkScheme =
ColorScheme.fromSeed(seedColor: _seed, brightness: Brightness.dark);
final _lightScheme =
ColorScheme.fromSeed(seedColor: _seed, brightness: Brightness.light);
final darkTheme = ThemeData(
colorScheme: _darkScheme,
toggleableActiveColor: _darkScheme.primary,
floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: _darkScheme.primary,
splashColor: _darkScheme.secondary,
),
cardColor: _darkScheme.secondaryContainer);
final lightTheme = ThemeData(
colorScheme: _lightScheme,
toggleableActiveColor: _lightScheme.primary,
cardColor: _lightScheme.secondaryContainer);
final bottomButtonStyle = ButtonStyle(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: MaterialStateProperty.all(
const RoundedRectangleBorder(
borderRadius:
BorderRadius.vertical(top: Radius.zero, bottom: Radius.circular(10)),
),
),
);
// https://stackoverflow.com/a/51119796/13538080
class MyBehavior extends ScrollBehavior {
@override
Widget buildOverscrollIndicator(
BuildContext context, Widget child, ScrollableDetails details) {
return child;
}
}

View File

@@ -0,0 +1,112 @@
import "package:flutter/material.dart";
enum ListViewState { inUse, changed, inactive }
class PaginationListView extends StatefulWidget {
final Widget Function(BuildContext context, dynamic data) itemBuilder;
final Widget Function(BuildContext context, dynamic data) seperatorBuilder;
final bool? shrinkWrap;
final ListViewState state;
final Future<List<dynamic>> Function(int page) dataProvider;
const PaginationListView(
{super.key,
required this.itemBuilder,
required this.dataProvider,
required this.state,
required this.seperatorBuilder,
this.shrinkWrap});
@override
State<PaginationListView> createState() => _PaginationListViewState();
}
class _PaginationListViewState extends State<PaginationListView> {
int _pagesLoaded = 0;
List<dynamic> _data = [];
bool _dataLeft = true;
bool _isLoading = false;
ListViewState _state = ListViewState.inactive;
late ScrollController _scrollController;
void consumeData() async {
if (_state != ListViewState.inUse || _isLoading || !_dataLeft) {
return;
}
setState(() {
_isLoading = true;
});
List<dynamic> newData = await widget.dataProvider(_pagesLoaded + 1);
if (newData[newData.length - 1] == null) {
newData.removeAt(newData.length - 1);
_dataLeft = false;
}
_pagesLoaded++;
setState(() {
_data.addAll(newData);
});
setState(() {
_isLoading = false;
});
}
@override
void initState() {
super.initState();
_scrollController = ScrollController();
_state = widget.state;
}
@override
void didUpdateWidget(covariant PaginationListView oldWidget) {
super.didUpdateWidget(oldWidget);
_state = widget.state;
if (_state == ListViewState.changed) {
_state = ListViewState.inUse;
_data = [];
_dataLeft = true;
_isLoading = false;
_pagesLoaded = 0;
consumeData();
}
}
@override
Widget build(BuildContext context) {
List<Widget> stackChildren = [
NotificationListener<ScrollNotification>(
onNotification: (notification) {
if (_scrollController.position.maxScrollExtent ==
_scrollController.position.pixels) {
consumeData();
}
return true;
},
// child: Scrollbar(
child: ListView.separated(
controller: _scrollController,
itemCount: _data.length,
shrinkWrap: widget.shrinkWrap ?? false,
itemBuilder: (context, index) =>
widget.itemBuilder(context, _data[index]),
separatorBuilder: (context, index) =>
widget.seperatorBuilder(context, _data[index]),
),
),
// )
];
if (_isLoading) {
stackChildren.add(const Center(child: CircularProgressIndicator()));
}
return Stack(
children: stackChildren,
);
}
}

View File

@@ -0,0 +1,82 @@
import 'package:flutter/material.dart';
class TextEntryForm extends StatefulWidget {
final String title;
final String label;
final String? defaultValue;
const TextEntryForm(
{super.key, required this.title, required this.label, this.defaultValue});
@override
State<TextEntryForm> createState() => _TextEntryFormState();
}
class _TextEntryFormState extends State<TextEntryForm> {
late TextEditingController _textController;
@override
void initState() {
super.initState();
_textController = TextEditingController(text: widget.defaultValue);
}
@override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.zero,
child: Padding(
padding: const EdgeInsets.all(8),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.title,
style: Theme.of(context).textTheme.titleMedium,
),
const Divider(),
TextFormField(
controller: _textController,
decoration: InputDecoration(hintText: widget.label),
),
Padding(
padding: const EdgeInsets.only(top: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text("Cancel"),
),
ElevatedButton(
onPressed: () =>
Navigator.pop(context, _textController.text),
child: const Text("Done")),
],
),
),
],
),
),
);
}
}
Future<String?> createHomegroupDialog(
BuildContext context, String title, String label,
{String? defaultValue}) async {
String? name = await showDialog(
context: context,
builder: (context) {
return Dialog(
child: TextEntryForm(
title: title,
label: label,
defaultValue: defaultValue,
),
);
},
);
return name;
}

1
grocery_helper/linux/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
flutter/ephemeral

View File

@@ -0,0 +1,138 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
set(BINARY_NAME "one_trip")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.example.grocery_helper")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(SET CMP0063 NEW)
# Load bundled libraries from the lib/ directory relative to the binary.
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Root filesystem for cross-building.
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endif()
# Define build configuration options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
# Compilation settings that should be applied to most targets.
#
# Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead
# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction()
# Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
# Define the application target. To change its name, change BINARY_NAME above,
# not the value here, or `flutter run` will no longer work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)
# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})
# Add dependency libraries. Add any application-specific dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)
# Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid
# people trying to run the unbundled copy, put it in a subdirectory instead of
# the default top-level location.
set_target_properties(${BINARY_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build
# directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
# Start with a clean build bundle directory every time.
install(CODE "
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
" COMPONENT Runtime)
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
install(FILES "${bundled_library}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endforeach(bundled_library)
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()

View File

@@ -0,0 +1,88 @@
# This file controls Flutter-level build steps. It should not be edited.
cmake_minimum_required(VERSION 3.10)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
# which isn't available in 3.10.
function(list_prepend LIST_NAME PREFIX)
set(NEW_LIST "")
foreach(element ${${LIST_NAME}})
list(APPEND NEW_LIST "${PREFIX}${element}")
endforeach(element)
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
endfunction()
# === Flutter Library ===
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"fl_basic_message_channel.h"
"fl_binary_codec.h"
"fl_binary_messenger.h"
"fl_dart_project.h"
"fl_engine.h"
"fl_json_message_codec.h"
"fl_json_method_codec.h"
"fl_message_codec.h"
"fl_method_call.h"
"fl_method_channel.h"
"fl_method_codec.h"
"fl_method_response.h"
"fl_plugin_registrar.h"
"fl_plugin_registry.h"
"fl_standard_message_codec.h"
"fl_standard_method_codec.h"
"fl_string_codec.h"
"fl_value.h"
"fl_view.h"
"flutter_linux.h"
)
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
target_link_libraries(flutter INTERFACE
PkgConfig::GTK
PkgConfig::GLIB
PkgConfig::GIO
)
add_dependencies(flutter flutter_assemble)
# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/_phony_
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
)

View File

@@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
}

View File

@@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

Some files were not shown because too many files have changed in this diff Show More