2
0
mirror of https://github.com/inventree/inventree-app.git synced 2025-05-02 07:26:50 +00:00

Merge pull request #2 from SchrodingersGat/initial

Initial
This commit is contained in:
Oliver 2020-04-05 01:13:10 +11:00 committed by GitHub
commit 51ac5b0bfb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
75 changed files with 3737 additions and 1 deletions

71
.gitignore vendored Normal file
View File

@ -0,0 +1,71 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# Visual Studio Code related
.vscode/
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages

10
.metadata Normal file
View File

@ -0,0 +1,10 @@
# 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: 7a4c33425ddd78c54aba07d86f3f9a4a0051769b
channel: stable
project_type: app

View File

@ -1,3 +1,11 @@
# InvenTreee Mobile App # InvenTreee Mobile App
The InvenTree mobile / tablet application (supports Android + iOS) is a companion app for the [InvenTree stock management system](https://github.com/inventree/InvenTree). The InvenTree mobile / tablet application is a companion app for the [InvenTree stock management system](https://github.com/inventree/InvenTree).
Written in the [Flutter](https://flutter.dev/) environment, the app provides native support for Android and iOS devices.
## Features
`TODO`

65
android/app/build.gradle Normal file
View File

@ -0,0 +1,65 @@
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 28
packagingOptions {
exclude 'META-INF/proguard/androidx-annotations.pro'
}
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "inventree.inventree_app"
minSdkVersion 16
targetSdkVersion 28
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

View File

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="inventree.inventree_app">
<!-- Flutter 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,33 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="inventree.inventree_app">
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name="io.flutter.app.FlutterApplication"
android:label="InvenTree"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- This keeps the window background of the activity showing
until Flutter renders its first frame. It can be removed if
there is no splash screen (such as the default splash screen
defined in @style/LaunchTheme). -->
<meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,13 @@
package inventree.inventree_app;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
}

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: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
</resources>

View File

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

29
android/build.gradle Normal file
View File

@ -0,0 +1,29 @@
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@ -0,0 +1,2 @@
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true

View File

@ -0,0 +1,6 @@
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip

15
android/settings.gradle Normal file
View File

@ -0,0 +1,15 @@
include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}

BIN
assets/image/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

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>8.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,10 @@
#!/bin/sh
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=C:\flutter"
export "FLUTTER_APPLICATION_PATH=C:\inventree-app"
export "FLUTTER_TARGET=lib\main.dart"
export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build\ios"
export "FLUTTER_FRAMEWORK_DIR=C:\flutter\bin\cache\artifacts\engine\ios"
export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1"

View File

@ -0,0 +1,506 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; 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>"; };
9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
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 = (
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B80C3931E831B6300D905FE /* App.framework */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEBA1CF902C7004384FC /* Flutter.framework */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
CF3B75C9A7D2FA2A4C99F110 /* Frameworks */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
);
path = Runner;
sourceTree = "<group>";
};
97C146F11CF9000F007C117D /* Supporting Files */ = {
isa = PBXGroup;
children = (
97C146F21CF9000F007C117D /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0910;
ORGANIZATIONNAME = "The Chromium Authors";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
9740EEB41CF90195004384FC /* Debug.xcconfig 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;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
97C146F31CF9000F007C117D /* main.m in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_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_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = 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;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = S8QB4VV633;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = inventree.inventreeApp;
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_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_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_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_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = inventree.inventreeApp;
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = inventree.inventreeApp;
PRODUCT_NAME = "$(TARGET_NAME)";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}

View File

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

View File

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

View File

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

6
ios/Runner/AppDelegate.h Normal file
View File

@ -0,0 +1,6 @@
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
@interface AppDelegate : FlutterAppDelegate
@end

13
ios/Runner/AppDelegate.m Normal file
View File

@ -0,0 +1,13 @@
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end

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: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 785 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 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>

45
ios/Runner/Info.plist Normal file
View File

@ -0,0 +1,45 @@
<?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>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>inventree_app</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>

9
ios/Runner/main.m Normal file
View File

@ -0,0 +1,9 @@
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char* argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

313
lib/api.dart Normal file
View File

@ -0,0 +1,313 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_advanced_networkimage/provider.dart';
import 'package:image/image.dart';
import 'package:path/path.dart' as path;
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
/**
* InvenTree API - Access to the InvenTree REST interface.
*
* InvenTree implements token-based authentication, which is
* initialised using a username:password combination.
*/
class InvenTreeAPI {
// Endpoint for requesting an API token
static const _URL_GET_TOKEN = "user/token/";
static const _URL_GET_VERSION = "";
// Base URL for InvenTree API e.g. http://192.168.120.10:8000
String _BASE_URL = "";
// Accessors for various url endpoints
String get baseUrl {
String url = _BASE_URL;
if (!url.endsWith("/")) {
url += "/";
}
return url;
}
String _makeUrl(String url) {
if (url.startsWith('/')) {
url = url.substring(1, url.length);
}
url = url.replaceAll('//', '/');
return baseUrl + url;
}
String get apiUrl {
return _makeUrl("/api/");
}
String get imageUrl {
return _makeUrl("/image/");
}
String makeApiUrl(String endpoint) {
return apiUrl + endpoint;
}
String makeUrl(String endpoint) {
return _makeUrl(endpoint);
}
String _username = "";
String _password = "";
// Authentication token (initially empty, must be requested)
String _token = "";
// Server version information
String _version;
// Getter for server version information
String get version => _version;
// Connection status flag - set once connection has been validated
bool _connected = false;
bool get connected {
return _connected && baseUrl.isNotEmpty && _token.isNotEmpty;
}
// Ensure we only ever create a single instance of the API class
static final InvenTreeAPI _api = new InvenTreeAPI._internal();
factory InvenTreeAPI() { return _api; }
InvenTreeAPI._internal();
Future<bool> connect() async {
var prefs = await SharedPreferences.getInstance();
String server = prefs.getString("server");
String username = prefs.getString("username");
String password = prefs.getString("password");
return connectToServer(server, username, password);
}
Future<bool> connectToServer(String address, String username, String password) async {
/* Address is the base address for the InvenTree server,
* e.g. http://127.0.0.1:8000
*/
String errorMessage = "";
address = address.trim();
username = username.trim();
if (address.isEmpty || username.isEmpty || password.isEmpty) {
errorMessage = "Server Error: Empty details supplied";
print(errorMessage);
throw errorMessage;
}
if (!address.endsWith('/')) {
address = address + '/';
}
// TODO - Better URL validation
/*
* - If not a valid URL, return error
* - If no port supplied, append a default port
*/
_BASE_URL = address;
_username = username;
_password = password;
_connected = false;
print("Connecting to " + apiUrl + " -> " + username + ":" + password);
// TODO - Add connection timeout
var response = await get("").timeout(Duration(seconds: 10)).catchError((error) {
if (error is SocketException) {
errorMessage = "Could not connect to server.";
print(errorMessage);
throw errorMessage;
} else {
// Unknown error type, re-throw error
throw error;
}
});
if (response.statusCode != 200) {
print("Invalid status code: " + response.statusCode.toString());
return false;
}
var data = json.decode(response.body);
print("Response from server: $data");
// We expect certain response from the server
if (!data.containsKey("server") || !data.containsKey("version")) {
errorMessage = "Server resonse contained incorrect data";
print(errorMessage);
throw errorMessage;
}
print("Server: " + data["server"]);
print("Version: " + data["version"]);
_version = data["version"];
// Request token from the server if we do not already have one
if (_token.isNotEmpty) {
print("Already have token - $_token");
return true;
}
// Clear out the token
_token = "";
response = await post(_URL_GET_TOKEN, body: {"username": _username, "password": _password}).catchError((error) {
print("Error requesting token:");
print(error);
return false;
});
if (response.statusCode != 200) {
print("Invalid status code: " + response.statusCode.toString());
return false;
} else {
var data = json.decode(response.body);
if (!data.containsKey("token")) {
print("No token provided in response");
return false;
}
// Return the received token
_token = data["token"];
print("Received token - $_token");
_connected = true;
return true;
};
}
// Perform a PATCH request
Future<http.Response> patch(String url, {Map<String, String> body}) async {
var _url = makeApiUrl(url);
var _headers = defaultHeaders();
var _body = Map<String, String>();
// Copy across provided data
body.forEach((K, V) => _body[K] = V);
print("PATCH: " + _url);
final response = await http.patch(_url,
headers: _headers,
body: _body,
);
return response;
}
// Perform a POST request
Future<http.Response> post(String url, {Map<String, String> body}) async {
var _url = makeApiUrl(url);
var _headers = defaultHeaders();
var _body = Map<String, String>();
// Copy across provided data
body.forEach((K, V) => _body[K] = V);
print("POST: " + _url);
return http.post(_url,
headers: _headers,
body: _body,
);
}
// Perform a GET request
Future<http.Response> get(String url, {Map<String, String> params}) async {
var _url = makeApiUrl(url);
var _headers = defaultHeaders();
// If query parameters are supplied, form a query string
if (params != null && params.isNotEmpty) {
String query = '?';
params.forEach((K, V) => query += K + '=' + V + '&');
_url += query;
}
// Remove extraneous character if present
if (_url.endsWith('&')) {
_url = _url.substring(0, _url.length - 1);
}
print("GET: " + _url);
return http.get(_url, headers: _headers);
}
Map<String, String> defaultHeaders() {
var headers = Map<String, String>();
headers[HttpHeaders.authorizationHeader] = _authorizationHeader();
//headers['Authorization'] = _authorizationHeader();
return headers;
}
String _authorizationHeader () {
if (_token.isNotEmpty) {
return "Token $_token";
} else {
return "Basic " + base64Encode(utf8.encode('$_username:$_password'));
}
}
static String get staticImage => "/static/img/blank_image.png";
static String get staticThumb => "/static/img/blank_image.thumbnail.png";
/*
* Get an image from the server (or, from cache)
*/
AdvancedNetworkImage getImage(String imageUrl) {
if (imageUrl.isEmpty) {
imageUrl = staticImage;
}
return new AdvancedNetworkImage(makeUrl(imageUrl),
header: defaultHeaders(),
useDiskCache: true,
cacheRule: CacheRule(maxAge: const Duration(days: 5)),
);
}
}

70
lib/barcode.dart Normal file
View File

@ -0,0 +1,70 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:qr_utils/qr_utils.dart';
import 'package:InvenTree/inventree/stock.dart';
import 'package:InvenTree/inventree/part.dart';
import 'package:InvenTree/widget/location_display.dart';
import 'package:InvenTree/widget/part_display.dart';
import 'package:InvenTree/widget/category_display.dart';
import 'package:InvenTree/widget/stock_display.dart';
import 'dart:convert';
void scanQrCode(BuildContext context) async {
QrUtils.scanQR.then((String result) {
print("Scanned: $result");
// Look for JSON data in the result...
final data = json.decode(result);
// Look for an 'InvenTree' style barcode
if ((data['tool'] ?? '').toString().toLowerCase() == 'inventree') {
_handleInvenTreeBarcode(context, data);
}
// Unknown barcode style!
else {
showDialog(
context: context,
child: new SimpleDialog(
title: new Text("Unknown barcode"),
children: <Widget>[
Text("Data: $result"),
]
)
);
}
});
}
void _handleInvenTreeBarcode(BuildContext context, Map<String, dynamic> data) {
final String codeType = (data['type'] ?? '').toString().toLowerCase();
final int pk = (data['id'] ?? -1) as int;
if (codeType == 'stocklocation') {
// Try to open a stock location...
InvenTreeStockLocation().get(pk).then((var loc) {
if (loc is InvenTreeStockLocation) {
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(loc)));
}
});
} else if (codeType == 'stockitem') {
InvenTreeStockItem().get(pk).then((var item) {
Navigator.push(context, MaterialPageRoute(builder: (context) => StockItemDisplayWidget(item)));
});
} else if (codeType == 'part') {
InvenTreePart().get(pk).then((var part) {
Navigator.push(context,
MaterialPageRoute(builder: (context) => PartDisplayWidget(part)));
});
}
}

