Initial commit

This commit is contained in:
joe 2026-02-09 09:06:36 +09:00
commit f67e266569
135 changed files with 13270 additions and 0 deletions

58
.gitignore vendored Normal file
View file

@ -0,0 +1,58 @@
# General
*.log
.DS_Store
# system-specific
*~
*.swp
# IDE configurations
.idea/
.vscode/
# Flutter/Dart specific
.dart_tool/
.flutter-plugins-dependencies
# ios/Pods/ is often ignored, but sometimes specific projects include it
# ios/Pods/
# For macOS desktop builds
macos/Runner/Flutter/AppFrameworkInfo.plist
macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
# For Windows desktop builds
windows/flutter/ephemeral/
# Android specific
android/.gradle/
android/gradle/wrapper/gradle-wrapper.properties
android/app/build.gradle.kts # Usually generated, but can be ignored if specific configurations are managed elsewhere or to prevent accidental commits
android/app/build/ # Build artifacts
android/captures/ # For Android Studio captures
android/gradle.properties # Usually fine to commit, but depends on project setup
# iOS specific
ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json # Example for specific asset files, usually not ignored unless generated
ios/Runner.xcworkspace/contents.xcworkspacedata
ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
# Web specific
web/icons/
web/manifest.json
# Build output
build/
# Dependency caching
.pub-cache/
# OS-generated files
.DS_Store
Thumbs.db
# IDE settings (IntelliJ IDEA)
*.iml
# Temporary files
*.tmp

45
.metadata Normal file
View file

@ -0,0 +1,45 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "67323de285b00232883f53b84095eb72be97d35c"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 67323de285b00232883f53b84095eb72be97d35c
base_revision: 67323de285b00232883f53b84095eb72be97d35c
- platform: android
create_revision: 67323de285b00232883f53b84095eb72be97d35c
base_revision: 67323de285b00232883f53b84095eb72be97d35c
- platform: ios
create_revision: 67323de285b00232883f53b84095eb72be97d35c
base_revision: 67323de285b00232883f53b84095eb72be97d35c
- platform: linux
create_revision: 67323de285b00232883f53b84095eb72be97d35c
base_revision: 67323de285b00232883f53b84095eb72be97d35c
- platform: macos
create_revision: 67323de285b00232883f53b84095eb72be97d35c
base_revision: 67323de285b00232883f53b84095eb72be97d35c
- platform: web
create_revision: 67323de285b00232883f53b84095eb72be97d35c
base_revision: 67323de285b00232883f53b84095eb72be97d35c
- platform: windows
create_revision: 67323de285b00232883f53b84095eb72be97d35c
base_revision: 67323de285b00232883f53b84095eb72be97d35c
# 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'

46
README.md Normal file
View file

@ -0,0 +1,46 @@
# gemi_invoice
A new Flutter project.
## Getting Started
!! 日本語が主体のアプリです。アプリの基本言語は日本語です。コメントも日本語と英語併記です。!!
!! 可読性は重視しません。AIの開発効率を最大に上げて下さい。!!
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.
## 販売アシスト1号の機能
販売アシスト1号は、営業マンの販売業務を支援するためのアプリケーションです。主な機能は以下の通りです。
- ファイル名の規則 "{date}({請求等}){顧客名}_{件名}_{金額カンマ付}円_{sha256}" 20251202(請求)佐々木製作所_あの件_20,000円_25ab85cc9988
- 伝票(件名)の管理
- 管理ではファイルの様にオブジェクトとして管理します。
- 顧客マスター管理
- 商品マスター管理
- 見積納品請求領収証の作成
- 見積納品請求領収証の一覧表示
- 見積納品請求領収証のPDF出力
- PDFの管理
- GPSと顧客マスターを連携し座標の履歴を記録する機能
- フォーム入力時にGPS情報で自動的に顧客マスターから候補を選出する機能
- 入力時にQRコードやOCRをGoogleレンズを使ってアシストします
- レシート印刷するポータブルプリンタを使って領収証印刷する機能
- 印刷時にQRコードを印刷する機能
- Googleドライブにバックアップする機能
- 未来の機能(今は実装しない可能にしておく)odooとの同期・連携 伝票単位で同期
- アプリの基本言語は日本語です。
- コメントも日本語と英語併記です。
- 可読性は重視しません。AIの開発効率を最大に上げるものです。
- 表示言語は日本語です。環境により英語を表示する機能は未来に実装予定です。
- 見積・納品・請求・領収証はフォームがほぼ同じですが、項目が時間と共に変化するので件名が同じでも明細は異なる場合があります。
- 伝票はsqliteで管理します。

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

28
analysis_options.yaml Normal file
View file

@ -0,0 +1,28 @@
# 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.dev/lints.
#
# 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

14
android/.gitignore vendored Normal file
View file

@ -0,0 +1,14 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
.cxx/
# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks

View file

@ -0,0 +1,44 @@
plugins {
id("com.android.application")
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
}
android {
namespace = "com.example.gemi_invoice"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.gemi_invoice"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
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.getByName("debug")
}
}
}
flutter {
source = "../.."
}

View file

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

View file

@ -0,0 +1,54 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="file" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="content" />
</intent>
<intent>
<action android:name="android.intent.action.PICK" />
<data android:mimeType="vnd.android.cursor.dir/contact" />
</intent>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT" />
<data android:mimeType="text/plain" />
</intent>
</queries>
<application
android:label="gemi_invoice"
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">
<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>
<meta-data android:name="flutterEmbedding" android:value="2" />
</application>
</manifest>

View file

@ -0,0 +1,5 @@
package com.example.gemi_invoice
import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity()

View file

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

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

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

View file

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

View file

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 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>

24
android/build.gradle.kts Normal file
View file

@ -0,0 +1,24 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
val newBuildDir: Directory =
rootProject.layout.buildDirectory
.dir("../../build")
.get()
rootProject.layout.buildDirectory.value(newBuildDir)
subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory)
}

View file

@ -0,0 +1,2 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true

View file

@ -0,0 +1,26 @@
pluginManagement {
val flutterSdkPath =
run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
flutterSdkPath
}
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.11.1" apply false
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
}
include(":app")

BIN
assets/fonts/ipaexg.ttf Executable file

Binary file not shown.

2478
dir.tree Normal file

File diff suppressed because it is too large Load diff

28
fix_build_errors.md Normal file
View file

@ -0,0 +1,28 @@
# 依頼内容
Flutterプロジェクトのビルドエラーを解消し、顧客マスター管理GPSソート/電話帳連携)と請求書履歴一覧の機能を完成させてください。
# 現状の問題
1. `CustomerPickerModal` 内で DB の `Customer` 型とアプリ用 `Customer` モデルが衝突し、型エラーが発生している。
2. `pdf_list_screen.dart` で、DB から顧客情報付きの請求データを取得する `InvoiceWithCustomer` 型や `watchAllInvoices()` メソッドが見つからずエラーになっている。
3. `invoice_input_screen.dart``CustomerPickerModal` を呼び出す際、古い引数 `existingCustomers` を渡しておりエラーになっている。
# 実行ステップ
1. **lib/data/database.dart の更新**:
- `Invoices``Customers` を結合して取得するための `InvoiceWithCustomer` クラスを定義してください。
- `AppDatabase` クラスに、最新順でデータを流す `Stream<List<InvoiceWithCustomer>> watchAllInvoices()` メソッドを実装してください。
- その後、`flutter pub run build_runner build --delete-conflicting-outputs` を実行してコード生成を完了させてください。
2. **lib/screens/customer_picker_modal.dart の修正**:
- インポートで `../models/invoice_models.dart``app_model` として別名を付け、DBの `Customer` 型と明確に区別してください。
- GPS座標latitude/longitudeを使用して、現在地に近い順にリストをソートするロジックを実装してください。
- 電話帳flutter_contactsからの取り込みと、DBへの保存insertOnConflictUpdateが正常に動くようにしてください。
3. **lib/screens/pdf_list_screen.dart の修正**:
- `database.watchAllInvoices()` を使用して履歴一覧を表示するようにしてください。
- リストタップ時に、DBのモデルからアプリ用の `Invoice` モデルへ変換して詳細画面へ遷移させてください。
4. **lib/screens/invoice_input_screen.dart の修正**:
- `CustomerPickerModal` の呼び出し箇所から、存在しない引数 `existingCustomers` を削除してください。
# 完了条件
`flutter build apk --debug` がエラーなく通り、かつ顧客マスターのGPSソートが機能すること。

34
ios/.gitignore vendored Normal file
View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,616 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
remoteInfo = Runner;
};
/* End PBXContainerItemProxy 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>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
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 */
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C807B294A618700263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
};
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 */,
331C8082294A63A400263BE5 /* RunnerTests */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
);
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 */
331C8080294A63A400263BE5 /* RunnerTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */,
);
buildRules = (
);
dependencies = (
331C8086294A63A400263BE5 /* PBXTargetDependency */,
);
name = RunnerTests;
productName = RunnerTests;
productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
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 = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C8080294A63A400263BE5 = {
CreatedOnToolsVersion = 14.0;
TestTargetID = 97C146ED1CF9000F007C117D;
};
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 */,
331C8080294A63A400263BE5 /* RunnerTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
331C807F294A63A400263BE5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
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 = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
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 */
331C807D294A63A400263BE5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency 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;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
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;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
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 = 13.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.gemiInvoice;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.gemiInvoice.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Debug;
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.gemiInvoice.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Release;
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.gemiInvoice.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
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;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
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 = 13.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;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
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;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
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 = 13.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.gemiInvoice;
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.gemiInvoice;
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 */
331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C8088294A63A400263BE5 /* Debug */,
331C8089294A63A400263BE5 /* Release */,
331C808A294A63A400263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}

View file

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

View file

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

View file

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

View file

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

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View file

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

View file

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

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

49
ios/Runner/Info.plist Normal file
View file

@ -0,0 +1,49 @@
<?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>Gemi Invoice</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>gemi_invoice</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>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>

View file

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

View file

@ -0,0 +1,12 @@
import Flutter
import UIKit
import XCTest
class RunnerTests: XCTestCase {
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}

111
lib/data/database.dart Normal file
View file

@ -0,0 +1,111 @@
// gemi_invoice/lib/data/database.dart
// version: 1.4.0 (Fix: Added missing types and methods for List Screen)
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
import 'dart:io';
part 'database.g.dart';
class Customers extends Table {
TextColumn get id => text()();
TextColumn get displayName => text()();
TextColumn get formalName => text()();
TextColumn get address => text().nullable()();
TextColumn get zipCode => text().nullable()();
TextColumn get department => text().nullable()();
RealColumn get latitude => real().nullable()();
RealColumn get longitude => real().nullable()();
DateTimeColumn get lastUpdatedAt => dateTime().nullable()();
@override
Set<Column> get primaryKey => {id};
}
class Products extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get name => text()();
IntColumn get defaultPrice => integer()();
TextColumn get category => text().nullable()();
}
class Invoices extends Table {
TextColumn get id => text()();
TextColumn get customerId => text().references(Customers, #id)();
DateTimeColumn get date => dateTime()();
TextColumn get type => text()();
TextColumn get filePath => text().nullable()();
TextColumn get notes => text().nullable()();
IntColumn get totalAmount => integer()();
@override
Set<Column> get primaryKey => {id};
}
class InvoiceItems extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get invoiceId => text().references(Invoices, #id)();
TextColumn get description => text()();
IntColumn get quantity => integer()();
IntColumn get unitPrice => integer()();
}
// 使
class InvoiceWithCustomer {
final Invoices invoice;
final Customers customer;
InvoiceWithCustomer(this.invoice, this.customer);
}
@DriftDatabase(tables: [Customers, Products, Invoices, InvoiceItems])
class AppDatabase extends _$AppDatabase {
AppDatabase() : super(_openConnection());
@override
int get schemaVersion => 1;
// --- DAOメソッド ---
// 使
Stream<List<InvoiceWithCustomer>> watchAllInvoices() {
final query = select(
invoices,
).join([innerJoin(customers, customers.id.equalsExp(invoices.customerId))]);
query.orderBy([OrderingTerm.desc(invoices.date)]);
return query.watch().map((rows) {
return rows.map((row) {
return InvoiceWithCustomer(
row.readTable(invoices),
row.readTable(customers),
);
}).toList();
});
}
Future<void> saveFullInvoice(
InvoicesCompanion invoice,
List<InvoiceItemsCompanion> items,
) async {
await transaction(() async {
await into(invoices).insertOnConflictUpdate(invoice);
await (delete(
invoiceItems,
)..where((t) => t.invoiceId.equals(invoice.id.value))).go();
for (var item in items) {
await into(invoiceItems).insert(item);
}
});
}
Future<List<Customer>> getNearbyCustomers(double lat, double lon) {
return select(customers).get();
}
}
LazyDatabase _openConnection() {
return LazyDatabase(() async {
final dbFolder = await getApplicationDocumentsDirectory();
final file = File(p.join(dbFolder.path, 'db.sqlite'));
return NativeDatabase(file);
});
}

3070
lib/data/database.g.dart Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,99 @@
import '../models/invoice_models.dart';
///
/// Odoo IDodooId
class Product {
final String id; // ID
final int? odooId; // Odoo上の product.product ID (nullの場合は未同期)
final String name; //
final int defaultUnitPrice; //
final String? category; //
const Product({
required this.id,
this.odooId,
required this.name,
required this.defaultUnitPrice,
this.category,
});
/// InvoiceItem
InvoiceItem toInvoiceItem({int quantity = 1}) {
return InvoiceItem(
description: name,
quantity: quantity,
unitPrice: defaultUnitPrice,
);
}
///
Product copyWith({
String? id,
int? odooId,
String? name,
int? defaultUnitPrice,
String? category,
}) {
return Product(
id: id ?? this.id,
odooId: odooId ?? this.odooId,
name: name ?? this.name,
defaultUnitPrice: defaultUnitPrice ?? this.defaultUnitPrice,
category: category ?? this.category,
);
}
/// JSON変換 (Odoo同期用)
Map<String, dynamic> toJson() {
return {
'id': id,
'odoo_id': odooId,
'name': name,
'default_unit_price': defaultUnitPrice,
'category': category,
};
}
/// JSONからモデルを生成
factory Product.fromJson(Map<String, dynamic> json) {
return Product(
id: json['id'],
odooId: json['odoo_id'],
name: json['name'],
defaultUnitPrice: json['default_unit_price'],
category: json['category'],
);
}
}
///
class ProductMaster {
static const List<Product> products = [
Product(id: 'S001', name: 'システム開発費', defaultUnitPrice: 500000, category: '開発'),
Product(id: 'S002', name: '保守・メンテナンス費', defaultUnitPrice: 50000, category: '運用'),
Product(id: 'S003', name: '技術コンサルティング', defaultUnitPrice: 100000, category: '開発'),
Product(id: 'G001', name: 'ライセンス料 (Pro)', defaultUnitPrice: 15000, category: '製品'),
Product(id: 'G002', name: '初期導入セットアップ', defaultUnitPrice: 30000, category: '製品'),
Product(id: 'M001', name: 'ハードウェア一式', defaultUnitPrice: 250000, category: '物品'),
Product(id: 'Z001', name: '諸経費', defaultUnitPrice: 5000, category: 'その他'),
];
///
static List<String> get categories {
return products.map((p) => p.category ?? 'その他').toSet().toList();
}
///
static List<Product> getProductsByCategory(String category) {
return products.where((p) => (p.category ?? 'その他') == category).toList();
}
/// IDで検索
static List<Product> search(String query) {
final q = query.toLowerCase();
return products.where((p) =>
p.name.toLowerCase().contains(q) ||
p.id.toLowerCase().contains(q)
).toList();
}
}

81
lib/main.dart Normal file
View file

@ -0,0 +1,81 @@
// lib/main.dart
// version: 1.5.1 (Fix: Class name conflict)
import 'package:flutter/material.dart';
import 'screens/pdf_list_screen.dart';
import 'models/invoice_models.dart';
import 'screens/invoice_input_screen.dart';
import 'screens/invoice_detail_page.dart';
import 'data/database.dart' as db; //
///
late db.AppDatabase database;
void main() {
WidgetsFlutterBinding.ensureInitialized();
database = db.AppDatabase();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '販売アシスト1号',
theme: ThemeData(
primarySwatch: Colors.blueGrey,
visualDensity: VisualDensity.adaptivePlatformDensity,
useMaterial3: true,
),
home: const InvoiceFlowScreen(),
);
}
}
class InvoiceFlowScreen extends StatefulWidget {
const InvoiceFlowScreen({super.key});
@override
State<InvoiceFlowScreen> createState() => _InvoiceFlowScreenState();
}
class _InvoiceFlowScreenState extends State<InvoiceFlowScreen> {
Invoice? _lastGeneratedInvoice;
void _handleInvoiceGenerated(Invoice generatedInvoice, String filePath) {
setState(() {
_lastGeneratedInvoice = generatedInvoice;
});
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => InvoiceDetailPage(invoice: generatedInvoice),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("販売アシスト1号 V1.5.1"),
backgroundColor: Colors.blueGrey,
actions: [
IconButton(
icon: const Icon(Icons.picture_as_pdf),
tooltip: 'PDF一覧',
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const PdfListScreen()),
);
},
),
],
),
body: InvoiceInputForm(onInvoiceGenerated: _handleInvoiceGenerated),
);
}
}

122
lib/main.dart.org Normal file
View file

@ -0,0 +1,122 @@
import 'package:flutter/material.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: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// TRY THIS: Try running your application with "flutter run". You'll see
// the application has a purple toolbar. Then, without quitting the app,
// try changing the seedColor in the colorScheme below to Colors.green
// and then invoke "hot reload" (save your changes or press the "hot
// reload" button in a Flutter-supported IDE, or press "r" if you used
// the command line to start the app).
//
// Notice that the counter didn't reset back to zero; the application
// state is not lost during the reload. To reset the state, use hot
// restart instead.
//
// This works for code too, not just values: Most code changes can be
// tested with just a hot reload.
colorScheme: .fromSeed(seedColor: Colors.deepPurple),
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// TRY THIS: Try changing the color here to a specific color (to
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
// change color while the other colors stay the same.
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
//
// TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
// action in the IDE, or press "p" in the console), to see the
// wireframe for each widget.
mainAxisAlignment: .center,
children: [
const Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}

View file

@ -0,0 +1,2 @@
export 'customer_model.dart';
export 'invoice_models.dart';

View file

@ -0,0 +1,87 @@
import 'package:intl/intl.dart';
///
/// Odoo IDodooId
class Customer {
final String id; // ID
final int? odooId; // Odoo上の res.partner ID (nullの場合は未同期)
final String displayName; //
final String formalName; //
final String? zipCode; // 便
final String? address; //
final String? department; //
final String? title; // ()
final DateTime lastUpdatedAt; //
Customer({
required this.id,
this.odooId,
required this.displayName,
required this.formalName,
this.zipCode,
this.address,
this.department,
this.title = '御中',
DateTime? lastUpdatedAt,
}) : this.lastUpdatedAt = lastUpdatedAt ?? DateTime.now();
///
String get invoiceName => department != null && department!.isNotEmpty
? "$formalName\n$department $title"
: "$formalName $title";
///
Customer copyWith({
String? id,
int? odooId,
String? displayName,
String? formalName,
String? zipCode,
String? address,
String? department,
String? title,
DateTime? lastUpdatedAt,
}) {
return Customer(
id: id ?? this.id,
odooId: odooId ?? this.odooId,
displayName: displayName ?? this.displayName,
formalName: formalName ?? this.formalName,
zipCode: zipCode ?? this.zipCode,
address: address ?? this.address,
department: department ?? this.department,
title: title ?? this.title,
lastUpdatedAt: lastUpdatedAt ?? DateTime.now(),
);
}
/// JSON変換 (Odoo同期用)
Map<String, dynamic> toJson() {
return {
'id': id,
'odoo_id': odooId,
'display_name': displayName,
'formal_name': formalName,
'zip_code': zipCode,
'address': address,
'department': department,
'title': title,
'last_updated_at': lastUpdatedAt.toIso8601String(),
};
}
/// JSONからモデルを生成
factory Customer.fromJson(Map<String, dynamic> json) {
return Customer(
id: json['id'],
odooId: json['odoo_id'],
displayName: json['display_name'],
formalName: json['formal_name'],
zipCode: json['zip_code'],
address: json['address'],
department: json['department'],
title: json['title'] ?? '御中',
lastUpdatedAt: DateTime.parse(json['last_updated_at']),
);
}
}

View file

@ -0,0 +1,145 @@
import 'package:intl/intl.dart';
import 'customer_model.dart';
///
class InvoiceItem {
String description;
int quantity;
int unitPrice;
InvoiceItem({
required this.description,
required this.quantity,
required this.unitPrice,
});
// ( * )
int get subtotal => quantity * unitPrice;
//
InvoiceItem copyWith({
String? description,
int? quantity,
int? unitPrice,
}) {
return InvoiceItem(
description: description ?? this.description,
quantity: quantity ?? this.quantity,
unitPrice: unitPrice ?? this.unitPrice,
);
}
// JSON変換
Map<String, dynamic> toJson() {
return {
'description': description,
'quantity': quantity,
'unit_price': unitPrice,
};
}
// JSONから復元
factory InvoiceItem.fromJson(Map<String, dynamic> json) {
return InvoiceItem(
description: json['description'] as String,
quantity: json['quantity'] as int,
unitPrice: json['unit_price'] as int,
);
}
}
///
class Invoice {
Customer customer; //
DateTime date;
List<InvoiceItem> items;
String? filePath; // PDFのパス
String invoiceNumber; //
String? notes; //
Invoice({
required this.customer,
required this.date,
required this.items,
this.filePath,
String? invoiceNumber,
this.notes,
}) : invoiceNumber = invoiceNumber ?? DateFormat('yyyyMMdd-HHmm').format(date);
//
String get clientName => customer.formalName;
//
int get subtotal {
return items.fold(0, (sum, item) => sum + item.subtotal);
}
// (10%)
int get tax {
return (subtotal * 0.1).floor();
}
//
int get totalAmount {
return subtotal + tax;
}
//
Invoice copyWith({
Customer? customer,
DateTime? date,
List<InvoiceItem>? items,
String? filePath,
String? invoiceNumber,
String? notes,
}) {
return Invoice(
customer: customer ?? this.customer,
date: date ?? this.date,
items: items ?? this.items,
filePath: filePath ?? this.filePath,
invoiceNumber: invoiceNumber ?? this.invoiceNumber,
notes: notes ?? this.notes,
);
}
// CSV形式への変換
String toCsv() {
StringBuffer sb = StringBuffer();
sb.writeln("Customer,${customer.formalName}");
sb.writeln("Invoice Number,$invoiceNumber");
sb.writeln("Date,${DateFormat('yyyy/MM/dd').format(date)}");
sb.writeln("");
sb.writeln("Description,Quantity,UnitPrice,Subtotal");
for (var item in items) {
sb.writeln("${item.description},${item.quantity},${item.unitPrice},${item.subtotal}");
}
return sb.toString();
}
// JSON変換 ()
Map<String, dynamic> toJson() {
return {
'customer': customer.toJson(),
'date': date.toIso8601String(),
'items': items.map((item) => item.toJson()).toList(),
'file_path': filePath,
'invoice_number': invoiceNumber,
'notes': notes,
};
}
// JSONから復元 ()
factory Invoice.fromJson(Map<String, dynamic> json) {
return Invoice(
customer: Customer.fromJson(json['customer'] as Map<String, dynamic>),
date: DateTime.parse(json['date'] as String),
items: (json['items'] as List)
.map((i) => InvoiceItem.fromJson(i as Map<String, dynamic>))
.toList(),
filePath: json['file_path'] as String?,
invoiceNumber: json['invoice_number'] as String,
notes: json['notes'] as String?,
);
}
}

View file

@ -0,0 +1,427 @@
// lib/screens/customer_picker_modal.dart
// Version: 1.5.0 (Strict Type Handling: DB vs App Model)
import 'package:flutter/material.dart';
import 'package:flutter_contacts/flutter_contacts.dart';
import 'package:geolocator/geolocator.dart';
import 'package:uuid/uuid.dart';
import 'package:drift/drift.dart' as drift;
import '../models/app_model.dart' as app_model; //
import '../data/database.dart' as db;
import '../main.dart';
class CustomerPickerModal extends StatefulWidget {
final Function(app_model.Customer) onCustomerSelected;
const CustomerPickerModal({Key? key, required this.onCustomerSelected})
: super(key: key);
@override
State<CustomerPickerModal> createState() => _CustomerPickerModalState();
}
class _CustomerPickerModalState extends State<CustomerPickerModal> {
String _searchQuery = "";
List<db.Customers> _dbCustomers = [];
List<app_model.Customer> _filteredCustomers = [];
bool _isLoading = true;
bool _isImportingFromContacts = false;
Position? _currentPosition;
@override
void initState() {
super.initState();
_refreshData();
}
Future<void> _refreshData() async {
if (!mounted) return;
setState(() => _isLoading = true);
try {
_currentPosition = await _determinePosition();
_dbCustomers = await database.select(database.customers).get();
_applyFilterAndSort();
} catch (e) {
debugPrint("Data Fetch Error: $e");
} finally {
if (mounted) setState(() => _isLoading = false);
}
}
void _applyFilterAndSort() {
final query = _searchQuery.toLowerCase();
// DBの型をアプリ用の型に変換しながらフィルタリング
List<app_model.Customer> list = _dbCustomers
.where((c) {
return c.formalName.toLowerCase().contains(query) ||
c.displayName.toLowerCase().contains(query);
})
.map(
(c) => app_model.Customer(
id: c.id,
displayName: c.displayName,
formalName: c.formalName,
address: c.address ?? "",
department: c.department ?? "",
),
)
.toList();
// GPS距離でソート
if (_currentPosition != null) {
list.sort((a, b) {
final dbA = _dbCustomers.firstWhere((e) => e.id == a.id);
final dbB = _dbCustomers.firstWhere((e) => e.id == b.id);
if (dbA.latitude == null) return 1;
if (dbB.latitude == null) return -1;
double distA = Geolocator.distanceBetween(
_currentPosition!.latitude,
_currentPosition!.longitude,
dbA.latitude!,
dbA.longitude!,
);
double distB = Geolocator.distanceBetween(
_currentPosition!.latitude,
_currentPosition!.longitude,
dbB.latitude!,
dbB.longitude!,
);
return distA.compareTo(distB);
});
}
setState(() {
_filteredCustomers = list;
});
}
Future<Position?> _determinePosition() async {
try {
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
}
if (permission == LocationPermission.always ||
permission == LocationPermission.whileInUse) {
return await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.low,
).timeout(const Duration(seconds: 3));
}
} catch (_) {}
return null;
}
void _onSearchChanged(String query) {
_searchQuery = query;
_applyFilterAndSort();
}
Future<void> _importFromPhoneContacts() async {
setState(() => _isImportingFromContacts = true);
try {
if (await FlutterContacts.requestPermission(readonly: true)) {
final contacts = await FlutterContacts.getContacts();
if (!mounted) return;
setState(() => _isImportingFromContacts = false);
final Contact? selectedContact = await showModalBottomSheet<Contact>(
context: context,
isScrollControlled: true,
builder: (context) => _PhoneContactListSelector(contacts: contacts),
);
if (selectedContact != null) {
_showCustomerEditDialog(
displayName: selectedContact.displayName,
initialFormalName: selectedContact.displayName,
);
}
}
} catch (e) {
setState(() => _isImportingFromContacts = false);
}
}
void _showCustomerEditDialog({
required String displayName,
required String initialFormalName,
app_model.Customer? existingCustomer,
}) {
final formalNameController = TextEditingController(text: initialFormalName);
final departmentController = TextEditingController(
text: existingCustomer?.department ?? "",
);
final addressController = TextEditingController(
text: existingCustomer?.address ?? "",
);
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(existingCustomer == null ? "顧客の新規登録" : "顧客情報の編集"),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"表示名: $displayName",
style: const TextStyle(fontSize: 12, color: Colors.grey),
),
const SizedBox(height: 16),
TextField(
controller: formalNameController,
decoration: const InputDecoration(
labelText: "請求書用 正式名称",
border: OutlineInputBorder(),
),
),
const SizedBox(height: 12),
TextField(
controller: departmentController,
decoration: const InputDecoration(
labelText: "部署名",
border: OutlineInputBorder(),
),
),
const SizedBox(height: 12),
TextField(
controller: addressController,
decoration: const InputDecoration(
labelText: "住所",
border: OutlineInputBorder(),
),
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text("キャンセル"),
),
ElevatedButton(
onPressed: () async {
final newId = existingCustomer?.id ?? const Uuid().v4();
await database
.into(database.customers)
.insertOnConflictUpdate(
db.CustomersCompanion.insert(
id: newId,
displayName: displayName,
formalName: formalNameController.text.trim(),
department: drift.Value(departmentController.text.trim()),
address: drift.Value(addressController.text.trim()),
latitude: drift.Value(_currentPosition?.latitude),
longitude: drift.Value(_currentPosition?.longitude),
lastUpdatedAt: drift.Value(DateTime.now()),
),
);
if (mounted) {
Navigator.pop(context);
_refreshData();
}
},
child: const Text("保存"),
),
],
),
);
}
Future<void> _confirmDelete(app_model.Customer customer) async {
final confirm = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text("顧客の削除"),
content: Text("${customer.formalName}」を削除しますか?"),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text("キャンセル"),
),
TextButton(
onPressed: () => Navigator.pop(context, true),
child: const Text("削除する", style: TextStyle(color: Colors.red)),
),
],
),
);
if (confirm == true) {
await (database.delete(
database.customers,
)..where((t) => t.id.equals(customer.id))).go();
_refreshData();
}
}
@override
Widget build(BuildContext context) {
return Material(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
"顧客マスター管理",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.pop(context),
),
],
),
const SizedBox(height: 12),
TextField(
decoration: InputDecoration(
hintText: "検索...",
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
onChanged: _onSearchChanged,
),
const SizedBox(height: 12),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: _isImportingFromContacts
? null
: _importFromPhoneContacts,
icon: _isImportingFromContacts
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.contact_phone),
label: const Text("電話帳から新規取り込み"),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blueGrey.shade700,
foregroundColor: Colors.white,
),
),
),
],
),
),
const Divider(),
if (_isLoading) const LinearProgressIndicator(),
Expanded(
child: _filteredCustomers.isEmpty && !_isLoading
? const Center(child: Text("該当する顧客がいません"))
: ListView.builder(
itemCount: _filteredCustomers.length,
itemBuilder: (context, index) {
final customer = _filteredCustomers[index];
return ListTile(
leading: const CircleAvatar(
child: Icon(Icons.business),
),
title: Text(customer.formalName),
subtitle: Text(
customer.address.isEmpty ? "住所未設定" : customer.address,
),
onTap: () => widget.onCustomerSelected(customer),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(
Icons.edit,
color: Colors.blueGrey,
size: 20,
),
onPressed: () => _showCustomerEditDialog(
displayName: customer.displayName,
initialFormalName: customer.formalName,
existingCustomer: customer,
),
),
IconButton(
icon: const Icon(
Icons.delete_outline,
color: Colors.redAccent,
size: 20,
),
onPressed: () => _confirmDelete(customer),
),
],
),
);
},
),
),
],
),
);
}
}
class _PhoneContactListSelector extends StatefulWidget {
final List<Contact> contacts;
const _PhoneContactListSelector({required this.contacts});
@override
State<_PhoneContactListSelector> createState() =>
_PhoneContactListSelectorState();
}
class _PhoneContactListSelectorState extends State<_PhoneContactListSelector> {
List<Contact> _filtered = [];
final _controller = TextEditingController();
@override
void initState() {
super.initState();
_filtered = widget.contacts;
}
@override
Widget build(BuildContext context) {
return FractionallySizedBox(
heightFactor: 0.8,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: TextField(
controller: _controller,
decoration: const InputDecoration(
hintText: "電話帳検索...",
prefixIcon: Icon(Icons.search),
),
onChanged: (q) => setState(
() => _filtered = widget.contacts
.where(
(c) =>
c.displayName.toLowerCase().contains(q.toLowerCase()),
)
.toList(),
),
),
),
Expanded(
child: ListView.builder(
itemCount: _filtered.length,
itemBuilder: (context, index) => ListTile(
title: Text(_filtered[index].displayName),
onTap: () => Navigator.pop(context, _filtered[index]),
),
),
),
],
),
);
}
}

View file

@ -0,0 +1,240 @@
// lib/screens/invoice_detail_page.dart
// version: 1.2.2 (Fix: Resolved Class name conflict strictly)
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:share_plus/share_plus.dart';
import 'package:open_filex/open_filex.dart';
import 'package:printing/printing.dart';
import 'package:drift/drift.dart' as drift;
import '../models/invoice_models.dart'; // Invoice, InvoiceItem 使
import '../services/pdf_generator.dart';
import '../data/database.dart' as db; // db
import '../main.dart';
import 'product_picker_modal.dart';
class InvoiceDetailPage extends StatefulWidget {
final Invoice invoice;
const InvoiceDetailPage({Key? key, required this.invoice}) : super(key: key);
@override
State<InvoiceDetailPage> createState() => _InvoiceDetailPageState();
}
class _InvoiceDetailPageState extends State<InvoiceDetailPage> {
late TextEditingController _formalNameController;
late TextEditingController _notesController;
late List<InvoiceItem> _items;
late bool _isEditing;
late Invoice _currentInvoice;
String? _currentFilePath;
@override
void initState() {
super.initState();
_currentInvoice = widget.invoice;
_currentFilePath = widget.invoice.filePath;
_formalNameController = TextEditingController(text: _currentInvoice.customer.formalName);
_notesController = TextEditingController(text: _currentInvoice.notes ?? "");
_items = List.from(_currentInvoice.items);
_isEditing = false;
}
@override
void dispose() {
_formalNameController.dispose();
_notesController.dispose();
super.dispose();
}
void _addItem() => setState(() => _items.add(InvoiceItem(description: "新項目", quantity: 1, unitPrice: 0)));
void _removeItem(int index) => setState(() => _items.removeAt(index));
void _pickFromMaster() {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => FractionallySizedBox(
heightFactor: 0.9,
child: ProductPickerModal(
onItemSelected: (item) {
setState(() => _items.add(item));
Navigator.pop(context);
},
),
),
);
}
Future<void> _saveChanges() async {
final String formalName = _formalNameController.text.trim();
if (formalName.isEmpty) return;
final updatedCustomer = _currentInvoice.customer.copyWith(formalName: formalName);
final updatedInvoice = _currentInvoice.copyWith(
customer: updatedCustomer,
items: _items,
notes: _notesController.text,
);
setState(() => _isEditing = false);
try {
final File pdfFile = await PdfGenerator.generateInvoicePdf(updatedInvoice);
final String newPath = pdfFile.path;
// DB側のクラスにはすべて `db.`
await database.into(database.customers).insertOnConflictUpdate(
db.CustomersCompanion.insert(
id: updatedCustomer.id,
displayName: updatedCustomer.displayName,
formalName: updatedCustomer.formalName,
address: drift.Value(updatedCustomer.address),
department: drift.Value(updatedCustomer.department),
lastUpdatedAt: drift.Value(DateTime.now()),
),
);
final invoiceCompanion = db.InvoicesCompanion.insert(
id: updatedInvoice.invoiceNumber,
customerId: updatedCustomer.id,
date: updatedInvoice.date,
type: "請求",
filePath: drift.Value(newPath),
notes: drift.Value(updatedInvoice.notes),
totalAmount: updatedInvoice.totalAmount,
);
// item InvoiceItem getter
final List<db.InvoiceItemsCompanion> itemCompanions = _items.map((InvoiceItem item) => db.InvoiceItemsCompanion.insert(
invoiceId: updatedInvoice.invoiceNumber,
description: item.description,
quantity: item.quantity,
unitPrice: item.unitPrice,
)).toList();
await database.saveFullInvoice(invoiceCompanion, itemCompanions);
setState(() {
_currentInvoice = updatedInvoice.copyWith(filePath: newPath);
_currentFilePath = newPath;
});
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('保存完了')));
await Printing.layoutPdf(onLayout: (format) async => pdfFile.readAsBytesSync(), name: newPath.split('/').last);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('エラー: $e'), backgroundColor: Colors.red));
}
}
@override
Widget build(BuildContext context) {
final amountFormatter = NumberFormat("#,###");
return Scaffold(
appBar: AppBar(
title: const Text("請求書詳細"),
backgroundColor: Colors.blueGrey,
foregroundColor: Colors.white,
actions: [
if (!_isEditing)
IconButton(icon: const Icon(Icons.edit), onPressed: () => setState(() => _isEditing = true))
else ...[
IconButton(icon: const Icon(Icons.save), onPressed: _saveChanges),
IconButton(icon: const Icon(Icons.cancel), onPressed: () => setState(() => _isEditing = false)),
]
],
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildHeaderSection(),
const Divider(height: 32),
_buildItemTable(amountFormatter),
if (_isEditing) _buildEditButtons(),
const SizedBox(height: 24),
_buildSummarySection(amountFormatter),
const SizedBox(height: 24),
_buildFooterActions(),
],
),
),
);
}
Widget _buildHeaderSection() {
final dateFormatter = DateFormat('yyyy年MM月dd日');
if (_isEditing) {
return Column(
children: [
TextField(controller: _formalNameController, decoration: const InputDecoration(labelText: "取引先 正式名称")),
const SizedBox(height: 12),
TextField(controller: _notesController, maxLines: 2, decoration: const InputDecoration(labelText: "備考")),
],
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("${_currentInvoice.customer.formalName} 御中", style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
Text("請求番号: ${_currentInvoice.invoiceNumber}"),
Text("発行日: ${dateFormatter.format(_currentInvoice.date)}"),
],
);
}
Widget _buildItemTable(NumberFormat formatter) {
return Table(
border: TableBorder.all(color: Colors.grey.shade300),
columnWidths: const {0: FlexColumnWidth(4), 1: FixedColumnWidth(50), 2: FixedColumnWidth(80), 3: FlexColumnWidth(2), 4: FixedColumnWidth(40)},
children: [
TableRow(decoration: BoxDecoration(color: Colors.grey.shade100), children: const [_TableCell("品名"), _TableCell("数量"), _TableCell("単価"), _TableCell("金額"), _TableCell("")]),
..._items.asMap().entries.map((entry) {
int idx = entry.key;
InvoiceItem item = entry.value;
if (_isEditing) {
return TableRow(children: [
_EditableCell(initialValue: item.description, onChanged: (val) => item.description = val),
_EditableCell(initialValue: item.quantity.toString(), keyboardType: TextInputType.number, onChanged: (val) => setState(() => item.quantity = int.tryParse(val) ?? 0)),
_EditableCell(initialValue: item.unitPrice.toString(), keyboardType: TextInputType.number, onChanged: (val) => setState(() => item.unitPrice = int.tryParse(val) ?? 0)),
_TableCell(formatter.format(item.subtotal)),
IconButton(icon: const Icon(Icons.delete, color: Colors.red, size: 20), onPressed: () => _removeItem(idx)),
]);
}
return TableRow(children: [_TableCell(item.description), _TableCell(item.quantity.toString()), _TableCell(formatter.format(item.unitPrice)), _TableCell(formatter.format(item.subtotal)), const SizedBox()]);
}),
],
);
}
Widget _buildEditButtons() => Row(children: [ElevatedButton(onPressed: _addItem, child: const Text("行追加")), const SizedBox(width: 8), ElevatedButton(onPressed: _pickFromMaster, child: const Text("マスター"))]);
Widget _buildSummarySection(NumberFormat formatter) {
// int
int subtotal = _items.fold<int>(0, (int sum, InvoiceItem item) => sum + (item.quantity * item.unitPrice));
return Align(
alignment: Alignment.centerRight,
child: Column(children: [Text("合計: ¥${formatter.format((subtotal * 1.1).floor())}", style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold))]),
);
}
Widget _buildFooterActions() => (_isEditing || _currentFilePath == null) ? const SizedBox() : Row(children: [Expanded(child: ElevatedButton(onPressed: () => OpenFilex.open(_currentFilePath!), child: const Text("開く"))), const SizedBox(width: 12), Expanded(child: ElevatedButton(onPressed: () => Share.shareXFiles([XFile(_currentFilePath!)]), child: const Text("共有")))]);
}
class _TableCell extends StatelessWidget {
final String text;
const _TableCell(this.text);
@override
Widget build(BuildContext context) => Padding(padding: const EdgeInsets.all(8.0), child: Text(text, textAlign: TextAlign.right, style: const TextStyle(fontSize: 12)));
}
class _EditableCell extends StatelessWidget {
final String initialValue;
final TextInputType keyboardType;
final Function(String) onChanged;
const _EditableCell({required this.initialValue, this.keyboardType = TextInputType.text, required this.onChanged});
@override
Widget build(BuildContext context) => Padding(padding: const EdgeInsets.symmetric(horizontal: 4), child: TextField(controller: TextEditingController(text: initialValue), keyboardType: keyboardType, style: const TextStyle(fontSize: 12), onChanged: onChanged));
}

View file

@ -0,0 +1,205 @@
import 'package:flutter/material.dart';
import 'package:uuid/uuid.dart';
import '../models/customer_model.dart';
import '../models/invoice_models.dart';
import '../services/pdf_generator.dart';
import '../services/invoice_repository.dart';
import 'customer_picker_modal.dart';
///
class InvoiceInputForm extends StatefulWidget {
final Function(Invoice invoice, String filePath) onInvoiceGenerated;
const InvoiceInputForm({Key? key, required this.onInvoiceGenerated})
: super(key: key);
@override
State<InvoiceInputForm> createState() => _InvoiceInputFormState();
}
class _InvoiceInputFormState extends State<InvoiceInputForm> {
final _clientController = TextEditingController();
final _amountController = TextEditingController(text: "250000");
final _repository = InvoiceRepository();
String _status = "取引先を選択してPDFを生成してください";
List<Customer> _customerBuffer = [];
Customer? _selectedCustomer;
@override
void initState() {
super.initState();
_selectedCustomer = Customer(
id: const Uuid().v4(),
displayName: "佐々木製作所",
formalName: "株式会社 佐々木製作所",
);
_customerBuffer.add(_selectedCustomer!);
_clientController.text = _selectedCustomer!.formalName;
// PDFを掃除する
_repository.cleanupOrphanedPdfs().then((count) {
if (count > 0) {
debugPrint('Cleaned up $count orphaned PDF files.');
}
});
}
@override
void dispose() {
_clientController.dispose();
_amountController.dispose();
super.dispose();
}
Future<void> _openCustomerPicker() async {
setState(() => _status = "顧客マスターを開いています...");
await showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => FractionallySizedBox(
heightFactor: 0.9,
child: CustomerPickerModal(
onCustomerSelected: (customer) {
setState(() {
bool exists = _customerBuffer.any((c) => c.id == customer.id);
if (!exists) {
_customerBuffer.add(customer);
}
_selectedCustomer = customer;
_clientController.text = customer.formalName;
_status = "${customer.formalName}」を選択しました";
});
Navigator.pop(context);
},
),
),
);
}
Future<void> _handleInitialGenerate() async {
if (_selectedCustomer == null) {
setState(() => _status = "取引先を選択してください");
return;
}
final unitPrice = int.tryParse(_amountController.text) ?? 0;
final initialItems = [
InvoiceItem(description: "ご請求分", quantity: 1, unitPrice: unitPrice),
];
final invoice = Invoice(
customer: _selectedCustomer!,
date: DateTime.now(),
items: initialItems,
);
setState(() => _status = "A4請求書を生成中...");
final path = await PdfGenerator.generateInvoicePdf(invoice);
if (path != null) {
final updatedInvoice = invoice.copyWith(filePath: path.path);
// DBに保存
await _repository.saveInvoice(updatedInvoice);
widget.onInvoiceGenerated(updatedInvoice, path.path);
setState(() => _status = "PDFを生成しDBに登録しました。");
} else {
setState(() => _status = "PDFの生成に失敗しました");
}
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Column(
children: [
const Text(
"ステップ1: 宛先と基本金額の設定",
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blueGrey,
),
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: TextField(
controller: _clientController,
readOnly: true,
onTap: _openCustomerPicker,
decoration: const InputDecoration(
labelText: "取引先名 (タップして選択)",
hintText: "電話帳から取り込むか、マスターから選択",
prefixIcon: Icon(Icons.business),
border: OutlineInputBorder(),
),
),
),
const SizedBox(width: 8),
IconButton(
icon: const Icon(
Icons.person_add_alt_1,
color: Colors.indigo,
size: 40,
),
onPressed: _openCustomerPicker,
tooltip: "顧客を選択・登録",
),
],
),
const SizedBox(height: 16),
TextField(
controller: _amountController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: "基本金額 (税抜)",
hintText: "明細の1行目として登録されます",
prefixIcon: Icon(Icons.currency_yen),
border: OutlineInputBorder(),
),
),
const SizedBox(height: 24),
ElevatedButton.icon(
onPressed: _handleInitialGenerate,
icon: const Icon(Icons.description),
label: const Text("A4請求書を作成して詳細編集へ"),
style: ElevatedButton.styleFrom(
minimumSize: const Size(double.infinity, 60),
backgroundColor: Colors.indigo,
foregroundColor: Colors.white,
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
const SizedBox(height: 24),
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey.shade300),
),
child: Text(
_status,
style: const TextStyle(fontSize: 12, color: Colors.black54),
textAlign: TextAlign.center,
),
),
],
),
),
);
}
}

View file

@ -0,0 +1,168 @@
// lib/screens/pdf_list_screen.dart
// Version: 1.2.0 (DB Integration: List Invoices from SQLite)
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../data/database.dart' as db;
import '../main.dart';
import '../models/invoice_models.dart';
import '../models/app_model.dart' as app_model;
import 'invoice_detail_page.dart';
class PdfListScreen extends StatefulWidget {
const PdfListScreen({Key? key}) : super(key: key);
@override
State<PdfListScreen> createState() => _PdfListScreenState();
}
class _PdfListScreenState extends State<PdfListScreen> {
late Stream<List<db.InvoiceWithCustomer>> _invoiceStream;
@override
void initState() {
super.initState();
//
//
_invoiceStream = database.watchAllInvoices();
}
@override
Widget build(BuildContext context) {
final currencyFormat = NumberFormat("#,###");
final dateFormat = DateFormat('yyyy/MM/dd');
return Scaffold(
appBar: AppBar(
title: const Text("請求書 履歴一覧"),
backgroundColor: Colors.blueGrey,
foregroundColor: Colors.white,
),
body: StreamBuilder<List<db.InvoiceWithCustomer>>(
stream: _invoiceStream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(child: Text("エラーが発生しました: ${snapshot.error}"));
}
final invoices = snapshot.data ?? [];
if (invoices.isEmpty) {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.description_outlined,
size: 64,
color: Colors.grey,
),
SizedBox(height: 16),
Text(
"保存された請求書はまだありません。",
style: TextStyle(color: Colors.grey),
),
],
),
);
}
return ListView.separated(
itemCount: invoices.length,
separatorBuilder: (context, index) => const Divider(height: 1),
itemBuilder: (context, index) {
final item = invoices[index];
final invoice = item.invoice;
final customer = item.customer;
return ListTile(
leading: CircleAvatar(
backgroundColor: Colors.blueGrey.shade100,
child: const Icon(
Icons.picture_as_pdf,
color: Colors.blueGrey,
),
),
title: Text(
customer.formalName,
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"No: ${invoice.id} | ${dateFormat.format(invoice.date)}",
),
if (invoice.notes != null && invoice.notes!.isNotEmpty)
Text(
invoice.notes!,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 12),
),
],
),
trailing: Text(
"¥${currencyFormat.format(invoice.totalAmount)}",
style: const TextStyle(
color: Colors.blueGrey,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
onTap: () {
// Invoice
_navigateToDetail(item);
},
);
},
);
},
),
);
}
/// DBモデルからアプリ内モデルに変換
void _navigateToDetail(db.InvoiceWithCustomer data) async {
// DBから取得
final items = await (database.select(
database.invoiceItems,
)..where((t) => t.invoiceId.equals(data.invoice.id))).get();
final mappedItems = items
.map(
(i) => InvoiceItem(
description: i.description,
quantity: i.quantity,
unitPrice: i.unitPrice,
),
)
.toList();
final invoiceModel = Invoice(
invoiceNumber: data.invoice.id,
customer: app_model.Customer(
id: data.customer.id,
displayName: data.customer.displayName,
formalName: data.customer.formalName,
address: data.customer.address ?? "",
department: data.customer.department ?? "",
),
items: mappedItems,
date: data.invoice.date,
notes: data.invoice.notes,
filePath: data.invoice.filePath,
);
if (!mounted) return;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => InvoiceDetailPage(invoice: invoiceModel),
),
);
}
}

View file

@ -0,0 +1,255 @@
import 'package:flutter/material.dart';
import 'package:uuid/uuid.dart';
import '../data/product_master.dart';
import '../models/invoice_models.dart';
///
class ProductPickerModal extends StatefulWidget {
final Function(InvoiceItem) onItemSelected;
const ProductPickerModal({
Key? key,
required this.onItemSelected,
}) : super(key: key);
@override
State<ProductPickerModal> createState() => _ProductPickerModalState();
}
class _ProductPickerModalState extends State<ProductPickerModal> {
String _searchQuery = "";
List<Product> _masterProducts = [];
List<Product> _filteredProducts = [];
String _selectedCategory = "すべて";
@override
void initState() {
super.initState();
// ProductMasterの初期データを使用
_masterProducts = List.from(ProductMaster.products);
_filterProducts();
}
void _filterProducts() {
setState(() {
_filteredProducts = _masterProducts.where((product) {
final matchesQuery = product.name.toLowerCase().contains(_searchQuery.toLowerCase()) ||
product.id.toLowerCase().contains(_searchQuery.toLowerCase());
final matchesCategory = _selectedCategory == "すべて" || (product.category == _selectedCategory);
return matchesQuery && matchesCategory;
}).toList();
});
}
///
void _showProductEditDialog({Product? existingProduct}) {
final idController = TextEditingController(text: existingProduct?.id ?? "");
final nameController = TextEditingController(text: existingProduct?.name ?? "");
final priceController = TextEditingController(text: existingProduct?.defaultUnitPrice.toString() ?? "");
final categoryController = TextEditingController(text: existingProduct?.category ?? "");
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(existingProduct == null ? "新規商品の登録" : "商品情報の編集"),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (existingProduct == null)
TextField(
controller: idController,
decoration: const InputDecoration(labelText: "商品コード (例: S001)", border: OutlineInputBorder()),
),
const SizedBox(height: 12),
TextField(
controller: nameController,
decoration: const InputDecoration(labelText: "商品名", border: OutlineInputBorder()),
),
const SizedBox(height: 12),
TextField(
controller: priceController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(labelText: "標準単価", border: OutlineInputBorder()),
),
const SizedBox(height: 12),
TextField(
controller: categoryController,
decoration: const InputDecoration(labelText: "カテゴリ (任意)", border: OutlineInputBorder()),
),
],
),
),
actions: [
TextButton(onPressed: () => Navigator.pop(context), child: const Text("キャンセル")),
ElevatedButton(
onPressed: () {
final String name = nameController.text.trim();
final int price = int.tryParse(priceController.text) ?? 0;
if (name.isEmpty) return;
setState(() {
if (existingProduct != null) {
//
final index = _masterProducts.indexWhere((p) => p.id == existingProduct.id);
if (index != -1) {
_masterProducts[index] = existingProduct.copyWith(
name: name,
defaultUnitPrice: price,
category: categoryController.text.trim(),
);
}
} else {
//
_masterProducts.add(Product(
id: idController.text.isEmpty ? const Uuid().v4().substring(0, 8) : idController.text,
name: name,
defaultUnitPrice: price,
category: categoryController.text.trim(),
));
}
_filterProducts();
});
Navigator.pop(context);
},
child: const Text("保存"),
),
],
),
);
}
///
void _confirmDelete(Product product) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text("商品の削除"),
content: Text("${product.name}」をマスターから削除しますか?"),
actions: [
TextButton(onPressed: () => Navigator.pop(context), child: const Text("キャンセル")),
TextButton(
onPressed: () {
setState(() {
_masterProducts.removeWhere((p) => p.id == product.id);
_filterProducts();
});
Navigator.pop(context);
},
child: const Text("削除する", style: TextStyle(color: Colors.red)),
),
],
),
);
}
@override
Widget build(BuildContext context) {
//
final dynamicCategories = ["すべて", ..._masterProducts.map((p) => p.category ?? 'その他').toSet().toList()];
return Material(
color: Colors.white,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text("商品マスター管理", style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
IconButton(icon: const Icon(Icons.close), onPressed: () => Navigator.pop(context)),
],
),
const SizedBox(height: 12),
TextField(
decoration: InputDecoration(
hintText: "商品名やコードで検索...",
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
filled: true,
fillColor: Colors.grey.shade50,
),
onChanged: (val) {
_searchQuery = val;
_filterProducts();
},
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: dynamicCategories.map((cat) {
final isSelected = _selectedCategory == cat;
return Padding(
padding: const EdgeInsets.only(right: 8.0),
child: ChoiceChip(
label: Text(cat),
selected: isSelected,
onSelected: (s) {
if (s) {
setState(() {
_selectedCategory = cat;
_filterProducts();
});
}
},
),
);
}).toList(),
),
),
),
const SizedBox(width: 8),
IconButton.filled(
onPressed: () => _showProductEditDialog(),
icon: const Icon(Icons.add),
tooltip: "新規商品を追加",
),
],
),
],
),
),
const Divider(height: 1),
Expanded(
child: _filteredProducts.isEmpty
? const Center(child: Text("該当する商品がありません"))
: ListView.separated(
itemCount: _filteredProducts.length,
separatorBuilder: (context, index) => const Divider(height: 1),
itemBuilder: (context, index) {
final product = _filteredProducts[index];
return ListTile(
leading: const Icon(Icons.inventory_2, color: Colors.blueGrey),
title: Text(product.name, style: const TextStyle(fontWeight: FontWeight.bold)),
subtitle: Text("${product.id} | ¥${product.defaultUnitPrice}"),
onTap: () => widget.onItemSelected(product.toInvoiceItem()),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.edit_outlined, size: 20, color: Colors.blueGrey),
onPressed: () => _showProductEditDialog(existingProduct: product),
),
IconButton(
icon: const Icon(Icons.delete_outline, size: 20, color: Colors.redAccent),
onPressed: () => _confirmDelete(product),
),
],
),
);
},
),
),
],
),
);
}
}

View file

@ -0,0 +1,107 @@
import 'dart:convert';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import '../models/invoice_models.dart';
/// DB
/// PDFファイルとデータの整合性を保つための機能を提供します
class InvoiceRepository {
static const String _dbFileName = 'invoices_db.json';
///
Future<File> _getDbFile() async {
final directory = await getApplicationDocumentsDirectory();
return File('${directory.path}/$_dbFileName');
}
///
Future<List<Invoice>> getAllInvoices() async {
try {
final file = await _getDbFile();
if (!await file.exists()) return [];
final String content = await file.readAsString();
final List<dynamic> jsonList = json.decode(content);
return jsonList.map((json) => Invoice.fromJson(json)).toList()
..sort((a, b) => b.date.compareTo(a.date)); //
} catch (e) {
print('DB Loading Error: $e');
return [];
}
}
///
Future<void> saveInvoice(Invoice invoice) async {
final List<Invoice> all = await getAllInvoices();
//
final index = all.indexWhere((i) => i.invoiceNumber == invoice.invoiceNumber);
if (index != -1) {
// PDFの掃除
final oldPath = all[index].filePath;
if (oldPath != null && oldPath != invoice.filePath) {
await _deletePhysicalFile(oldPath);
}
all[index] = invoice;
} else {
all.add(invoice);
}
final file = await _getDbFile();
await file.writeAsString(json.encode(all.map((i) => i.toJson()).toList()));
}
///
Future<void> deleteInvoice(Invoice invoice) async {
final List<Invoice> all = await getAllInvoices();
all.removeWhere((i) => i.invoiceNumber == invoice.invoiceNumber);
//
if (invoice.filePath != null) {
await _deletePhysicalFile(invoice.filePath!);
}
final file = await _getDbFile();
await file.writeAsString(json.encode(all.map((i) => i.toJson()).toList()));
}
/// PDFファイルをストレージから削除する
Future<void> _deletePhysicalFile(String path) async {
try {
final file = File(path);
if (await file.exists()) {
await file.delete();
print('Physical file deleted: $path');
}
} catch (e) {
print('File Deletion Error: $path, $e');
}
}
/// DBに登録されていないPDFファイル
Future<int> cleanupOrphanedPdfs() async {
final List<Invoice> all = await getAllInvoices();
final Set<String> registeredPaths = all
.where((i) => i.filePath != null)
.map((i) => i.filePath!)
.toSet();
final directory = await getExternalStorageDirectory();
if (directory == null) return 0;
int deletedCount = 0;
final List<FileSystemEntity> files = directory.listSync();
for (var entity in files) {
if (entity is File && entity.path.endsWith('.pdf')) {
// DBに登録されていないPDFは削除
if (!registeredPaths.contains(entity.path)) {
await entity.delete();
deletedCount++;
}
}
}
return deletedCount;
}
}

View file

@ -0,0 +1,99 @@
// lib/services/pdf_generator.dart
// Version: 1.0.1
// Updated: 2026-02-08
// Description: PDF生成および命名規則に基づいたファイル保存
import 'dart:io';
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:path_provider/path_provider.dart';
import 'package:crypto/crypto.dart';
import '../models/invoice_models.dart';
class PdfGenerator {
/// PDFを生成し
static Future<File> generateInvoicePdf(Invoice invoice) async {
final pdf = pw.Document();
// 1.
final fontData = await rootBundle.load("assets/fonts/ipaexg.ttf");
final ttf = pw.Font.ttf(fontData);
// 2. PDFコンテンツの構築
pdf.addPage(
pw.MultiPage(
theme: pw.ThemeData.withFont(base: ttf),
build: (pw.Context context) {
return [
pw.Header(
level: 0,
child: pw.Text("請求書", style: pw.TextStyle(fontSize: 24)),
),
pw.SizedBox(height: 20),
pw.Text("請求先: ${invoice.customer.formalName} 御中"),
pw.Text("請求番号: ${invoice.invoiceNumber}"),
pw.Text("日付: ${DateFormat('yyyy/MM/dd').format(invoice.date)}"),
pw.Divider(),
pw.Table.fromTextArray(
headers: ['内容', '数量', '単価', '金額'],
data: invoice.items.map((item) {
return [
item.description,
item.quantity.toString(),
"¥${item.unitPrice}",
"¥${item.subtotal}",
];
}).toList(),
),
pw.SizedBox(height: 20),
pw.Container(
alignment: pw.Alignment.centerRight,
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.end,
children: [
pw.Text("小計: ¥${invoice.subtotal}"),
pw.Text("消費税: ¥${invoice.tax}"),
pw.Text("合計金額: ¥${invoice.totalAmount}",
style: pw.TextStyle(fontWeight: pw.FontWeight.bold)),
],
),
),
if (invoice.notes != null) ...[
pw.SizedBox(height: 20),
pw.Text("備考:"),
pw.Text(invoice.notes!),
],
];
},
),
);
// 3.
// : []_[]_[]_[]_[]_[SHA256ハッシュ].pdf
final dateStr = DateFormat('yyyyMMdd').format(invoice.date);
// 使
const typeStr = "請求書"; // Invoiceにプロパティ追加検討
final customerStr = invoice.customer.formalName.replaceAll(RegExp(r'[\\/:*?"<>|]'), '');
final titleStr = (invoice.notes != null && invoice.notes!.length > 10)
? invoice.notes!.substring(0, 10)
: (invoice.items.isNotEmpty ? invoice.items.first.description : "名称未設定");
final amountStr = invoice.totalAmount.toString();
// SHA256ハッシュの生成
final rawData = "$dateStr$customerStr$amountStr${invoice.invoiceNumber}";
final hash = sha256.convert(utf8.encode(rawData)).toString().substring(0, 8);
final fileName = "${dateStr}_${typeStr}_${customerStr}_${titleStr}_${amountStr}_$hash.pdf";
// 4.
final directory = await getApplicationDocumentsDirectory();
final file = File("${directory.path}/$fileName");
await file.writeAsBytes(await pdf.save());
return file;
}
}

1
linux/.gitignore vendored Normal file
View file

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

128
linux/CMakeLists.txt Normal file
View file

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

View file

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

View file

@ -0,0 +1,23 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
#include <printing/printing_plugin.h>
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) printing_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "PrintingPlugin");
printing_plugin_register_with_registrar(printing_registrar);
g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin");
sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
}

View file

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

View file

@ -0,0 +1,26 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
printing
sqlite3_flutter_libs
url_launcher_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

View file

@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.13)
project(runner LANGUAGES CXX)
# Define the application target. To change its name, change BINARY_NAME in the
# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
# work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)
# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})
# Add preprocessor definitions for the application ID.
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
# Add dependency libraries. Add any application-specific dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")

6
linux/runner/main.cc Normal file
View file

@ -0,0 +1,6 @@
#include "my_application.h"
int main(int argc, char** argv) {
g_autoptr(MyApplication) app = my_application_new();
return g_application_run(G_APPLICATION(app), argc, argv);
}

View file

@ -0,0 +1,148 @@
#include "my_application.h"
#include <flutter_linux/flutter_linux.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#include "flutter/generated_plugin_registrant.h"
struct _MyApplication {
GtkApplication parent_instance;
char** dart_entrypoint_arguments;
};
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
// Called when first Flutter frame received.
static void first_frame_cb(MyApplication* self, FlView* view) {
gtk_widget_show(gtk_widget_get_toplevel(GTK_WIDGET(view)));
}
// Implements GApplication::activate.
static void my_application_activate(GApplication* application) {
MyApplication* self = MY_APPLICATION(application);
GtkWindow* window =
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
// Use a header bar when running in GNOME as this is the common style used
// by applications and is the setup most users will be using (e.g. Ubuntu
// desktop).
// If running on X and not using GNOME then just use a traditional title bar
// in case the window manager does more exotic layout, e.g. tiling.
// If running on Wayland assume the header bar will work (may need changing
// if future cases occur).
gboolean use_header_bar = TRUE;
#ifdef GDK_WINDOWING_X11
GdkScreen* screen = gtk_window_get_screen(window);
if (GDK_IS_X11_SCREEN(screen)) {
const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
use_header_bar = FALSE;
}
}
#endif
if (use_header_bar) {
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "gemi_invoice");
gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
} else {
gtk_window_set_title(window, "gemi_invoice");
}
gtk_window_set_default_size(window, 1280, 720);
g_autoptr(FlDartProject) project = fl_dart_project_new();
fl_dart_project_set_dart_entrypoint_arguments(
project, self->dart_entrypoint_arguments);
FlView* view = fl_view_new(project);
GdkRGBA background_color;
// Background defaults to black, override it here if necessary, e.g. #00000000
// for transparent.
gdk_rgba_parse(&background_color, "#000000");
fl_view_set_background_color(view, &background_color);
gtk_widget_show(GTK_WIDGET(view));
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
// Show the window when Flutter renders.
// Requires the view to be realized so we can start rendering.
g_signal_connect_swapped(view, "first-frame", G_CALLBACK(first_frame_cb),
self);
gtk_widget_realize(GTK_WIDGET(view));
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
gtk_widget_grab_focus(GTK_WIDGET(view));
}
// Implements GApplication::local_command_line.
static gboolean my_application_local_command_line(GApplication* application,
gchar*** arguments,
int* exit_status) {
MyApplication* self = MY_APPLICATION(application);
// Strip out the first argument as it is the binary name.
self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
g_autoptr(GError) error = nullptr;
if (!g_application_register(application, nullptr, &error)) {
g_warning("Failed to register: %s", error->message);
*exit_status = 1;
return TRUE;
}
g_application_activate(application);
*exit_status = 0;
return TRUE;
}
// Implements GApplication::startup.
static void my_application_startup(GApplication* application) {
// MyApplication* self = MY_APPLICATION(object);
// Perform any actions required at application startup.
G_APPLICATION_CLASS(my_application_parent_class)->startup(application);
}
// Implements GApplication::shutdown.
static void my_application_shutdown(GApplication* application) {
// MyApplication* self = MY_APPLICATION(object);
// Perform any actions required at application shutdown.
G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);
}
// Implements GObject::dispose.
static void my_application_dispose(GObject* object) {
MyApplication* self = MY_APPLICATION(object);
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
}
static void my_application_class_init(MyApplicationClass* klass) {
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
G_APPLICATION_CLASS(klass)->local_command_line =
my_application_local_command_line;
G_APPLICATION_CLASS(klass)->startup = my_application_startup;
G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;
G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
}
static void my_application_init(MyApplication* self) {}
MyApplication* my_application_new() {
// Set the program name to the application ID, which helps various systems
// like GTK and desktop environments map this running application to its
// corresponding .desktop file. This ensures better integration by allowing
// the application to be recognized beyond its binary name.
g_set_prgname(APPLICATION_ID);
return MY_APPLICATION(g_object_new(my_application_get_type(),
"application-id", APPLICATION_ID, "flags",
G_APPLICATION_NON_UNIQUE, nullptr));
}

View file

@ -0,0 +1,21 @@
#ifndef FLUTTER_MY_APPLICATION_H_
#define FLUTTER_MY_APPLICATION_H_
#include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE(MyApplication,
my_application,
MY,
APPLICATION,
GtkApplication)
/**
* my_application_new:
*
* Creates a new Flutter-based application.
*
* Returns: a new #MyApplication.
*/
MyApplication* my_application_new();
#endif // FLUTTER_MY_APPLICATION_H_

7
macos/.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
# Flutter-related
**/Flutter/ephemeral/
**/Pods/
# Xcode-related
**/dgph
**/xcuserdata/

View file

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

View file

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

View file

@ -0,0 +1,22 @@
//
// Generated file. Do not edit.
//
import FlutterMacOS
import Foundation
import geolocator_apple
import package_info_plus
import printing
import share_plus
import sqlite3_flutter_libs
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
}

View file

@ -0,0 +1,705 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXAggregateTarget section */
33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
isa = PBXAggregateTarget;
buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
buildPhases = (
33CC111E2044C6BF0003C045 /* ShellScript */,
);
dependencies = (
);
name = "Flutter Assemble";
productName = FLX;
};
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 33CC10EC2044A3C60003C045;
remoteInfo = Runner;
};
33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 33CC111A2044C6BA0003C045;
remoteInfo = FLX;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
33CC110E2044A8840003C045 /* Bundle Framework */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Bundle Framework";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
33CC10ED2044A3C60003C045 /* gemi_invoice.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "gemi_invoice.app"; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = "<group>"; };
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = "<group>"; };
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = "<group>"; };
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = "<group>"; };
33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = "<group>"; };
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
331C80D2294CF70F00263BE5 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
33CC10EA2044A3C60003C045 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
331C80D6294CF71000263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C80D7294CF71000263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
};
33BA886A226E78AF003329D5 /* Configs */ = {
isa = PBXGroup;
children = (
33E5194F232828860026EE4D /* AppInfo.xcconfig */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
);
path = Configs;
sourceTree = "<group>";
};
33CC10E42044A3C60003C045 = {
isa = PBXGroup;
children = (
33FAB671232836740065AC1E /* Runner */,
33CEB47122A05771004F2AC0 /* Flutter */,
331C80D6294CF71000263BE5 /* RunnerTests */,
33CC10EE2044A3C60003C045 /* Products */,
D73912EC22F37F3D000D13A0 /* Frameworks */,
);
sourceTree = "<group>";
};
33CC10EE2044A3C60003C045 /* Products */ = {
isa = PBXGroup;
children = (
33CC10ED2044A3C60003C045 /* gemi_invoice.app */,
331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
33CC11242044D66E0003C045 /* Resources */ = {
isa = PBXGroup;
children = (
33CC10F22044A3C60003C045 /* Assets.xcassets */,
33CC10F42044A3C60003C045 /* MainMenu.xib */,
33CC10F72044A3C60003C045 /* Info.plist */,
);
name = Resources;
path = ..;
sourceTree = "<group>";
};
33CEB47122A05771004F2AC0 /* Flutter */ = {
isa = PBXGroup;
children = (
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
);
path = Flutter;
sourceTree = "<group>";
};
33FAB671232836740065AC1E /* Runner */ = {
isa = PBXGroup;
children = (
33CC10F02044A3C60003C045 /* AppDelegate.swift */,
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
33E51913231747F40026EE4D /* DebugProfile.entitlements */,
33E51914231749380026EE4D /* Release.entitlements */,
33CC11242044D66E0003C045 /* Resources */,
33BA886A226E78AF003329D5 /* Configs */,
);
path = Runner;
sourceTree = "<group>";
};
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
isa = PBXGroup;
children = (
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
331C80D4294CF70F00263BE5 /* RunnerTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
331C80D1294CF70F00263BE5 /* Sources */,
331C80D2294CF70F00263BE5 /* Frameworks */,
331C80D3294CF70F00263BE5 /* Resources */,
);
buildRules = (
);
dependencies = (
331C80DA294CF71000263BE5 /* PBXTargetDependency */,
);
name = RunnerTests;
productName = RunnerTests;
productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
33CC10EC2044A3C60003C045 /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
33CC10E92044A3C60003C045 /* Sources */,
33CC10EA2044A3C60003C045 /* Frameworks */,
33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */,
);
buildRules = (
);
dependencies = (
33CC11202044C79F0003C045 /* PBXTargetDependency */,
);
name = Runner;
productName = Runner;
productReference = 33CC10ED2044A3C60003C045 /* gemi_invoice.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
33CC10E52044A3C60003C045 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C80D4294CF70F00263BE5 = {
CreatedOnToolsVersion = 14.0;
TestTargetID = 33CC10EC2044A3C60003C045;
};
33CC10EC2044A3C60003C045 = {
CreatedOnToolsVersion = 9.2;
LastSwiftMigration = 1100;
ProvisioningStyle = Automatic;
SystemCapabilities = {
com.apple.Sandbox = {
enabled = 1;
};
};
};
33CC111A2044C6BA0003C045 = {
CreatedOnToolsVersion = 9.2;
ProvisioningStyle = Manual;
};
};
};
buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 33CC10E42044A3C60003C045;
productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
33CC10EC2044A3C60003C045 /* Runner */,
331C80D4294CF70F00263BE5 /* RunnerTests */,
33CC111A2044C6BA0003C045 /* Flutter Assemble */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
331C80D3294CF70F00263BE5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
33CC10EB2044A3C60003C045 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3399D490228B24CF009A79C7 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
};
33CC111E2044C6BF0003C045 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
Flutter/ephemeral/FlutterInputs.xcfilelist,
);
inputPaths = (
Flutter/ephemeral/tripwire,
);
outputFileListPaths = (
Flutter/ephemeral/FlutterOutputs.xcfilelist,
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
331C80D1294CF70F00263BE5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
33CC10E92044A3C60003C045 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 33CC10EC2044A3C60003C045 /* Runner */;
targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;
};
33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
33CC10F52044A3C60003C045 /* Base */,
);
name = MainMenu.xib;
path = Runner;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
331C80DB294CF71000263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.gemiInvoice.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/gemi_invoice.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/gemi_invoice";
};
name = Debug;
};
331C80DC294CF71000263BE5 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.gemiInvoice.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/gemi_invoice.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/gemi_invoice";
};
name = Release;
};
331C80DD294CF71000263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.gemiInvoice.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/gemi_invoice.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/gemi_invoice";
};
name = Profile;
};
338D0CE9231458BD00FA5F75 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
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_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
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_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
};
name = Profile;
};
338D0CEA231458BD00FA5F75 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};
name = Profile;
};
338D0CEB231458BD00FA5F75 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Profile;
};
33CC10F92044A3C60003C045 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
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_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
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_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
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_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
33CC10FA2044A3C60003C045 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
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_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
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_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
};
name = Release;
};
33CC10FC2044A3C60003C045 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
33CC10FD2044A3C60003C045 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};
name = Release;
};
33CC111C2044C6BA0003C045 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
33CC111D2044C6BA0003C045 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C80DB294CF71000263BE5 /* Debug */,
331C80DC294CF71000263BE5 /* Release */,
331C80DD294CF71000263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
33CC10F92044A3C60003C045 /* Debug */,
33CC10FA2044A3C60003C045 /* Release */,
338D0CE9231458BD00FA5F75 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
33CC10FC2044A3C60003C045 /* Debug */,
33CC10FD2044A3C60003C045 /* Release */,
338D0CEA231458BD00FA5F75 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
isa = XCConfigurationList;
buildConfigurations = (
33CC111C2044C6BA0003C045 /* Debug */,
33CC111D2044C6BA0003C045 /* Release */,
338D0CEB231458BD00FA5F75 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 33CC10E52044A3C60003C045 /* Project object */;
}

View file

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

View file

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

View file

@ -0,0 +1,13 @@
import Cocoa
import FlutterMacOS
@main
class AppDelegate: FlutterAppDelegate {
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
}

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

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