feat: launcher icon, splash, i18n

This commit is contained in:
2026-04-09 01:38:21 +03:00
parent 5d54e1aa03
commit 4b1e876572
98 changed files with 1136 additions and 290 deletions

View File

@@ -355,10 +355,14 @@ endif
.PHONY: icons
icons:
mkdir -p assets/icon
rsvg-convert -h 1024 assets/logo_icon.svg > assets/icon/icon.png
dart run flutter_launcher_icons
.PHONY: splash
splash:
mkdir -p assets/icon
rsvg-convert -h 512 --page-width 1024 --page-height 1024 --top 256 --left 256 assets/logo_icon.svg > assets/icon/splash.png
dart run flutter_native_splash:create
# Website

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

View File

@@ -1,12 +1,9 @@
<?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> -->
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/splash"/>
</item>
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

View File

@@ -1,12 +1,9 @@
<?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> -->
<item>
<bitmap android:gravity="fill" android:src="@drawable/background"/>
</item>
<item>
<bitmap android:gravity="center" android:src="@drawable/splash"/>
</item>
</layer-list>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground>
<inset
android:drawable="@drawable/ic_launcher_foreground"
android:inset="16%" />
</foreground>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -0,0 +1,21 @@
<?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">
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#0082C9</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -5,6 +5,10 @@
<!-- 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>
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</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

View File

@@ -0,0 +1,21 @@
<?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">
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
<item name="android:windowSplashScreenBackground">#0082C9</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#0082C9</color>
</resources>

View File

@@ -5,6 +5,10 @@
<!-- 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>
<item name="android:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</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

BIN
assets/icon/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
assets/icon/splash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

6
assets/logo_icon.svg Normal file
View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<circle cx="256" cy="256" r="256" fill="#0082C9"/>
<g transform="translate(106, 106) scale(12.5)">
<path fill="#FFFFFF" d="M12,3L2,12H5V20H19V12H22L12,3M12,8.75A2.25,2.25 0 0,1 14.25,11A2.25,2.25 0 0,1 12,13.25A2.25,2.25 0 0,1 9.75,11A2.25,2.25 0 0,1 12,8.75M12,15C13.5,15 16.5,15.75 16.5,17.25V18H7.5V17.25C7.5,15.75 10.5,15 12,15Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 421 B

View File

@@ -432,7 +432,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@@ -489,7 +489,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";

View File

