Account Management mostly implemented
@@ -22,6 +22,10 @@ class UserInline(nested_admin.NestedTabularInline):
|
|||||||
def has_delete_permission(self, request, obj=None):
|
def has_delete_permission(self, request, obj=None):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
class HomegroupInviteInline(nested_admin.NestedTabularInline):
|
||||||
|
model = HomegroupInvite
|
||||||
|
extra = 0
|
||||||
|
|
||||||
class RecipeInline(nested_admin.NestedTabularInline):
|
class RecipeInline(nested_admin.NestedTabularInline):
|
||||||
model = Recipe
|
model = Recipe
|
||||||
inlines = (IngredientInline,)
|
inlines = (IngredientInline,)
|
||||||
@@ -35,9 +39,9 @@ class RecipeAdmin(nested_admin.NestedModelAdmin):
|
|||||||
@admin.register(Homegroup)
|
@admin.register(Homegroup)
|
||||||
class HomegroupAdmin(nested_admin.NestedModelAdmin):
|
class HomegroupAdmin(nested_admin.NestedModelAdmin):
|
||||||
list_display = ("id", "name")
|
list_display = ("id", "name")
|
||||||
inlines = (UserInline, RecipeInline, IngredientInline)
|
inlines = (UserInline, HomegroupInviteInline, RecipeInline, IngredientInline)
|
||||||
|
|
||||||
@admin.register(List)
|
@admin.register(List)
|
||||||
class ListAdmin(nested_admin.NestedModelAdmin):
|
class ListAdmin(nested_admin.NestedModelAdmin):
|
||||||
list_display = ("id",)
|
list_display = ("homegroup",)
|
||||||
inlines = (RecipeInline, IngredientInline)
|
inlines = (RecipeInline, IngredientInline)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 4.1.3 on 2022-11-22 20:42
|
# Generated by Django 4.1.3 on 2022-11-25 09:10
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
@@ -17,12 +17,13 @@ class Migration(migrations.Migration):
|
|||||||
name='Homegroup',
|
name='Homegroup',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=50)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='List',
|
name='List',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('homegroup', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='api.homegroup')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
@@ -31,7 +32,6 @@ class Migration(migrations.Migration):
|
|||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('name', models.CharField(max_length=50)),
|
('name', models.CharField(max_length=50)),
|
||||||
('homegroup', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='recipes', to='api.homegroup')),
|
('homegroup', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='recipes', to='api.homegroup')),
|
||||||
('list', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='recipes', to='api.list')),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
@@ -44,4 +44,11 @@ class Migration(migrations.Migration):
|
|||||||
('content_type', models.ForeignKey(limit_choices_to=models.Q(models.Q(('app_label', 'api'), ('model', 'recipe')), models.Q(('app_label', 'api'), ('model', 'list')), _connector='OR'), on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
|
('content_type', models.ForeignKey(limit_choices_to=models.Q(models.Q(('app_label', 'api'), ('model', 'recipe')), models.Q(('app_label', 'api'), ('model', 'list')), _connector='OR'), on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='HomegroupInvite',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('homegroup', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='invites', to='api.homegroup')),
|
||||||
|
],
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
# Generated by Django 4.1.3 on 2022-11-22 21:43
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('api', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='homegroup',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(default="Alex's Kitchen", max_length=50),
|
|
||||||
preserve_default=False,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
37
django_backend/api/migrations/0002_initial.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# Generated by Django 4.1.3 on 2022-11-25 09:10
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('api', '0001_initial'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='homegroupinvite',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='invites', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='homegroup',
|
||||||
|
name='invited_users',
|
||||||
|
field=models.ManyToManyField(related_name='homegroup_invites', through='api.HomegroupInvite', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='recipe',
|
||||||
|
name='list',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='recipes', to='api.list'),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='homegroupinvite',
|
||||||
|
unique_together={('homegroup', 'user')},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -13,15 +13,29 @@ class Ingredient(models.Model):
|
|||||||
name = models.CharField(max_length=50)
|
name = models.CharField(max_length=50)
|
||||||
in_stock = models.BooleanField(default=False)
|
in_stock = models.BooleanField(default=False)
|
||||||
|
|
||||||
class List(models.Model):
|
class HomegroupInvite(models.Model):
|
||||||
# Foreign Key Recipe -> List [as recipes]
|
homegroup = models.ForeignKey("api.Homegroup", on_delete=models.CASCADE, related_name="invites", blank=True)
|
||||||
extra_ingredients = GenericRelation(Ingredient, related_query_name="extra_ingredients")
|
user = models.ForeignKey("users.User", on_delete=models.CASCADE, related_name="invites", blank=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = ("homegroup", "user")
|
||||||
|
|
||||||
class Homegroup(models.Model):
|
class Homegroup(models.Model):
|
||||||
# Foreign Key Recipe -> Homegroup [as recipes]
|
# Foreign Key Recipe -> Homegroup [as recipes]
|
||||||
# Foreign Key User -> Homegroup [as users]
|
# Foreign Key User -> Homegroup [as users]
|
||||||
name = models.CharField(max_length=50)
|
name = models.CharField(max_length=50)
|
||||||
|
invited_users = models.ManyToManyField("users.User", related_name="homegroup_invites", through=HomegroupInvite)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"{self.id}: {self.name}"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.id}: {self.name}"
|
||||||
|
|
||||||
|
class List(models.Model):
|
||||||
|
# Foreign Key Recipe -> List [as recipes]
|
||||||
|
extra_ingredients = GenericRelation(Ingredient, related_query_name="extra_ingredients")
|
||||||
|
homegroup = models.OneToOneField(Homegroup, on_delete=models.CASCADE, primary_key=True)
|
||||||
|
|
||||||
|
|
||||||
class Recipe(models.Model):
|
class Recipe(models.Model):
|
||||||
|
|||||||
@@ -1,24 +1,42 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from api.models import *
|
from api.models import *
|
||||||
|
from users.serializers import UserSerializer
|
||||||
|
|
||||||
|
class IngredientSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Ingredient
|
||||||
|
fields = ["id", "name", "in_stock", "content_type", "object_id"]
|
||||||
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
class RecipeSerializer(serializers.ModelSerializer):
|
class RecipeSerializer(serializers.ModelSerializer):
|
||||||
ingredients = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
# ingredients = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||||
|
# ingredients = IngredientSerializer(many=True, read_only=True)
|
||||||
|
ingredients = serializers.SerializerMethodField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Recipe
|
model = Recipe
|
||||||
fields = ["id", "name", "ingredients"]
|
fields = ["id", "name", "ingredients"]
|
||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
class IngredientSerializer(serializers.ModelSerializer):
|
def get_ingredients(self, instance):
|
||||||
class Meta:
|
ingredients = instance.ingredients.all().order_by("name")
|
||||||
model = Ingredient
|
return IngredientSerializer(ingredients, many=True).data
|
||||||
fields = ["id", "name", "in_stock"]
|
|
||||||
read_only_fields = ["id"]
|
|
||||||
|
|
||||||
class HomegroupSerializer(serializers.ModelSerializer):
|
class HomegroupSerializer(serializers.ModelSerializer):
|
||||||
users = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
users = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||||
recipes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
recipes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
homegroup = super().create(validated_data)
|
||||||
|
List.objects.get_or_create(homegroup=homegroup)
|
||||||
|
return homegroup
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Homegroup
|
model = Homegroup
|
||||||
fields = ["id", "recipes", "users"]
|
fields = ["id", "name", "recipes", "users", "invites"]
|
||||||
|
read_only_fields = ["recipes", "users", "invites"]
|
||||||
|
|
||||||
|
class InviteSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = HomegroupInvite
|
||||||
|
fields = ["id", "homegroup", "user"]
|
||||||
@@ -6,6 +6,7 @@ router = routers.DefaultRouter()
|
|||||||
router.register(r'recipes', views.RecipeView)
|
router.register(r'recipes', views.RecipeView)
|
||||||
router.register(r'ingredients', views.IngredientView)
|
router.register(r'ingredients', views.IngredientView)
|
||||||
router.register(r'homegroups', views.HomegroupView)
|
router.register(r'homegroups', views.HomegroupView)
|
||||||
|
router.register(r'groupinvites', views.HomegroupInviteView)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', include(router.urls))
|
path('', include(router.urls))
|
||||||
|
|||||||
@@ -14,3 +14,7 @@ class IngredientView(viewsets.ModelViewSet):
|
|||||||
class HomegroupView(viewsets.ModelViewSet):
|
class HomegroupView(viewsets.ModelViewSet):
|
||||||
serializer_class = HomegroupSerializer
|
serializer_class = HomegroupSerializer
|
||||||
queryset = Homegroup.objects.all()
|
queryset = Homegroup.objects.all()
|
||||||
|
|
||||||
|
class HomegroupInviteView(viewsets.ModelViewSet):
|
||||||
|
serializer_class = InviteSerializer
|
||||||
|
queryset = HomegroupInvite.objects.all()
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ from pathlib import Path
|
|||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
MEDIA_ROOT = BASE_DIR.joinpath("media")
|
||||||
|
STATIC_URL = "static/"
|
||||||
|
MEDIA_URL = "/media/"
|
||||||
|
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
# Quick-start development settings - unsuitable for production
|
||||||
@@ -27,7 +30,9 @@ AUTH_USER_MODEL = 'users.User'
|
|||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
ALLOWED_HOSTS = []
|
ALLOWED_HOSTS = ["192.168.0.16", "127.0.0.1", "localhost"]
|
||||||
|
# CORS_ALLOWED_ORIGINS = ["http://192.168.0.16:8000"]
|
||||||
|
CORS_ALLOW_ALL_ORIGINS = True
|
||||||
|
|
||||||
REST_FRAMEWORK = {
|
REST_FRAMEWORK = {
|
||||||
'DEFAULT_AUTHENTICATION_CLASSES': [
|
'DEFAULT_AUTHENTICATION_CLASSES': [
|
||||||
@@ -47,6 +52,7 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
'corsheaders',
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
'rest_framework.authtoken',
|
'rest_framework.authtoken',
|
||||||
'nested_admin'
|
'nested_admin'
|
||||||
@@ -55,6 +61,7 @@ INSTALLED_APPS = [
|
|||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'corsheaders.middleware.CorsMiddleware',
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
@@ -124,12 +131,6 @@ USE_I18N = True
|
|||||||
|
|
||||||
USE_TZ = True
|
USE_TZ = True
|
||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
|
||||||
# https://docs.djangoproject.com/en/4.1/howto/static-files/
|
|
||||||
|
|
||||||
STATIC_URL = 'static/'
|
|
||||||
|
|
||||||
# Default primary key field type
|
# Default primary key field type
|
||||||
# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field
|
# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,12 @@ Including another URLconf
|
|||||||
"""
|
"""
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
|
from django.conf.urls.static import static
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
path('api/', include("api.urls")),
|
path('api/', include("api.urls")),
|
||||||
path('auth/', include("users.urls"))
|
path('auth/', include("users.urls")),
|
||||||
]
|
path('api-auth/', include('rest_framework.urls')),
|
||||||
|
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|||||||
@@ -4,18 +4,18 @@ from .models import User
|
|||||||
|
|
||||||
class CustomUserAdmin(UserAdmin):
|
class CustomUserAdmin(UserAdmin):
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {"fields": ("username", "password", "homegroup")}),
|
(None, {"fields": ("username", "password", "image", "homegroup")}),
|
||||||
("Personal info", {"fields": ("first_name", "last_name", "email")}),
|
("Personal info", {"fields": ("first_name", "last_name")}),
|
||||||
(
|
# (
|
||||||
"Permissions",
|
# "Permissions",
|
||||||
{
|
# {
|
||||||
"fields": (
|
# "fields": (
|
||||||
"is_active",
|
# "is_active",
|
||||||
"is_staff",
|
# "is_staff",
|
||||||
"is_superuser",
|
# "is_superuser",
|
||||||
),
|
# ),
|
||||||
},
|
# },
|
||||||
),
|
# ),
|
||||||
("Important dates", {"fields": ("last_login", "date_joined")}),
|
("Important dates", {"fields": ("last_login", "date_joined")}),
|
||||||
)
|
)
|
||||||
add_fieldsets = (
|
add_fieldsets = (
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
# Generated by Django 4.1.3 on 2022-11-22 20:42
|
# Generated by Django 4.1.3 on 2022-11-25 09:10
|
||||||
|
|
||||||
import django.contrib.auth.models
|
import django.contrib.auth.models
|
||||||
import django.contrib.auth.validators
|
import django.contrib.auth.validators
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import django.utils.timezone
|
import django.utils.timezone
|
||||||
|
import users.models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
@@ -12,8 +13,8 @@ class Migration(migrations.Migration):
|
|||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('api', '0001_initial'),
|
|
||||||
('auth', '0012_alter_user_first_name_max_length'),
|
('auth', '0012_alter_user_first_name_max_length'),
|
||||||
|
('api', '0001_initial'),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
@@ -31,6 +32,7 @@ class Migration(migrations.Migration):
|
|||||||
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||||
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||||
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||||
|
('image', models.ImageField(blank=True, null=True, upload_to=users.models.image_path)),
|
||||||
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
|
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
|
||||||
('homegroup', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='users', to='api.homegroup')),
|
('homegroup', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='users', to='api.homegroup')),
|
||||||
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
|
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
from django.contrib.auth.models import AbstractUser
|
from django.contrib.auth.models import AbstractUser
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from api.models import Homegroup
|
from api.models import Homegroup
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def image_path(instance, fname):
|
||||||
|
extension = Path(fname).suffix
|
||||||
|
return f"profile-images/{instance.id}{extension}"
|
||||||
|
|
||||||
class User(AbstractUser):
|
class User(AbstractUser):
|
||||||
homegroup = models.ForeignKey(Homegroup, related_name="users", on_delete=models.SET_NULL, blank=True, null=True)
|
homegroup = models.ForeignKey(Homegroup, related_name="users", on_delete=models.SET_NULL, blank=True, null=True)
|
||||||
|
image = models.ImageField(upload_to=image_path, null=True, blank=True)
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,6 @@ class UserSerializer(serializers.ModelSerializer): # https://stackoverflow.com/
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ("id", "username", "email", "password")
|
fields = ("id", "username", "first_name", "last_name", "password", "image", "homegroup", "homegroup_invites")
|
||||||
write_only_fields = ("password",)
|
write_only_fields = ("password",)
|
||||||
read_only_fields = ("id",)
|
read_only_fields = ("id", "homegroup_invites")
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
from rest_framework.authtoken import views as authviews
|
from rest_framework.authtoken import views as authviews
|
||||||
from rest_framework import routers
|
from rest_framework import routers
|
||||||
|
|
||||||
from users import views
|
from users import views
|
||||||
|
|
||||||
|
router = routers.DefaultRouter()
|
||||||
|
router.register(r'users', views.UsersListView)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
path('', include(router.urls)),
|
||||||
path('token', authviews.obtain_auth_token, name="api-token-auth"),
|
path('token', authviews.obtain_auth_token, name="api-token-auth"),
|
||||||
path('users', views.RegisterUserView.as_view()), # Exposes POST to everyone for registering
|
# path('users', views.RegisterUserView), # Exposes POST to everyone for registering
|
||||||
path('users/me', views.ModifyUserView.as_view()) # exposes GET / PUT / PATCH / DELETE for registered users for their user object
|
path('users/me', views.ModifyUserView.as_view()) # exposes GET / PUT / PATCH / DELETE for registered users for their user object
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
from rest_framework import generics, permissions, views, status
|
from rest_framework import generics, permissions, viewsets, mixins, filters, pagination
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from users import serializers, models
|
from users.models import *
|
||||||
|
from users.serializers import *
|
||||||
|
|
||||||
|
class Pagination(pagination.PageNumberPagination):
|
||||||
|
page_size = 4
|
||||||
|
|
||||||
class IsOwner(permissions.BasePermission):
|
class IsOwner(permissions.BasePermission):
|
||||||
def has_object_permission(self, request, view, obj):
|
def has_object_permission(self, request, view, obj):
|
||||||
@@ -12,10 +15,17 @@ class IsOwner(permissions.BasePermission):
|
|||||||
|
|
||||||
|
|
||||||
# Anyone can register
|
# Anyone can register
|
||||||
class RegisterUserView(generics.CreateAPIView):
|
class UsersListView(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
|
||||||
model = models.User
|
model = User
|
||||||
serializer_class = serializers.UserSerializer
|
serializer_class = UserSerializer
|
||||||
permission_classes = [permissions.AllowAny]
|
permission_classes = [permissions.AllowAny]
|
||||||
|
queryset = User.objects.all().order_by("first_name", "last_name", "username")
|
||||||
|
filter_backends = [filters.SearchFilter]
|
||||||
|
search_fields = ["username", "first_name", "last_name"]
|
||||||
|
pagination_class = Pagination
|
||||||
|
|
||||||
|
def get_serializer(self, *args, **kwargs):
|
||||||
|
return super().get_serializer(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
# Allows user to modify their own data only
|
# Allows user to modify their own data only
|
||||||
@@ -24,13 +34,13 @@ class ModifyUserView(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
permissions.IsAuthenticated,
|
permissions.IsAuthenticated,
|
||||||
IsOwner
|
IsOwner
|
||||||
]
|
]
|
||||||
model = models.User
|
model = User
|
||||||
serializer_class = serializers.UserSerializer
|
serializer_class = UserSerializer
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
return models.User.objects.get(pk=self.request.user.id)
|
return User.objects.get(pk=self.request.user.id)
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
user = models.User.objects.get(pk=request.user.id)
|
user = User.objects.get(pk=request.user.id)
|
||||||
serializer = serializers.UserSerializer(user)
|
serializer = UserSerializer(user, context={"request":request})
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|||||||
44
grocery_helper/.gitignore
vendored
Normal 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
@@ -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
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"cmake.configureOnOpen": false
|
||||||
|
}
|
||||||
16
grocery_helper/README.md
Normal 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.
|
||||||
29
grocery_helper/analysis_options.yaml
Normal 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
@@ -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
|
||||||
72
grocery_helper/android/app/build.gradle
Normal 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"
|
||||||
|
}
|
||||||
8
grocery_helper/android/app/src/debug/AndroidManifest.xml
Normal 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>
|
||||||
34
grocery_helper/android/app/src/main/AndroidManifest.xml
Normal 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>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.example.grocery_helper
|
||||||
|
|
||||||
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
|
class MainActivity: FlutterActivity() {
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
@@ -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>
|
||||||
18
grocery_helper/android/app/src/main/res/values/styles.xml
Normal 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>
|
||||||
@@ -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>
|
||||||
31
grocery_helper/android/build.gradle
Normal 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
|
||||||
|
}
|
||||||
3
grocery_helper/android/gradle.properties
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
org.gradle.jvmargs=-Xmx1536M
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
||||||
5
grocery_helper/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal 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
|
||||||
11
grocery_helper/android/settings.gradle
Normal 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"
|
||||||
92
grocery_helper/assets/icons/android.svg
Normal 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 |
BIN
grocery_helper/assets/icons/icon.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
93
grocery_helper/assets/images/base.svg
Normal 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 |
72
grocery_helper/assets/images/holes-dark.svg
Normal 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 |
72
grocery_helper/assets/images/holes.svg
Normal 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 |
1
grocery_helper/assets/images/person.svg
Normal 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
@@ -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
|
||||||
26
grocery_helper/ios/Flutter/AppFrameworkInfo.plist
Normal 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>
|
||||||
1
grocery_helper/ios/Flutter/Debug.xcconfig
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#include "Generated.xcconfig"
|
||||||
1
grocery_helper/ios/Flutter/Release.xcconfig
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#include "Generated.xcconfig"
|
||||||
483
grocery_helper/ios/Runner.xcodeproj/project.pbxproj
Normal 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 */;
|
||||||
|
}
|
||||||
7
grocery_helper/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
7
grocery_helper/ios/Runner.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:Runner.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
13
grocery_helper/ios/Runner/AppDelegate.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 624 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 981 B |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
23
grocery_helper/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
vendored
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
grocery_helper/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
vendored
Normal file
|
After Width: | Height: | Size: 68 B |
BIN
grocery_helper/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 68 B |
BIN
grocery_helper/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 68 B |
5
grocery_helper/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
vendored
Normal 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.
|
||||||
37
grocery_helper/ios/Runner/Base.lproj/LaunchScreen.storyboard
Normal 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>
|
||||||
26
grocery_helper/ios/Runner/Base.lproj/Main.storyboard
Normal 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>
|
||||||
54
grocery_helper/ios/Runner/Info.plist
Normal 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>
|
||||||
1
grocery_helper/ios/Runner/Runner-Bridging-Header.h
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#import "GeneratedPluginRegistrant.h"
|
||||||
86
grocery_helper/lib/api/auth.dart
Normal 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(", ");
|
||||||
|
}
|
||||||
3
grocery_helper/lib/api/consts.dart
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
const String baseURL = "http://192.168.0.16:8000";
|
||||||
|
|
||||||
|
const int resultsPerPage = 4;
|
||||||
72
grocery_helper/lib/api/models/homegroup.dart
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
71
grocery_helper/lib/api/models/homegroupinvite.dart
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
28
grocery_helper/lib/api/models/ingredient.dart
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
59
grocery_helper/lib/api/models/recipe.dart
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
89
grocery_helper/lib/api/models/simpleuser.dart
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
115
grocery_helper/lib/api/models/user.dart
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
30
grocery_helper/lib/main.dart
Normal 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())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
165
grocery_helper/lib/pages/profile_page/profile_page.dart
Normal 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();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||