122
lib/generated/i18n.dart Normal file
View File

@ -0,0 +1,122 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: camel_case_types
// ignore_for_file: prefer_single_quotes
// This file is automatically generated. DO NOT EDIT, all your changes would be lost.
class S implements WidgetsLocalizations {
const S();
static const GeneratedLocalizationsDelegate delegate =
GeneratedLocalizationsDelegate();
static S of(BuildContext context) => Localizations.of<S>(context, S);
@override
TextDirection get textDirection => TextDirection.ltr;
}
class $en extends S {
const $en();
}
class GeneratedLocalizationsDelegate extends LocalizationsDelegate<S> {
const GeneratedLocalizationsDelegate();
List<Locale> get supportedLocales {
return const <Locale>[
Locale("en", ""),
];
}
LocaleListResolutionCallback listResolution({Locale fallback, bool withCountry = true}) {
return (List<Locale> locales, Iterable<Locale> supported) {
if (locales == null || locales.isEmpty) {
return fallback ?? supported.first;
} else {
return _resolve(locales.first, fallback, supported, withCountry);
}
};
}
LocaleResolutionCallback resolution({Locale fallback, bool withCountry = true}) {
return (Locale locale, Iterable<Locale> supported) {
return _resolve(locale, fallback, supported, withCountry);
};
}
@override
Future<S> load(Locale locale) {
final String lang = getLang(locale);
if (lang != null) {
switch (lang) {
case "en":
return SynchronousFuture<S>(const $en());
default:
// NO-OP.
}
}
return SynchronousFuture<S>(const S());
}
@override
bool isSupported(Locale locale) => _isSupported(locale, true);
@override
bool shouldReload(GeneratedLocalizationsDelegate old) => false;
///
/// Internal method to resolve a locale from a list of locales.
///
Locale _resolve(Locale locale, Locale fallback, Iterable<Locale> supported, bool withCountry) {
if (locale == null || !_isSupported(locale, withCountry)) {
return fallback ?? supported.first;
}
final Locale languageLocale = Locale(locale.languageCode, "");
if (supported.contains(locale)) {
return locale;
} else if (supported.contains(languageLocale)) {
return languageLocale;
} else {
final Locale fallbackLocale = fallback ?? supported.first;
return fallbackLocale;
}
}
///
/// Returns true if the specified locale is supported, false otherwise.
///
bool _isSupported(Locale locale, bool withCountry) {
if (locale != null) {
for (Locale supportedLocale in supportedLocales) {
// Language must always match both locales.
if (supportedLocale.languageCode != locale.languageCode) {
continue;
}
// If country code matches, return this locale.
if (supportedLocale.countryCode == locale.countryCode) {
return true;
}
// If no country requirement is requested, check if this locale has no country.
if (true != withCountry && (supportedLocale.countryCode == null || supportedLocale.countryCode.isEmpty)) {
return true;
}
}
}
return false;
}
}
String getLang(Locale l) => l == null
? null
: l.countryCode != null && l.countryCode.isEmpty
? l.languageCode
: l.toString();

View File

@ -0,0 +1,45 @@
import 'model.dart';
/*
* The InvenTreeCompany class repreents the Company model in the InvenTree database.
*/
class InvenTreeCompany extends InvenTreeModel {
@override
String URL = "company/";
InvenTreeCompany() : super();
InvenTreeCompany.fromJson(Map<String, dynamic> json) : super.fromJson(json) {
// TODO
}
@override
InvenTreeModel createFromJson(Map<String, dynamic> json) {
var company = InvenTreeCompany.fromJson(json);
return company;
}
}
/*
* The InvenTreeSupplierPart class represents the SupplierPart model in the InvenTree database
*/
class InvenTreeSupplierPart extends InvenTreeModel {
@override
String url = "company/part/";
InvenTreeSupplierPart() : super();
InvenTreeSupplierPart.fromJson(Map<String, dynamic> json) : super.fromJson(json) {
// TODO
}
@override
InvenTreeModel createFromJson(Map<String, dynamic> json) {
var part = InvenTreeSupplierPart.fromJson(json);
return part;
}
}

167
lib/inventree/model.dart Normal file
View File

@ -0,0 +1,167 @@
import 'package:InvenTree/api.dart';
import 'dart:convert';
import 'package:path/path.dart' as path;
import 'package:http/http.dart' as http;
/**
* The InvenTreeModel class provides a base-level object
* for interacting with InvenTree data.
*/
class InvenTreeModel {
// Override the endpoint URL for each subclass
String URL = "";
// JSON data which defines this object
Map<String, dynamic> jsondata = {};
// Accessor for the API
var api = InvenTreeAPI();
// Default empty object constructor
InvenTreeModel() {
jsondata.clear();
}
// Construct an InvenTreeModel from a JSON data object
InvenTreeModel.fromJson(Map<String, dynamic> json) {
// Store the json object
jsondata = json;
}
int get pk => jsondata['pk'] ?? -1;
// Some common accessors
String get name => jsondata['name'] ?? '';
String get description => jsondata['description'] ?? '';
int get parentId => jsondata['parent'] ?? -1;
// Create a new object from JSON data (not a constructor!)
InvenTreeModel createFromJson(Map<String, dynamic> json) {
var obj = InvenTreeModel.fromJson(json);
return obj;
}
String get url{ return path.join(URL, pk.toString()); }
/*
// Search this Model type in the database
Future<List<InvenTreeModel>> search(String searchTerm) async {
String addr = url + "?search=" + search;
print("Searching endpoint: $url");
// TODO - Add "timeout"
// TODO - Add error catching
var response =
}
*/
// Return the detail view for the associated pk
Future<InvenTreeModel> get(int pk) async {
// TODO - Add "timeout"
// TODO - Add error catching
var addr = path.join(URL, pk.toString());
if (!addr.endsWith("/")) {
addr += "/";
}
var response = await InvenTreeAPI().get(addr);
if (response.statusCode != 200) {
print("Error retrieving data");
return null;
}
final data = json.decode(response.body);
return createFromJson(data);
}
// Return list of objects from the database, with optional filters
Future<List<InvenTreeModel>> list({Map<String, String> filters}) async {
if (filters == null) {
filters = {};
}
print("Listing endpoint: $URL");
// TODO - Add "timeout"
// TODO - Add error catching
var response = await InvenTreeAPI().get(URL, params:filters);
// A list of "InvenTreeModel" items
List<InvenTreeModel> results = new List<InvenTreeModel>();
if (response.statusCode != 200) {
print("Error retreiving data");
return results;
}
final data = json.decode(response.body);
// TODO - handle possible error cases:
// - No data receieved
// - Data is not a list of maps
for (var d in data) {
// Create a new object (of the current class type
InvenTreeModel obj = createFromJson(d);
if (obj != null) {
results.add(obj);
}
}
return results;
}
// Provide a listing of objects at the endpoint
// TODO - Static function which returns a list of objects (of this class)
// TODO - Define a 'delete' function
// TODO - Define a 'save' / 'update' function
// Override this function for each sub-class
bool matchAgainstString(String filter) => false;
// Filter this item against a list of provided filters
// Each filter must be matched
// Used for (e.g.) filtering returned results
bool filter(String filterString) {
List<String> filters = filterString.trim().toLowerCase().split(" ");
for (var f in filters) {
if (!matchAgainstString(f)) {
return false;
}
}
return true;
}
}

74
lib/inventree/part.dart Normal file
View File

@ -0,0 +1,74 @@
import 'package:InvenTree/api.dart';
import 'model.dart';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:http/http.dart' as http;
class InvenTreePartCategory extends InvenTreeModel {
@override
String URL = "part/category/";
String get pathstring => jsondata['pathstring'] ?? '';
InvenTreePartCategory() : super();
InvenTreePartCategory.fromJson(Map<String, dynamic> json) : super.fromJson(json) {
}
@override
InvenTreeModel createFromJson(Map<String, dynamic> json) {
var cat = InvenTreePartCategory.fromJson(json);
// TODO ?
return cat;
}
}
class InvenTreePart extends InvenTreeModel {
@override
String URL = "part/";
int get categoryId => jsondata['category'] as int ?? -1;
String get categoryName => jsondata['category__name'] ?? '';
String get _image => jsondata['image'] ?? '';
String get _thumbnail => jsondata['thumbnail'] ?? '';
// Return a path to the image for this Part
String get image {
// Use thumbnail as a backup
String img = _image.isNotEmpty ? _image : _thumbnail;
return img.isNotEmpty ? img : InvenTreeAPI.staticImage;
}
// Return a path to the thumbnail for this part
String get thumbnail {
// Use image as a backup
String img = _thumbnail.isNotEmpty ? _thumbnail : _image;
return img.isNotEmpty ? img : InvenTreeAPI.staticThumb;
}
InvenTreePart() : super();
InvenTreePart.fromJson(Map<String, dynamic> json) : super.fromJson(json) {
// TODO
}
@override
InvenTreeModel createFromJson(Map<String, dynamic> json) {
var part = InvenTreePart.fromJson(json);
return part;
}
}

75
lib/inventree/stock.dart Normal file
View File

@ -0,0 +1,75 @@
import 'model.dart';
import 'package:InvenTree/api.dart';
class InvenTreeStockItem extends InvenTreeModel {
@override
String URL = "stock/";
InvenTreeStockItem() : super();
InvenTreeStockItem.fromJson(Map<String, dynamic> json) : super.fromJson(json) {
// TODO
}
String get partName => jsondata['part__name'] as String ?? '';
String get partDescription => jsondata['part__description'] as String ?? '';
String get partThumbnail => jsondata['part__thumbnail'] as String ?? InvenTreeAPI.staticThumb;
int get serialNumber => jsondata['serial'] as int ?? null;
double get quantity => jsondata['quantity'] as double ?? 0.0;
int get locationId => jsondata['location'] as int ?? -1;
String get displayQuantity {
// Display either quantity or serial number!
if (serialNumber != null) {
return "SN: $serialNumber";
} else {
return quantity.toString().trim();
}
}
@override
InvenTreeModel createFromJson(Map<String, dynamic> json) {
var item = InvenTreeStockItem.fromJson(json);
// TODO?
return item;
}
}
class InvenTreeStockLocation extends InvenTreeModel {
@override
String URL = "stock/location/";
InvenTreeStockLocation() : super();
InvenTreeStockLocation.fromJson(Map<String, dynamic> json) : super.fromJson(json) {
}
@override
InvenTreeModel createFromJson(Map<String, dynamic> json) {
var loc = InvenTreeStockLocation.fromJson(json);
return loc;
}
@override
bool matchAgainstString(String filter) {
if (name.toLowerCase().contains(filter)) return true;
if (description.toLowerCase().contains(filter)) return true;
return false;
}
}

143
lib/login_settings.dart Normal file
View File

@ -0,0 +1,143 @@
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'api.dart';
import 'preferences.dart';
class InvenTreeLoginSettingsWidget extends StatefulWidget {
@override
_InvenTreeLoginSettingsState createState() => _InvenTreeLoginSettingsState();
}
class _InvenTreeLoginSettingsState extends State<InvenTreeLoginSettingsWidget> {
final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
String _addr;
String _user;
String _pass;
String _validateServer(String value) {
if (value.isEmpty) {
return 'Server cannot be empty';
}
if (!value.startsWith("http:") && !value.startsWith("https:")) {
return 'Server must start with http[s]';
}
return null;
}
String _validateUsername(String value) {
if (value.isEmpty) {
return 'Username cannot be empty';
}
return null;
}
String _validatePassword(String value) {
if (value.isEmpty) {
return 'Password cannot be empty';
}
return null;
}
@override
void initState() {
load();
}
@override
Widget build(BuildContext context) {
final Size screenSize = MediaQuery.of(context).size;
load();
return Scaffold(
appBar: AppBar(
title: Text("Login Settings"),
),
body: new Container(
padding: new EdgeInsets.all(20.0),
child: new Form(
key: _formKey,
child: new ListView(
children: <Widget>[
Text("Server"),
new TextFormField(
initialValue: _addr,
decoration: InputDecoration(
hintText: "127.0.0.1:8000",
labelText: "Server:Port",
),
validator: _validateServer,
onSaved: (String value) {
_addr = value;
},
),
Text("Login Details"),
TextFormField(
initialValue: _user,
decoration: InputDecoration(
hintText: "Username",
labelText: "Username",
),
validator: _validateUsername,
onSaved: (String value) {
_user = value;
}
),
TextFormField(
initialValue: _pass,
obscureText: true,
decoration: InputDecoration(
hintText: "Password",
labelText: "Password",
),
validator: _validatePassword,
onSaved: (String value) {
_pass = value;
},
),
Container(
width: screenSize.width,
child: RaisedButton(
child: Text("Login"),
onPressed: this.save,
)
)
],
)
)
)
);
}
void load() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
_addr = prefs.getString('server');
_user = prefs.getString('username');
_pass = prefs.getString('password');
// Refresh the widget
setState(() {
});
}
void save() async {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
await InvenTreeUserPreferences().saveLoginDetails(_addr, _user, _pass);
}
}
}

307
lib/main.dart Normal file
View File

@ -0,0 +1,307 @@
import 'dart:async';
import 'package:InvenTree/inventree/stock.dart';
import 'package:InvenTree/widget/category_display.dart';
import 'package:InvenTree/widget/location_display.dart';
import 'package:InvenTree/widget/drawer.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'barcode.dart';
import 'dart:convert';
import 'settings.dart';
import 'api.dart';
import 'preferences.dart';
import 'package:InvenTree/inventree/part.dart';
void main() async {
// await PrefService.init(prefix: "inventree_");
WidgetsFlutterBinding.ensureInitialized();
// Load login details
InvenTreeUserPreferences().loadLoginDetails();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'InvenTree',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.lightGreen,
),
home: MyHomePage(title: 'InvenTree'),
);
}
}
class ProductList extends StatelessWidget {
final List<InvenTreePart> _parts;
ProductList(this._parts);
Widget _buildPart(BuildContext context, int index) {
InvenTreePart part;
if (index < _parts.length) {
part = _parts[index];
}
return Card(
child: Column(
children: <Widget>[
Text('${part.name} - ${part.description}'),
]
)
);
}
@override
Widget build(BuildContext context) {
return ListView.builder(itemBuilder: _buildPart, itemCount: _parts.length);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// 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
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
_MyHomePageState() : super() {
_checkServerConnection();
}
String _serverAddress = "";
String _serverStatus = "Connecting to server";
String _serverMessage = "";
bool _serverConnection = false;
Color _serverStatusColor = Color.fromARGB(255, 50, 50, 250);
/*
* Test the server connection
*/
void _checkServerConnection() async {
var prefs = await SharedPreferences.getInstance();
print("Checking server connection");
_serverAddress = prefs.getString("server");
InvenTreeAPI().connect().then((bool result) {
print("Connection status: $result");
_serverConnection = result;
if (_serverConnection) {
_serverStatus = "Connected to server: $_serverAddress";
_serverMessage = "";
_serverStatusColor = Color.fromARGB(255, 50, 250, 50);
} else {
_serverStatus = "Could not connect to server: $_serverAddress";
_serverStatusColor = Color.fromARGB(255, 250, 50, 50);
}
setState(() {});
}).catchError((e) {
_serverConnection = false;
_serverStatusColor = Color.fromARGB(255, 250, 50, 50);
_serverStatus = "Error connecting to $_serverAddress";
if (e is TimeoutException) {
_serverMessage = "No response from server";
} else {
_serverMessage = e.toString();
}
print("Server error: $_serverMessage");
setState(() {});
});
}
void _search() {
// TODO
}
void _scan() {
scanQrCode(context);
}
void _parts() {
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null)));
}
void _stock() {
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(null)));
}
void _suppliers() {
// TODO
}
@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(
// 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),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
tooltip: 'Search',
onPressed: null,
),
],
),
drawer: new InvenTreeDrawer(context),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Column(
children: <Widget>[
IconButton(
icon: new Icon(Icons.search),
tooltip: 'Search',
onPressed: _search,
),
Text("Search"),
],
),
Column(
children: <Widget>[
IconButton(
icon: new Icon(Icons.search),
tooltip: 'Scan Barcode',
onPressed: _scan,
),
Text("Scan Barcode"),
],
),
],
),
Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Column(
children: <Widget>[
IconButton(
icon: new Icon(Icons.category),
tooltip: 'Parts',
onPressed: _parts,
),
Text("Parts"),
],
),
Column(
children: <Widget>[
IconButton(
icon: new Icon(Icons.map),
tooltip: 'Stock',
onPressed: _stock,
),
Text('Stock'),
],
),
Column(
children: <Widget>[
IconButton(
icon: new Icon(Icons.business),
tooltip: 'Suppliers',
onPressed: _suppliers,
),
Text("Suppliers"),
]
)
],
),
Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: Card(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text('$_serverStatus',
style: TextStyle(
color: _serverStatusColor,
),
),
Text('$_serverMessage',
style: TextStyle(
color: _serverStatusColor,
),
),
],
),
),
),
],
),
],
),
),
);
}
}

45
lib/preferences.dart Normal file
View File

@ -0,0 +1,45 @@
import 'package:shared_preferences/shared_preferences.dart';
import 'api.dart';
class InvenTreeUserPreferences {
static const String _SERVER = 'server';
static const String _USERNAME = 'username';
static const String _PASSWORD = 'password';
// Ensure we only ever create a single instance of the preferences class
static final InvenTreeUserPreferences _api = new InvenTreeUserPreferences._internal();
factory InvenTreeUserPreferences() {
return _api;
}
InvenTreeUserPreferences._internal();
// Load saved login details, and attempt connection
void loadLoginDetails() async {
print("Loading login details");
SharedPreferences prefs = await SharedPreferences.getInstance();
var server = prefs.getString(_SERVER) ?? '';
var username = prefs.getString(_USERNAME) ?? '';
var password = prefs.getString(_PASSWORD) ?? '';
await InvenTreeAPI().connectToServer(server, username, password);
}
void saveLoginDetails(String server, String username, String password) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString(_SERVER, server);
await prefs.setString(_USERNAME, username);
await prefs.setString(_PASSWORD, password);
// Reconnect the API
await InvenTreeAPI().connectToServer(server, username, password);
}
}

76
lib/settings.dart Normal file
View File

@ -0,0 +1,76 @@
import 'package:flutter/material.dart';
import 'login_settings.dart';
import 'package:package_info/package_info.dart';
class InvenTreeSettingsWidget extends StatefulWidget {
// InvenTree settings view
@override
_InvenTreeSettingsState createState() => _InvenTreeSettingsState();
}
class _InvenTreeSettingsState extends State<InvenTreeSettingsWidget> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("InvenTree Settings"),
),
body: Center(
child: ListView(
children: <Widget>[
ListTile(
title: Text("Server Settings"),
subtitle: Text("Configure server and login settings"),
onTap: _editServerSettings,
),
Divider(),
ListTile(
title: Text("About"),
subtitle: Text("App details"),
onTap: _about,
),
],
)
)
);
}
void _editServerSettings() {
Navigator.push(context, MaterialPageRoute(builder: (context) => InvenTreeLoginSettingsWidget()));
}
void _about() async {
PackageInfo.fromPlatform().then((PackageInfo info) {
showDialog(
context: context,
child: new SimpleDialog(
title: new Text("About InvenTree"),
children: <Widget>[
ListTile(
title: Text("App Name"),
subtitle: Text("${info.appName}"),
),
ListTile(
title: Text("App Version"),
subtitle: Text("${info.version}"),
),
ListTile(
title: Text("Package Name"),
subtitle: Text("${info.packageName}"),
),
ListTile(
title: Text("Build Number"),
subtitle: Text("${info.buildNumber}")
),
]
),
);
});
}
}

View File

@ -0,0 +1,197 @@
import 'package:InvenTree/api.dart';
import 'package:InvenTree/inventree/part.dart';
import 'package:InvenTree/widget/part_display.dart';
import 'package:InvenTree/widget/drawer.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_advanced_networkimage/provider.dart';
class CategoryDisplayWidget extends StatefulWidget {
CategoryDisplayWidget(this.category, {Key key}) : super(key: key);
final InvenTreePartCategory category;
final String title = "Category";
@override
_CategoryDisplayState createState() => _CategoryDisplayState(category);
}
class _CategoryDisplayState extends State<CategoryDisplayWidget> {
_CategoryDisplayState(this.category) {
_requestData();
}
// The local InvenTreePartCategory object
final InvenTreePartCategory category;
List<InvenTreePartCategory> _subcategories = List<InvenTreePartCategory>();
List<InvenTreePart> _parts = List<InvenTreePart>();
String get _titleString {
if (category == null) {
return "Part Categories";
} else {
return "Part Category '${category.name}'";
}
}
/*
* Request data from the server
*/
void _requestData() {
int pk = category?.pk ?? -1;
// Request a list of sub-categories under this one
InvenTreePartCategory().list(filters: {"parent": "$pk"}).then((var cats) {
_subcategories.clear();
for (var cat in cats) {
if (cat is InvenTreePartCategory) {
_subcategories.add(cat);
}
}
// Update state
setState(() {});
});
// Request a list of parts under this category
InvenTreePart().list(filters: {"category": "$pk"}).then((var parts) {
_parts.clear();
for (var part in parts) {
if (part is InvenTreePart) {
_parts.add(part);
}
}
// Update state
setState(() {});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_titleString),
),
drawer: new InvenTreeDrawer(context),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Subcategories - ${_subcategories.length}",
textAlign: TextAlign.left,
style: TextStyle(fontWeight: FontWeight.bold),
),
Expanded(child: SubcategoryList(_subcategories)),
Divider(),
Text("Parts - ${_parts.length}",
textAlign: TextAlign.left,
style: TextStyle(fontWeight: FontWeight.bold),
),
Expanded(child: PartList(_parts)),
]
)
)
);
}
}
/*
* Builder for displaying a list of PartCategory objects
*/
class SubcategoryList extends StatelessWidget {
final List<InvenTreePartCategory> _categories;
SubcategoryList(this._categories);
void _openCategory(BuildContext context, int pk) {
// Attempt to load the sub-category.
InvenTreePartCategory().get(pk).then((var cat) {
if (cat is InvenTreePartCategory) {
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(cat)));
}
});
}
Widget _build(BuildContext context, int index) {
InvenTreePartCategory cat = _categories[index];
return ListTile(
title: Text("${cat.name}"),
subtitle: Text("${cat.description}"),
onTap: () {
_openCategory(context, cat.pk);
}
);
}
@override
Widget build(BuildContext context) {
return ListView.builder(itemBuilder: _build, itemCount: _categories.length);
}
}
/*
* Builder for displaying a list of Part objects
*/
class PartList extends StatelessWidget {
final List<InvenTreePart> _parts;
PartList(this._parts);
void _openPart(BuildContext context, int pk) {
// Attempt to load the part information
InvenTreePart().get(pk).then((var part) {
if (part is InvenTreePart) {
Navigator.push(context, MaterialPageRoute(builder: (context) => PartDisplayWidget(part)));
}
});
}
Widget _build(BuildContext context, int index) {
InvenTreePart part;
if (index < _parts.length) {
part = _parts[index];
}
return ListTile(
title: Text("${part.name}"),
subtitle: Text("${part.description}"),
leading: Image(
image: InvenTreeAPI().getImage(part.thumbnail),
width: 48,
),
onTap: () {
_openPart(context, part.pk);
},
);
}
@override
Widget build(BuildContext context) {
return ListView.builder(itemBuilder: _build, itemCount: _parts.length);
}
}

114
lib/widget/drawer.dart Normal file
View File

@ -0,0 +1,114 @@
import 'package:InvenTree/barcode.dart';
import 'package:flutter/material.dart';
import 'package:InvenTree/widget/category_display.dart';
import 'package:InvenTree/widget/location_display.dart';
import 'package:InvenTree/settings.dart';
class InvenTreeDrawer extends StatelessWidget {
final BuildContext context;
InvenTreeDrawer(this.context);
void _closeDrawer() {
// Close the drawer
Navigator.of(context).pop();
}
/*
* Return to the 'home' screen.
* This will empty the navigation stack.
*/
void _home() {
_closeDrawer();
Navigator.pushNamedAndRemoveUntil(context, "/", (r) => false);
}
/*
* Launch the camera to scan a QR code.
* Upon successful scan, data are passed off to be decoded.
*/
void _scan() async {
_closeDrawer();
scanQrCode(context);
}
/*
* Display the top-level PartCategory list
*/
void _showParts() {
_closeDrawer();
Navigator.push(context, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null)));
}
/*
* Display the top-level StockLocation list
*/
void _showStock() {
_closeDrawer();
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(null)));
}
/*
* Load settings widget
*/
void _settings() {
_closeDrawer();
Navigator.push(context, MaterialPageRoute(builder: (context) => InvenTreeSettingsWidget()));
}
@override
Widget build(BuildContext context) {
return Drawer(
child: new ListView(
children: <Widget>[
new ListTile(
leading: new Image.asset(
"assets/image/icon.png",
fit: BoxFit.scaleDown,
),
title: new Text("InvenTree"),
onTap: _home,
),
new Divider(),
new ListTile(
title: new Text("Search"),
leading: new Icon(Icons.search),
onTap: null,
),
new ListTile(
title: new Text("Scan"),
onTap: _scan,
leading: new Icon(Icons.search),
),
new Divider(),
new ListTile(
title: new Text("Parts"),
leading: new Icon(Icons.category),
onTap: _showParts,
),
new ListTile(
title: new Text("Stock"),
onTap: _showStock,
),
new ListTile(
title: new Text("Suppliers"),
leading: new Icon(Icons.business),
onTap: null,
),
new Divider(),
new ListTile(
title: new Text("Settings"),
leading: new Icon(Icons.settings),
onTap: _settings,
),
]
)
);
}
}

View File

@ -0,0 +1,197 @@
import 'package:InvenTree/api.dart';
import 'package:InvenTree/inventree/stock.dart';
import 'package:InvenTree/widget/drawer.dart';
import 'package:InvenTree/widget/stock_display.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
class LocationDisplayWidget extends StatefulWidget {
LocationDisplayWidget(this.location, {Key key}) : super(key: key);
final InvenTreeStockLocation location;
final String title = "Location";
@override
_LocationDisplayState createState() => _LocationDisplayState(location);
}
class _LocationDisplayState extends State<LocationDisplayWidget> {
_LocationDisplayState(this.location) {
_requestData();
}
final InvenTreeStockLocation location;
List<InvenTreeStockLocation> _sublocations = List<InvenTreeStockLocation>();
String _locationFilter = '';
List<InvenTreeStockLocation> get sublocations {
if (_locationFilter.isEmpty || _sublocations.isEmpty) {
return _sublocations;
} else {
return _sublocations.where((loc) => loc.filter(_locationFilter)).toList();
}
}
List<InvenTreeStockItem> _items = List<InvenTreeStockItem>();
String get _title {
if (location == null) {
return "Location:";
} else {
return "Stock Location '${location.name}'";
}
}
void _requestData() {
int pk = location?.pk ?? -1;
// Request a list of sub-locations under this one
InvenTreeStockLocation().list(filters: {"parent": "$pk"}).then((var locs) {
_sublocations.clear();
for (var loc in locs) {
if (loc is InvenTreeStockLocation) {
_sublocations.add(loc);
}
}
setState(() {});
// Request a list of stock-items under this one
InvenTreeStockItem().list(filters: {"location": "$pk"}).then((var items) {
_items.clear();
for (var item in items) {
if (item is InvenTreeStockItem) {
_items.add(item);
}
}
setState(() {});
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_title),
),
drawer: new InvenTreeDrawer(context),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Sublocations - ${_sublocations.length}",
textAlign: TextAlign.left,
style: TextStyle(fontWeight: FontWeight.bold),
),
TextField(
decoration: InputDecoration(
hintText: "Filter locations",
),
onChanged: (text) {
setState(() {
_locationFilter = text.trim().toLowerCase();
});
},
),
Expanded(child: SublocationList(sublocations)),
Divider(),
Text(
"Stock Items - ${_items.length}",
textAlign: TextAlign.left,
style: TextStyle(fontWeight: FontWeight.bold),
),
Expanded(child: StockList(_items)),
],
)
),
);
}
}
class SublocationList extends StatelessWidget {
final List<InvenTreeStockLocation> _locations;
SublocationList(this._locations);
void _openLocation(BuildContext context, int pk) {
InvenTreeStockLocation().get(pk).then((var loc) {
if (loc is InvenTreeStockLocation) {
Navigator.push(context, MaterialPageRoute(builder: (context) => LocationDisplayWidget(loc)));
}
});
}
Widget _build(BuildContext context, int index) {
InvenTreeStockLocation loc = _locations[index];
return ListTile(
title: Text('${loc.name}'),
subtitle: Text("${loc.description}"),
onTap: () {
_openLocation(context, loc.pk);
},
);
}
@override
Widget build(BuildContext context) {
return ListView.builder(itemBuilder: _build, itemCount: _locations.length);
}
}
class StockList extends StatelessWidget {
final List<InvenTreeStockItem> _items;
StockList(this._items);
void _openItem(BuildContext context, int pk) {
InvenTreeStockItem().get(pk).then((var item) {
if (item is InvenTreeStockItem) {
Navigator.push(context, MaterialPageRoute(builder: (context) => StockItemDisplayWidget(item)));
}
});
}
Widget _build(BuildContext context, int index) {
InvenTreeStockItem item = _items[index];
return ListTile(
title: Text("${item.partName}"),
subtitle: Text("${item.partDescription}"),
leading: Image(
image: InvenTreeAPI().getImage(item.partThumbnail),
width: 48,
),
trailing: Text("${item.displayQuantity}",
style: TextStyle(fontWeight: FontWeight.bold),
),
onTap: () {
_openItem(context, item.pk);
},
);
}
@override
Widget build(BuildContext context) {
return ListView.builder(itemBuilder: _build, itemCount: _items.length);
}
}

View File

@ -0,0 +1,54 @@
import 'package:InvenTree/inventree/part.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:InvenTree/widget/drawer.dart';
class PartDisplayWidget extends StatefulWidget {
PartDisplayWidget(this.part, {Key key}) : super(key: key);
final InvenTreePart part;
@override
_PartDisplayState createState() => _PartDisplayState(part);
}
class _PartDisplayState extends State<PartDisplayWidget> {
_PartDisplayState(this.part) {
// TODO
}
final InvenTreePart part;
String get _title {
if (part == null) {
return "Part";
} else {
return "Part '${part.name}'";
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_title),
),
drawer: new InvenTreeDrawer(context),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Description: ${part.description}"),
]
),
)
);
}
}

View File

@ -0,0 +1,53 @@
import 'package:InvenTree/inventree/stock.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:InvenTree/widget/drawer.dart';
class StockItemDisplayWidget extends StatefulWidget {
StockItemDisplayWidget(this.item, {Key key}) : super(key: key);
final InvenTreeStockItem item;
@override
_StockItemDisplayState createState() => _StockItemDisplayState(item);
}
class _StockItemDisplayState extends State<StockItemDisplayWidget> {
_StockItemDisplayState(this.item) {
// TODO
}
final InvenTreeStockItem item;
String get _title {
if (item == null) {
return "Stock Item";
} else {
return "Item: x ${item.partName}";
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_title),
),
drawer: new InvenTreeDrawer(context),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Stock Item: hello"),
],
)
)
);
}
}

341
pubspec.lock Normal file
View File

@ -0,0 +1,341 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
archive:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.11"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.2"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.2"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.14.11"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_advanced_networkimage:
dependency: "direct main"
description:
name: flutter_advanced_networkimage
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.0"
flutter_launcher_icons:
dependency: "direct dev"
description:
name: flutter_launcher_icons
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.4"
flutter_svg:
dependency: transitive
description:
name: flutter_svg
url: "https://pub.dartlang.org"
source: hosted
version: "0.17.3+1"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
http:
dependency: "direct main"
description:
name: http
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.0+4"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.4"
image:
dependency: transitive
description:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.4"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.6"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.8"
package_info:
dependency: "direct main"
description:
name: package_info
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.0+16"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.4"
path_drawing:
dependency: transitive
description:
name: path_drawing
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.1"
path_parsing:
dependency: transitive
description:
name: path_parsing
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
path_provider:
dependency: transitive
description:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.5"
path_provider_macos:
dependency: transitive
description:
name: path_provider_macos
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.4"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0+1"
petitparser:
dependency: transitive
description:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.0"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.1"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
preferences:
dependency: "direct main"
description:
name: preferences
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.0"
qr_utils:
dependency: "direct main"
description:
name: qr_utils
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
quiver:
dependency: transitive
description:
name: quiver
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.6+3"
shared_preferences_macos:
dependency: transitive
description:
name: shared_preferences_macos
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+6"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2+4"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.5"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.9.3"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.11"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.6"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
xml:
dependency: transitive
description:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "3.5.0"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
sdks:
dart: ">=2.4.0 <3.0.0"
flutter: ">=1.12.13+hotfix.4 <2.0.0"

90
pubspec.yaml Normal file
View File

@ -0,0 +1,90 @@
name: InvenTree
description: InvenTree stock management
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1
environment:
sdk: ">=2.1.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
http: ^0.12.0+2
shared_preferences: ^0.5.3+1
flutter_advanced_networkimage: any
preferences: ^5.1.0
qr_utils: ^0.1.4
package_info: ^0.4.0+16
dev_dependencies:
flutter_test:
sdk: flutter
flutter_launcher_icons:
flutter_icons:
android: true
ios: true
image_path: "assets/image/icon.png"
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
assets:
- assets/image/icon.png
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages

View File

30
test/widget_test.dart Normal file
View File

@ -0,0 +1,30 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:inventree_app/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}