@@ -1,122 +1 @@
{
"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"
}
}
{"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":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"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":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 649 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 B

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -16,13 +16,19 @@
<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>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="LaunchBackground" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/>
<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"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="3T2-ad-Qdv"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="RPx-PI-7Xg"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="SdS-ul-q2q"/>
<constraint firstAttribute="trailing" secondItem="tWc-Dq-wcI" secondAttribute="trailing" id="Swv-Gf-Rwn"/>
<constraint firstAttribute="trailing" secondItem="YRO-k0-Ey4" secondAttribute="trailing" id="TQA-XW-tRk"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="duK-uY-Gun"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="kV7-tw-vXt"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="xPn-NY-SIU"/>
</constraints>
</view>
</viewController>
@@ -32,6 +38,7 @@
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
<image name="LaunchImage" width="1024" height="1024"/>
<image name="LaunchBackground" width="1" height="1"/>
</resources>
</document>

View File

@@ -1,70 +1,72 @@
<?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>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Pantry</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>pantry</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>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Pantry</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>pantry</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>UIApplicationSceneManifest</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>flutter</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>flutter</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</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>UIStatusBarHidden</key>
<false/>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</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>
</dict>
</plist>

4
lib/i18n.dart Normal file
View File

@@ -0,0 +1,4 @@
import 'messages.i18n.dart';
/// Global messages accessor for i18n strings.
Messages m = Messages();

View File

@@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import 'i18n.dart';
import 'services/auth_service.dart';
import 'services/checklist_service.dart';
import 'services/prefs_service.dart';
@@ -58,7 +59,7 @@ class PantryAppState extends State<PantryApp> {
final color = ThemingService.instance.effectiveColor;
return MaterialApp(
navigatorKey: rootNavigatorKey,
title: 'Pantry',
title: m.common.appTitle,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: color,

356
lib/messages.i18n.dart Normal file
View File

@@ -0,0 +1,356 @@
// GENERATED FILE, do not edit!
// ignore_for_file: annotate_overrides, non_constant_identifier_names, prefer_single_quotes, unused_element, unused_field, unnecessary_string_interpolations, unnecessary_brace_in_string_interps
import 'package:i18n/i18n.dart' as i18n;
String get _languageCode => 'en';
String _plural(
int count, {
String? zero,
String? one,
String? two,
String? few,
String? many,
String? other,
}) => i18n.plural(
count,
_languageCode,
zero: zero,
one: one,
two: two,
few: few,
many: many,
other: other,
);
String _ordinal(
int count, {
String? zero,
String? one,
String? two,
String? few,
String? many,
String? other,
}) => i18n.ordinal(
count,
_languageCode,
zero: zero,
one: one,
two: two,
few: few,
many: many,
other: other,
);
String _cardinal(
int count, {
String? zero,
String? one,
String? two,
String? few,
String? many,
String? other,
}) => i18n.cardinal(
count,
_languageCode,
zero: zero,
one: one,
two: two,
few: few,
many: many,
other: other,
);
class Messages {
const Messages();
String get locale => "en";
String get languageCode => "en";
CommonMessages get common => CommonMessages(this);
LoginMessages get login => LoginMessages(this);
HomeMessages get home => HomeMessages(this);
NavMessages get nav => NavMessages(this);
ChecklistsMessages get checklists => ChecklistsMessages(this);
RecurrenceMessages get recurrence => RecurrenceMessages(this);
}
class CommonMessages {
final Messages _parent;
const CommonMessages(this._parent);
/// ```dart
/// "Pantry"
/// ```
String get appTitle => """Pantry""";
/// ```dart
/// "Cancel"
/// ```
String get cancel => """Cancel""";
/// ```dart
/// "Delete"
/// ```
String get delete => """Delete""";
/// ```dart
/// "Save"
/// ```
String get save => """Save""";
/// ```dart
/// "Retry"
/// ```
String get retry => """Retry""";
/// ```dart
/// "Logout"
/// ```
String get logout => """Logout""";
/// ```dart
/// "Loading..."
/// ```
String get loading => """Loading...""";
/// ```dart
/// "Error"
/// ```
String get error => """Error""";
}
class LoginMessages {
final Messages _parent;
const LoginMessages(this._parent);
/// ```dart
/// "Connect to your Nextcloud instance"
/// ```
String get connectToNextcloud => """Connect to your Nextcloud instance""";
/// ```dart
/// "Server URL"
/// ```
String get serverUrl => """Server URL""";
/// ```dart
/// "cloud.example.com"
/// ```
String get serverUrlHint => """cloud.example.com""";
/// ```dart
/// "Connect"
/// ```
String get connect => """Connect""";
/// ```dart
/// """
/// Waiting for authentication...
/// Please complete login in your browser.
/// """
/// ```
String get waitingForAuth => """Waiting for authentication...
Please complete login in your browser.""";
/// ```dart
/// "Could not connect to server. Please check the URL."
/// ```
String get couldNotConnect =>
"""Could not connect to server. Please check the URL.""";
/// ```dart
/// "Login failed. Please try again."
/// ```
String get loginFailed => """Login failed. Please try again.""";
}
class HomeMessages {
final Messages _parent;
const HomeMessages(this._parent);
/// ```dart
/// "No houses found. Create one in Nextcloud first."
/// ```
String get noHouses => """No houses found. Create one in Nextcloud first.""";
/// ```dart
/// "Failed to load houses."
/// ```
String get failedToLoadHouses => """Failed to load houses.""";
}
class NavMessages {
final Messages _parent;
const NavMessages(this._parent);
/// ```dart
/// "Checklists"
/// ```
String get checklists => """Checklists""";
/// ```dart
/// "Photo Board"
/// ```
String get photoBoard => """Photo Board""";
/// ```dart
/// "Notes Wall"
/// ```
String get notesWall => """Notes Wall""";
}
class ChecklistsMessages {
final Messages _parent;
const ChecklistsMessages(this._parent);
/// ```dart
/// "No checklists yet."
/// ```
String get noChecklists => """No checklists yet.""";
/// ```dart
/// "No items in this list."
/// ```
String get noItems => """No items in this list.""";
/// ```dart
/// "Failed to load checklists."
/// ```
String get failedToLoad => """Failed to load checklists.""";
/// ```dart
/// "Failed to load items."
/// ```
String get failedToLoadItems => """Failed to load items.""";
/// ```dart
/// "Completed ($count)"
/// ```
String completedCount(int count) => """Completed ($count)""";
/// ```dart
/// "Edit item"
/// ```
String get editItem => """Edit item""";
/// ```dart
/// "Remove item"
/// ```
String get removeItem => """Remove item""";
}
class RecurrenceMessages {
final Messages _parent;
const RecurrenceMessages(this._parent);
/// ```dart
/// "every $unit"
/// ```
String every(String unit) => """every $unit""";
/// ```dart
/// "every $count $unit"
/// ```
String everyN(int count, String unit) => """every $count $unit""";
/// ```dart
/// "on $days"
/// ```
String onDays(String days) => """on $days""";
/// ```dart
/// "${_plural(count, one: 'day', many: 'days')}"
/// ```
String day(int count) => """${_plural(count, one: 'day', many: 'days')}""";
/// ```dart
/// "${_plural(count, one: 'week', many: 'weeks')}"
/// ```
String week(int count) => """${_plural(count, one: 'week', many: 'weeks')}""";
/// ```dart
/// "${_plural(count, one: 'month', many: 'months')}"
/// ```
String month(int count) =>
"""${_plural(count, one: 'month', many: 'months')}""";
/// ```dart
/// "${_plural(count, one: 'year', many: 'years')}"
/// ```
String year(int count) => """${_plural(count, one: 'year', many: 'years')}""";
DayNamesRecurrenceMessages get dayNames => DayNamesRecurrenceMessages(this);
}
class DayNamesRecurrenceMessages {
final RecurrenceMessages _parent;
const DayNamesRecurrenceMessages(this._parent);
/// ```dart
/// "Monday"
/// ```
String get monday => """Monday""";
/// ```dart
/// "Tuesday"
/// ```
String get tuesday => """Tuesday""";
/// ```dart
/// "Wednesday"
/// ```
String get wednesday => """Wednesday""";
/// ```dart
/// "Thursday"
/// ```
String get thursday => """Thursday""";
/// ```dart
/// "Friday"
/// ```
String get friday => """Friday""";
/// ```dart
/// "Saturday"
/// ```
String get saturday => """Saturday""";
/// ```dart
/// "Sunday"
/// ```
String get sunday => """Sunday""";
}
Map<String, String> get messagesMap => {
"""common.appTitle""": """Pantry""",
"""common.cancel""": """Cancel""",
"""common.delete""": """Delete""",
"""common.save""": """Save""",
"""common.retry""": """Retry""",
"""common.logout""": """Logout""",
"""common.loading""": """Loading...""",
"""common.error""": """Error""",
"""login.connectToNextcloud""": """Connect to your Nextcloud instance""",
"""login.serverUrl""": """Server URL""",
"""login.serverUrlHint""": """cloud.example.com""",
"""login.connect""": """Connect""",
"""login.waitingForAuth""": """Waiting for authentication...
Please complete login in your browser.""",
"""login.couldNotConnect""":
"""Could not connect to server. Please check the URL.""",
"""login.loginFailed""": """Login failed. Please try again.""",
"""home.noHouses""": """No houses found. Create one in Nextcloud first.""",
"""home.failedToLoadHouses""": """Failed to load houses.""",
"""nav.checklists""": """Checklists""",
"""nav.photoBoard""": """Photo Board""",
"""nav.notesWall""": """Notes Wall""",
"""checklists.noChecklists""": """No checklists yet.""",
"""checklists.noItems""": """No items in this list.""",
"""checklists.failedToLoad""": """Failed to load checklists.""",
"""checklists.failedToLoadItems""": """Failed to load items.""",
"""checklists.editItem""": """Edit item""",
"""checklists.removeItem""": """Remove item""",
"""recurrence.dayNames.monday""": """Monday""",
"""recurrence.dayNames.tuesday""": """Tuesday""",
"""recurrence.dayNames.wednesday""": """Wednesday""",
"""recurrence.dayNames.thursday""": """Thursday""",
"""recurrence.dayNames.friday""": """Friday""",
"""recurrence.dayNames.saturday""": """Saturday""",
"""recurrence.dayNames.sunday""": """Sunday""",
};

53
lib/messages.i18n.yaml Normal file
View File

@@ -0,0 +1,53 @@
common:
appTitle: Pantry
cancel: Cancel
delete: Delete
save: Save
retry: Retry
logout: Logout
loading: Loading...
error: Error
login:
connectToNextcloud: Connect to your Nextcloud instance
serverUrl: Server URL
serverUrlHint: cloud.example.com
connect: Connect
waitingForAuth: "Waiting for authentication...\nPlease complete login in your browser."
couldNotConnect: Could not connect to server. Please check the URL.
loginFailed: Login failed. Please try again.
home:
noHouses: No houses found. Create one in Nextcloud first.
failedToLoadHouses: Failed to load houses.
nav:
checklists: Checklists
photoBoard: Photo Board
notesWall: Notes Wall
checklists:
noChecklists: No checklists yet.
noItems: No items in this list.
failedToLoad: Failed to load checklists.
failedToLoadItems: Failed to load items.
completedCount(int count): "Completed ($count)"
editItem: Edit item
removeItem: Remove item
recurrence:
every(String unit): "every $unit"
everyN(int count, String unit): "every $count $unit"
onDays(String days): "on $days"
day(int count): "${_plural(count, one: 'day', many: 'days')}"
week(int count): "${_plural(count, one: 'week', many: 'weeks')}"
month(int count): "${_plural(count, one: 'month', many: 'months')}"
year(int count): "${_plural(count, one: 'year', many: 'years')}"
dayNames:
monday: Monday
tuesday: Tuesday
wednesday: Wednesday
thursday: Thursday
friday: Friday
saturday: Saturday
sunday: Sunday

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:pantry/i18n.dart';
import 'package:pantry/models/category.dart' as models;
import 'package:pantry/models/checklist.dart';
import 'package:pantry/services/auth_service.dart';
@@ -149,7 +150,6 @@ class ChecklistItemTile extends StatelessWidget {
}
static String _formatRrule(String rrule) {
// Simple human-readable rrule summary
final parts = rrule.split(';');
final map = <String, String>{};
for (final part in parts) {
@@ -163,39 +163,31 @@ class ChecklistItemTile extends StatelessWidget {
if (freq == null) return rrule;
final r = m.recurrence;
final dayNames = {
'MO': 'Monday',
'TU': 'Tuesday',
'WE': 'Wednesday',
'TH': 'Thursday',
'FR': 'Friday',
'SA': 'Saturday',
'SU': 'Sunday',
'MO': r.dayNames.monday,
'TU': r.dayNames.tuesday,
'WE': r.dayNames.wednesday,
'TH': r.dayNames.thursday,
'FR': r.dayNames.friday,
'SA': r.dayNames.saturday,
'SU': r.dayNames.sunday,
};
const singularNames = {
'daily': 'day',
'weekly': 'week',
'monthly': 'month',
'yearly': 'year',
};
const pluralNames = {
'daily': 'days',
'weekly': 'weeks',
'monthly': 'months',
'yearly': 'years',
final unit = switch (freq) {
'daily' => r.day(interval),
'weekly' => r.week(interval),
'monthly' => r.month(interval),
'yearly' => r.year(interval),
_ => freq,
};
String prefix;
if (interval == 1) {
prefix = 'every ${singularNames[freq] ?? freq}';
} else {
prefix = 'every $interval ${pluralNames[freq] ?? freq}';
}
final prefix = interval == 1 ? r.every(unit) : r.everyN(interval, unit);
if (byDay != null && freq == 'weekly') {
final days = byDay.split(',').map((d) => dayNames[d] ?? d).join(', ');
return '$prefix on $days';
return '$prefix ${r.onDays(days)}';
}
return prefix;
@@ -292,23 +284,23 @@ class _MoreMenuButton extends StatelessWidget {
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
itemBuilder: (context) => [
const PopupMenuItem(
PopupMenuItem(
value: 'edit',
child: Row(
children: [
Icon(Icons.edit, size: 18),
SizedBox(width: 8),
Text('Edit item'),
const Icon(Icons.edit, size: 18),
const SizedBox(width: 8),
Text(m.checklists.editItem),
],
),
),
const PopupMenuItem(
PopupMenuItem(
value: 'remove',
child: Row(
children: [
Icon(Icons.delete, size: 18),
SizedBox(width: 8),
Text('Remove item'),
const Icon(Icons.delete, size: 18),
const SizedBox(width: 8),
Text(m.checklists.removeItem),
],
),
),

View File

@@ -1,4 +1,5 @@
import 'package:flutter/foundation.dart';
import 'package:pantry/i18n.dart';
import 'package:pantry/models/category.dart' as models;
import 'package:pantry/models/checklist.dart';
import 'package:pantry/services/category_service.dart';
@@ -86,7 +87,7 @@ class ChecklistsController extends ChangeNotifier {
}
} catch (e) {
debugPrint('[ChecklistsController] Failed to load: $e');
_error = 'Failed to load checklists.';
_error = m.checklists.failedToLoad;
_isLoading = false;
notifyListeners();
}
@@ -120,7 +121,7 @@ class ChecklistsController extends ChangeNotifier {
} catch (e) {
debugPrint('[ChecklistsController] Failed to load items: $e');
if (cached == null) {
_error = 'Failed to load items.';
_error = m.checklists.failedToLoadItems;
_isLoading = false;
notifyListeners();
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:pantry/i18n.dart';
import 'package:provider/provider.dart';
import 'package:pantry/models/checklist.dart';
@@ -61,7 +62,7 @@ class _ChecklistsBody extends StatelessWidget {
const SizedBox(height: 16),
FilledButton(
onPressed: controller.load,
child: const Text('Retry'),
child: Text(m.common.retry),
),
],
),
@@ -70,7 +71,7 @@ class _ChecklistsBody extends StatelessWidget {
}
if (controller.lists.isEmpty) {
return const Center(child: Text('No checklists yet.'));
return Center(child: Text(m.checklists.noChecklists));
}
Widget itemsArea;
@@ -87,7 +88,7 @@ class _ChecklistsBody extends StatelessWidget {
const SizedBox(height: 16),
FilledButton(
onPressed: controller.load,
child: const Text('Retry'),
child: Text(m.common.retry),
),
],
),
@@ -185,9 +186,9 @@ class _ItemList extends StatelessWidget {
if (controller.items.isEmpty) {
return ListView(
children: const [
SizedBox(height: 100),
Center(child: Text('No items in this list.')),
children: [
const SizedBox(height: 100),
Center(child: Text(m.checklists.noItems)),
],
);
}
@@ -209,7 +210,7 @@ class _ItemList extends StatelessWidget {
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: Text(
'Completed (${checked.length})',
m.checklists.completedCount(checked.length),
style: Theme.of(context).textTheme.labelMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),

View File

@@ -1,4 +1,5 @@
import 'package:flutter/foundation.dart';
import 'package:pantry/i18n.dart';
import 'package:pantry/models/house.dart';
import 'package:pantry/services/house_service.dart';
import 'package:pantry/services/prefs_service.dart';
@@ -25,7 +26,7 @@ class HomeController extends ChangeNotifier {
_houses = await HouseService.instance.getHouses();
if (_houses.isEmpty) {
_error = 'No houses found. Create one in Nextcloud first.';
_error = m.home.noHouses;
_isLoading = false;
notifyListeners();
return;
@@ -45,7 +46,7 @@ class HomeController extends ChangeNotifier {
notifyListeners();
} catch (e) {
debugPrint('[HomeController] Failed to load houses: $e');
_error = 'Failed to load houses.';
_error = m.home.failedToLoadHouses;
_isLoading = false;
notifyListeners();
}

View File

@@ -3,6 +3,7 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pantry/i18n.dart';
import 'package:pantry/models/house.dart';
import 'package:pantry/services/auth_service.dart';
import 'package:pantry/views/checklists/checklists_view.dart';
@@ -59,7 +60,7 @@ class _HomeViewBodyState extends State<_HomeViewBody> {
return Scaffold(
appBar: AppBar(
title: Text(controller.currentHouse?.name ?? 'Pantry'),
title: Text(controller.currentHouse?.name ?? m.common.appTitle),
actions: [
_UserMenuButton(
houses: controller.houses,
@@ -74,15 +75,18 @@ class _HomeViewBodyState extends State<_HomeViewBody> {
bottomNavigationBar: NavigationBar(
selectedIndex: _tabIndex,
onDestinationSelected: (i) => setState(() => _tabIndex = i),
destinations: const [
destinations: [
NavigationDestination(
icon: Icon(Icons.assignment_turned_in),
label: 'Checklists',
icon: const Icon(Icons.assignment_turned_in),
label: m.nav.checklists,
),
NavigationDestination(icon: Icon(Icons.photo), label: 'Photo Board'),
NavigationDestination(
icon: Icon(Icons.insert_drive_file),
label: 'Notes Wall',
icon: const Icon(Icons.photo),
label: m.nav.photoBoard,
),
NavigationDestination(
icon: const Icon(Icons.insert_drive_file),
label: m.nav.notesWall,
),
],
),
@@ -105,7 +109,7 @@ class _HomeViewBodyState extends State<_HomeViewBody> {
const SizedBox(height: 16),
FilledButton(
onPressed: controller.load,
child: const Text('Retry'),
child: Text(m.common.retry),
),
],
),
@@ -121,9 +125,9 @@ class _HomeViewBodyState extends State<_HomeViewBody> {
houseId: houseId,
);
case 1:
return const Center(child: Text('Photo Board'));
return Center(child: Text(m.nav.photoBoard));
case 2:
return const Center(child: Text('Notes Wall'));
return Center(child: Text(m.nav.notesWall));
default:
return const SizedBox.shrink();
}
@@ -192,13 +196,13 @@ class _UserMenuButton extends StatelessWidget {
),
],
const PopupMenuDivider(),
const PopupMenuItem<String>(
PopupMenuItem<String>(
value: 'logout',
child: Row(
children: [
Icon(Icons.logout, size: 18),
SizedBox(width: 8),
Text('Logout'),
const Icon(Icons.logout, size: 18),
const SizedBox(width: 8),
Text(m.common.logout),
],
),
),

View File

@@ -1,6 +1,7 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:pantry/i18n.dart';
import 'package:pantry/services/auth_service.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -62,7 +63,7 @@ class LoginController extends ChangeNotifier {
_startPolling(normalizedUrl);
} catch (e) {
_isLoading = false;
_error = 'Could not connect to server. Please check the URL.';
_error = m.login.couldNotConnect;
notifyListeners();
}
}

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:pantry/i18n.dart';
import 'package:provider/provider.dart';
import 'login_controller.dart';
@@ -73,7 +74,7 @@ class _LoginViewBody extends StatelessWidget {
),
const SizedBox(height: 16),
Text(
'Pantry',
m.common.appTitle,
style: Theme.of(context).textTheme.headlineLarge?.copyWith(
fontWeight: FontWeight.bold,
),
@@ -81,7 +82,7 @@ class _LoginViewBody extends StatelessWidget {
),
const SizedBox(height: 8),
Text(
'Connect to your Nextcloud instance',
m.login.connectToNextcloud,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
@@ -92,8 +93,8 @@ class _LoginViewBody extends StatelessWidget {
controller: urlController,
enabled: !controller.isLoading && !controller.isPolling,
decoration: InputDecoration(
labelText: 'Server URL',
hintText: 'cloud.example.com',
labelText: m.login.serverUrl,
hintText: m.login.serverUrlHint,
prefixIcon: const Icon(Icons.cloud_outlined),
border: const OutlineInputBorder(),
errorText: controller.error,
@@ -107,14 +108,14 @@ class _LoginViewBody extends StatelessWidget {
const LinearProgressIndicator(),
const SizedBox(height: 16),
Text(
'Waiting for authentication...\nPlease complete login in your browser.',
m.login.waitingForAuth,
style: Theme.of(context).textTheme.bodySmall,
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
OutlinedButton(
onPressed: controller.cancelLogin,
child: const Text('Cancel'),
child: Text(m.common.cancel),
),
] else
FilledButton(
@@ -127,7 +128,7 @@ class _LoginViewBody extends StatelessWidget {
width: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text('Connect'),
: Text(m.login.connect),
),
],
),

View File

@@ -1,6 +1,38 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f
url: "https://pub.dev"
source: hosted
version: "85.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "974859dc0ff5f37bc4313244b3218c791810d03ab3470a579580279ba971a48d"
url: "https://pub.dev"
source: hosted
version: "7.7.1"
ansicolor:
dependency: transitive
description:
name: ansicolor
sha256: "50e982d500bc863e1d703448afdbf9e5a72eb48840a4f766fa361ffd6877055f"
url: "https://pub.dev"
source: hosted
version: "2.0.3"
archive:
dependency: transitive
description:
name: archive
sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff
url: "https://pub.dev"
source: hosted
version: "4.0.9"
args:
dependency: transitive
description:
@@ -25,6 +57,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
build:
dependency: transitive
description:
name: build
sha256: "51dc711996cbf609b90cbe5b335bbce83143875a9d58e4b5c6d3c4f684d3dda7"
url: "https://pub.dev"
source: hosted
version: "2.5.4"
build_config:
dependency: transitive
description:
name: build_config
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
url: "https://pub.dev"
source: hosted
version: "1.1.2"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957
url: "https://pub.dev"
source: hosted
version: "4.1.1"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
sha256: ee4257b3f20c0c90e72ed2b57ad637f694ccba48839a821e87db762548c22a62
url: "https://pub.dev"
source: hosted
version: "2.5.4"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "382a4d649addbfb7ba71a3631df0ec6a45d5ab9b098638144faf27f02778eb53"
url: "https://pub.dev"
source: hosted
version: "2.5.4"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "85fbbb1036d576d966332a3f5ce83f2ce66a40bea1a94ad2d5fc29a19a0d3792"
url: "https://pub.dev"
source: hosted
version: "9.1.2"
built_collection:
dependency: transitive
description:
name: built_collection
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
url: "https://pub.dev"
source: hosted
version: "5.1.1"
built_value:
dependency: transitive
description:
name: built_value
sha256: "0730c18c770d05636a8f945c32a4d7d81cb6e0f0148c8db4ad12e7748f7e49af"
url: "https://pub.dev"
source: hosted
version: "8.12.5"
characters:
dependency: transitive
description:
@@ -33,6 +129,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.1"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
url: "https://pub.dev"
source: hosted
version: "2.0.4"
cli_util:
dependency: transitive
description:
name: cli_util
sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
url: "https://pub.dev"
source: hosted
version: "0.4.2"
clock:
dependency: transitive
description:
@@ -49,6 +161,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.0"
code_builder:
dependency: transitive
description:
name: code_builder
sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d"
url: "https://pub.dev"
source: hosted
version: "4.11.1"
collection:
dependency: transitive
description:
@@ -57,6 +177,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.19.1"
convert:
dependency: transitive
description:
name: convert
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://pub.dev"
source: hosted
version: "3.1.2"
crypto:
dependency: transitive
description:
@@ -65,6 +193,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.7"
csslib:
dependency: transitive
description:
name: csslib
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
cupertino_icons:
dependency: "direct main"
description:
@@ -73,6 +209,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.9"
dart_style:
dependency: transitive
description:
name: dart_style
sha256: "8a0e5fba27e8ee025d2ffb4ee820b4e6e2cf5e4246a6b1a477eb66866947e0bb"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
dbus:
dependency: transitive
description:
@@ -105,11 +249,27 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.1"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
url: "https://pub.dev"
source: hosted
version: "1.1.1"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_launcher_icons:
dependency: "direct dev"
description:
name: flutter_launcher_icons
sha256: "10f13781741a2e3972126fae08393d3c4e01fa4cd7473326b94b72cf594195e7"
url: "https://pub.dev"
source: hosted
version: "0.14.4"
flutter_lints:
dependency: "direct dev"
description:
@@ -118,6 +278,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.0.0"
flutter_native_splash:
dependency: "direct dev"
description:
name: flutter_native_splash
sha256: "4fb9f4113350d3a80841ce05ebf1976a36de622af7d19aca0ca9a9911c7ff002"
url: "https://pub.dev"
source: hosted
version: "2.4.7"
flutter_secure_storage:
dependency: "direct main"
description:
@@ -184,6 +352,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
url: "https://pub.dev"
source: hosted
version: "4.0.0"
glob:
dependency: transitive
description:
@@ -192,6 +368,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.3"
graphs:
dependency: transitive
description:
name: graphs
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
hooks:
dependency: transitive
description:
@@ -200,6 +384,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.2"
html:
dependency: transitive
description:
name: html
sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
url: "https://pub.dev"
source: hosted
version: "0.15.6"
http:
dependency: "direct main"
description:
@@ -208,6 +400,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.6.0"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
url: "https://pub.dev"
source: hosted
version: "3.2.2"
http_parser:
dependency: transitive
description:
@@ -216,6 +416,39 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.1.2"
i18n:
dependency: "direct main"
description:
path: "."
ref: HEAD
resolved-ref: a808af831cd140449d1a5b9ddbe18f53b88de259
url: "https://github.com/chenasraf/i18n"
source: git
version: "4.2.4"
image:
dependency: transitive
description:
name: image
sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce
url: "https://pub.dev"
source: hosted
version: "4.8.0"
intl:
dependency: "direct main"
description:
name: intl
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
url: "https://pub.dev"
source: hosted
version: "0.20.2"
io:
dependency: transitive
description:
name: io
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
url: "https://pub.dev"
source: hosted
version: "1.0.5"
jni:
dependency: transitive
description:
@@ -232,6 +465,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.1"
js:
dependency: transitive
description:
name: js
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
url: "https://pub.dev"
source: hosted
version: "0.7.2"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8
url: "https://pub.dev"
source: hosted
version: "4.11.0"
leak_tracker:
dependency: transitive
description:
@@ -296,6 +545,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.17.0"
mime:
dependency: transitive
description:
name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
native_toolchain_c:
dependency: transitive
description:
@@ -432,6 +689,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.8"
pool:
dependency: transitive
description:
name: pool
sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d"
url: "https://pub.dev"
source: hosted
version: "1.5.2"
posix:
dependency: transitive
description:
name: posix
sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07"
url: "https://pub.dev"
source: hosted
version: "6.5.0"
provider:
dependency: "direct main"
description:
@@ -448,6 +721,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.0"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
quick_log:
dependency: transitive
description:
name: quick_log
sha256: "94b0db8fe600c27788a8057496f0b51fdb799b5b35d8740ca141c9faf349dc48"
url: "https://pub.dev"
source: hosted
version: "5.5.3"
rxdart:
dependency: transitive
description:
name: rxdart
sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
url: "https://pub.dev"
source: hosted
version: "0.28.0"
shelf:
dependency: transitive
description:
name: shelf
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.dev"
source: hosted
version: "1.4.2"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
sky_engine:
dependency: transitive
description: flutter
@@ -477,6 +790,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
stream_transform:
dependency: transitive
description:
name: stream_transform
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
url: "https://pub.dev"
source: hosted
version: "2.1.1"
string_scanner:
dependency: transitive
description:
@@ -501,6 +822,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.10"
timing:
dependency: transitive
description:
name: timing
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
typed_data:
dependency: transitive
description:
@@ -509,6 +838,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
universal_io:
dependency: transitive
description:
name: universal_io
sha256: f63cbc48103236abf48e345e07a03ce5757ea86285ed313a6a032596ed9301e2
url: "https://pub.dev"
source: hosted
version: "2.3.1"
url_launcher:
dependency: "direct main"
description:
@@ -629,6 +966,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
watcher:
dependency: transitive
description:
name: watcher
sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
web:
dependency: transitive
description:
@@ -637,6 +982,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.1"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
url: "https://pub.dev"
source: hosted
version: "3.0.3"
win32:
dependency: transitive
description:

View File

@@ -41,6 +41,9 @@ dependencies:
flutter_svg: ^2.2.4
wakelock_plus: ^1.5.1
path_provider: ^2.1.5
intl: ^0.20.2
i18n:
git: https://github.com/chenasraf/i18n
dev_dependencies:
flutter_test:
@@ -52,6 +55,9 @@ dev_dependencies:
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^6.0.0
flutter_launcher_icons: ^0.14.4
flutter_native_splash: ^2.4.7
build_runner: ^2.5.4
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
@@ -97,3 +103,21 @@ flutter:
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package
flutter_launcher_icons:
android: true
ios: true
remove_alpha_ios: true
image_path: "assets/icon/icon.png"
adaptive_icon_background: "#0082C9"
adaptive_icon_foreground: "assets/icon/icon.png"
web:
generate: true
image_path: "assets/icon/icon.png"
flutter_native_splash:
color: "#0082C9"
image: "assets/icon/splash.png"
android_12:
color: "#0082C9"
image: "assets/icon/splash.png"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 917 B

After

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,6 +1,4 @@
<!DOCTYPE html>
<html>
<head>
<!DOCTYPE html><html><head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
@@ -27,12 +25,83 @@
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<link rel="icon" type="image/png" href="favicon.png">
<title>pantry</title>
<link rel="manifest" href="manifest.json">
<style id="splash-screen-style">
html {
height: 100%
}
body {
margin: 0;
min-height: 100%;
background-color: #0082C9;
background-size: 100% 100%;
}
.center {
margin: 0;
position: absolute;
top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.contain {
display:block;
width:100%; height:100%;
object-fit: contain;
}
.stretch {
display:block;
width:100%; height:100%;
}
.cover {
display:block;
width:100%; height:100%;
object-fit: cover;
}
.bottom {
position: absolute;
bottom: 0;
left: 50%;
-ms-transform: translate(-50%, 0);
transform: translate(-50%, 0);
}
.bottomLeft {
position: absolute;
bottom: 0;
left: 0;
}
.bottomRight {
position: absolute;
bottom: 0;
right: 0;
}
</style>
<script id="splash-screen-script">
function removeSplashFromWeb() {
document.getElementById("splash")?.remove();
document.getElementById("splash-branding")?.remove();
document.body.style.background = "transparent";
}
</script>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
</head>
<body>
<picture id="splash">
<source srcset="splash/img/light-1x.png 1x, splash/img/light-2x.png 2x, splash/img/light-3x.png 3x, splash/img/light-4x.png 4x" media="(prefers-color-scheme: light)">
<source srcset="splash/img/dark-1x.png 1x, splash/img/dark-2x.png 2x, splash/img/dark-3x.png 3x, splash/img/dark-4x.png 4x" media="(prefers-color-scheme: dark)">
<img class="center" aria-hidden="true" src="splash/img/light-1x.png" alt="">
</picture>
<!--
You can customize the "flutter_bootstrap.js" script.
This is useful to provide a custom configuration to the Flutter loader
@@ -41,6 +110,7 @@
For more details:
* https://docs.flutter.dev/platform-integration/web/initialization
-->
<script src="flutter_bootstrap.js" async></script>
</body>
</html>
<script src="flutter_bootstrap.js" async=""></script>
</body></html>

View File

@@ -32,4 +32,4 @@
"purpose": "maskable"
}
]
}
}

BIN
web/splash/img/dark-1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
web/splash/img/dark-2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
web/splash/img/dark-3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
web/splash/img/dark-4x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
web/splash/img/light-1x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
web/splash/img/light-2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
web/splash/img/light-3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
web/splash/img/light-4x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB