Initial commit

This commit is contained in:
Igor Kulikov
2021-04-23 19:35:13 +03:00
parent 422cfb3b0b
commit 2212d9db7c
81 changed files with 2873 additions and 26 deletions

46
.gitignore vendored Normal file
View File

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

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: adc687823a831bbebe28bdccfac1a628ca621513
channel: stable
project_type: app

48
LICENSE
View File

@@ -1,29 +1,25 @@
BSD 3-Clause License Copyright 2021 The ThingsBoard Authors
Copyright (c) 2021, ThingsBoard - Open-source IoT Platform Redistribution and use in source and binary forms, with or without modification,
All rights reserved. are permitted provided that the following conditions are met:
Redistribution and use in source and binary forms, with or without * Redistributions of source code must retain the above copyright
modification, are permitted provided that the following conditions are met: notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
1. Redistributions of source code must retain the above copyright notice, this THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
list of conditions and the following disclaimer. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2. Redistributions in binary form must reproduce the above copyright notice, DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
this list of conditions and the following disclaimer in the documentation ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
and/or other materials provided with the distribution. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
3. Neither the name of the copyright holder nor the names of its ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
contributors may be used to endorse or promote products derived from (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
this software without specific prior written permission. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

14
README.md Normal file
View File

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

11
android/.gitignore vendored Normal file
View File

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

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

@@ -0,0 +1,59 @@
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 30
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "org.thingsboard.app"
minSdkVersion 18
targetSdkVersion 30
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

View File

@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.thingsboard.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,41 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.thingsboard.app">
<application
android:label="ThingsBoard App"
android:icon="@mipmap/launcher_icon">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame. -->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>

View File

@@ -0,0 +1,6 @@
package org.thingsboard.app
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

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

View File

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

View File

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

31
android/build.gradle Normal file
View File

@@ -0,0 +1,31 @@
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
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,3 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=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-6.7-all.zip

11
android/settings.gradle Normal file
View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" height="320" width="1543.4" version="1.1" viewBox="0 0 1543.4268 320.00026">
<g transform="translate(0 -732.36)">
<g stroke-width="28">
<g transform="matrix(-.66287 .69913 -.66371 -.70001 863.46 1410.6)">
<g transform="translate(2.5254 3.0305)"></g>
<g transform="translate(41.25 -30.543)" stroke-width="28"></g>
</g>
<g transform="matrix(.57657 .52719 -.57729 .52786 584.63 346.6)"></g>
<g transform="matrix(.66287 -.69913 .66371 .70001 -543.46 380.47)">
<g transform="translate(2.5254 3.0305)"></g>
<g transform="translate(41.25 -30.543)" stroke-width="28"></g>
</g>
</g>
<g transform="translate(0 732.36)" fill="#305680">
<g transform="translate(-.000061238 -732.36)" fill="#305680">
<path style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m151.13 732.36c-28.363 0-54.915 7.9153-77.613 21.537-6.4723-5.2623-14.605-8.1915-23.067-8.1937a8.7661 8.7661 0 0 0 -0.0035 0c-20.154 0.00073-36.679 16.528-36.678 36.682a8.7661 8.7661 0 0 0 0 0.0106c0.0099 8.4147 2.9267 16.483 8.1033 22.927-13.83 22.83-21.87 49.58-21.87 78.17a8.7661 8.7661 0 1 0 17.531 0c0-24.702 6.7193-47.748 18.378-67.574 4.5663 1.9846 9.4727 3.1496 14.519 3.1573a8.7661 8.7661 0 0 0 0.01241 0c20.155 0.00062 36.683-16.527 36.682-36.682a8.7661 8.7661 0 0 0 0 -0.004c-0.0016-4.9994-1.1387-9.8628-3.0828-14.397 19.717-11.484 42.585-18.095 67.085-18.095a8.7661 8.7661 0 1 0 0 -17.531zm-100.69 30.88c5.9129 0.002 11.191 2.5121 14.836 7.0769a8.7661 8.7661 0 0 0 0.1826 0.21451c2.6715 3.3798 4.1326 7.5531 4.1341 11.863-0.0015 10.677-8.468 19.144-19.144 19.148-4.37-0.008-8.6011-1.5088-12-4.2546a8.7661 8.7661 0 0 0 -0.01241 -0.009c-4.514-3.6331-7.1358-9.0979-7.1442-14.893 0.0025-10.677 8.4701-19.144 19.148-19.146z"/>
<path style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m66.992 835.19c-1.492 1.5583-2.3663 3.7103-2.2576 6.0713 0.0962 2.0447 0.91322 4.0204 2.3372 5.5177 6.8051 6.8559 20.223 20.223 20.223 20.223l11.844-11.83s-12.973-12.961-20.176-20.171c-1.604-1.6325-3.7498-2.3141-6.012-2.3243-2.3583-0.0112-4.4673 0.95389-5.9592 2.5122zm32.147 19.983-36.639 36.639c-3.9751 3.976-3.9751 10.421 0 14.397l18.156 18.156 31.753 31.753 30.478 30.478c3.9758 3.9759 10.422 3.9763 14.398 0l24.791-24.791 37.914-37.914 36.639-36.639c3.9754-3.9764 3.975-10.422-0.00067-14.398l-18.63-18.63-31.75-31.76-30.01-30c-3.976-3.9751-10.421-3.9751-14.397 0l-24.79 24.79-37.91 37.91zm37.911-37.91s-12.973-12.961-20.176-20.171c-1.604-1.6325-3.7498-2.3141-6.012-2.3243-4.7166-0.0226-8.4341 3.8616-8.2169 8.5834 0.0962 2.0447 0.91322 4.0204 2.3372 5.5177 6.8051 6.8559 20.223 20.223 20.223 20.223l11.844-11.83zm69.193 5.2132s12.961-12.973 20.171-20.176c1.6325-1.604 2.3141-3.7498 2.3243-6.012 0.0225-4.7166-3.8616-8.4341-8.5834-8.2169-2.0447 0.0962-4.0204 0.91322-5.5177 2.3372-6.8559 6.8051-20.223 20.223-20.223 20.223l11.83 11.844zm31.753 31.753s12.961-12.973 20.171-20.176c1.6325-1.604 2.314-3.7498 2.3243-6.012 0.0225-4.7166-3.8616-8.4341-8.5834-8.2169-2.0447 0.0962-4.0204 0.91323-5.5177 2.3372-6.8559 6.8051-20.223 20.223-20.223 20.223l11.83 11.844zm-18.009 69.667s12.973 12.961 20.178 20.169c1.604 1.6324 3.7498 2.314 6.012 2.3243 4.7166 0.0225 8.4342-3.8615 8.2169-8.5834l-0.002 0.002c-0.0962-2.0447-0.91415-4.0214-2.3382-5.5186-6.8051-6.8559-20.222-20.222-20.222-20.222l-11.844 11.83zm-37.914 37.914s12.973 12.961 20.178 20.169c1.604 1.6324 3.7498 2.314 6.012 2.3242 4.7166 0.023 8.4342-3.8615 8.2169-8.5834h-0.002c-0.0962-2.0447-0.91323-4.0205-2.3372-5.5177-6.8051-6.8559-20.223-20.223-20.223-20.223l-11.844 11.83zm-69.667-5.6871s-12.961 12.973-20.169 20.178c-1.6324 1.604-2.314 3.7498-2.3243 6.012-0.02251 4.7166 3.8615 8.4342 8.5834 8.2169h-0.0019c2.0447-0.096 4.0204-0.9132 5.5177-2.3373 6.8561-6.8048 20.223-20.223 20.223-20.223l-11.82-11.84zm-31.743-31.74s-12.961 12.973-20.169 20.178c-1.6324 1.604-2.314 3.7498-2.3243 6.012-0.02251 4.7167 3.8615 8.4342 8.5834 8.2169h-0.0019c2.0447-0.0962 4.0204-0.91322 5.5177-2.3372 6.8561-6.8047 20.223-20.223 20.223-20.223l-11.829-11.85zm87.237-90.554c1.6794-1.7064 3.9669-2.6599 6.2971-2.626 1.6628 0.0237 3.253 0.55012 4.5625 1.5097l16.499 12.1c3.2009 2.297 4.1445 6.6589 2.2308 10.312-1.9137 3.6528-6.1234 5.5243-9.9506 4.4227l6.124 23.948c1.1128 4.3517-1.564 8.9677-5.9833 10.317l-44.642 13.631 8.2456 31.883c1.1728 4.3696-1.5017 9.0445-5.9546 10.407s-8.9756-1.1106-10.068-5.5047l-10.282-39.769c-1.1265-4.3556 1.5493-8.9847 5.9759-10.338l44.661-13.637-4.1219-16.118c-2.7634 3.0643-7.2335 3.8084-10.586 1.7615-3.3531-2.0469-4.6144-6.2896-2.9861-10.047l8.1169-19.454c0.43322-1.0376 1.0673-1.9888 1.8624-2.7973z" fill-rule="evenodd"/>
<path style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m168.87 1052.4c28.363 0 54.915-7.9155 77.614-21.538 6.4724 5.2624 14.605 8.1917 23.067 8.1939a8.7662 8.7662 0 0 0 0.004 0c20.155-0.0007 36.68-16.528 36.679-36.682a8.7662 8.7662 0 0 0 0 -0.011c-0.01-8.4149-2.9267-16.484-8.1034-22.927 13.825-22.82 21.866-49.572 21.866-78.162a8.7662 8.7662 0 1 0 -17.531 0c0 24.703-6.7194 47.749-18.378 67.575-4.5664-1.9846-9.4728-3.1496-14.519-3.1573a8.7662 8.7662 0 0 0 -0.0124 0c-20.155-0.00062-36.683 16.527-36.682 36.682 0.002 4.9994 1.1387 9.8628 3.0829 14.397-19.717 11.484-42.586 18.095-67.086 18.095a8.7662 8.7662 0 1 0 0 17.531zm100.69-30.875c-5.913 0-11.191-2.5122-14.836-7.0769a8.7662 8.7662 0 0 0 -0.18261 -0.2146c-2.6715-3.3799-4.1327-7.5531-4.1341-11.863 0.002-10.677 8.4681-19.144 19.144-19.148 4.37 0.008 8.6012 1.5088 12 4.2547a8.7662 8.7662 0 0 0 0.0124 0.009c4.5141 3.6332 7.1359 9.098 7.1443 14.893-0.003 10.677-8.4702 19.145-19.148 19.146z"/>
</g>
<path d="m477.92 93.229h-48.56v134.68h-19.818v-134.68h-48.456v-16.394h116.83v16.394z"/>
<path d="m516.72 129.23q12.762-15.668 33.203-15.668 35.59 0 35.901 40.155v74.188h-19.196v-74.292q-0.10376-12.14-5.603-17.95-5.3955-5.8105-16.913-5.8105-9.3384 0-16.394 4.9805-7.0557 4.9805-10.999 13.074v79.999h-19.196v-159.38h19.196v60.699z"/>
<path d="m635.43 227.91h-19.196v-112.27h19.196v112.27zm-20.76-142.05q0-4.6692 2.8015-7.8857 2.9053-3.2166 8.5083-3.2166t8.5083 3.2166 2.9053 7.8857q0 4.6692-2.9053 7.782t-8.5083 3.1128-8.5083-3.1128q-2.8015-3.1128-2.8015-7.782z"/>
<path d="m684.19 115.64 0.62256 14.111q12.866-16.187 33.618-16.187 35.59 0 35.901 40.155v74.188h-19.196v-74.292q-0.10376-12.14-5.603-17.95-5.3955-5.8105-16.913-5.8105-9.3384 0-16.394 4.9805-7.0557 4.9805-10.999 13.074v79.999h-19.196v-112.27h18.158z"/>
<path d="m778.92 170.84q0-26.251 12.14-41.711 12.14-15.564 32.166-15.564 20.544 0 32.062 14.526l0.93384-12.451h17.535v109.57q0 21.79-12.97 34.344-12.866 12.555-34.656 12.555-12.14 0-23.761-5.188t-17.743-14.215l9.9609-11.517q12.347 15.253 30.194 15.253 14.008 0 21.79-7.8857 7.8857-7.8858 7.8857-22.205v-9.6497q-11.517 13.281-31.439 13.281-19.714 0-31.958-15.875-12.14-15.875-12.14-43.268zm19.299 2.179q0 18.988 7.782 29.883 7.782 10.791 21.79 10.791 18.158 0 26.666-16.498v-51.257q-8.8196-16.083-26.459-16.083-14.008 0-21.893 10.895-7.8858 10.895-7.8858 32.269z"/>
<path d="m967.98 198.13q0-7.782-5.9143-12.036-5.8106-4.3579-20.441-7.4707-14.526-3.1128-23.138-7.4707-8.5083-4.3579-12.659-10.376-4.0466-6.0181-4.0466-14.319 0-13.8 11.621-23.346 11.725-9.5459 29.883-9.5459 19.092 0 30.92 9.8572 11.932 9.8572 11.932 25.214h-19.299q0-7.8858-6.7444-13.593-6.6406-5.7068-16.809-5.7068-10.48 0-16.394 4.5654-5.9143 4.5654-5.9143 11.932 0 6.9519 5.4993 10.48 5.4993 3.5278 19.818 6.7444 14.423 3.2166 23.346 7.6782 8.9233 4.4617 13.177 10.791 4.3579 6.2256 4.3579 15.253 0 15.045-12.036 24.176-12.036 9.0271-31.232 9.0271-13.489 0-23.865-4.773t-16.29-13.281q-5.8105-8.6121-5.8105-18.573h19.196q0.5188 9.6497 7.6782 15.356 7.2632 5.603 19.092 5.603 10.895 0 17.432-4.3579 6.6406-4.4617 6.6406-11.829z"/>
<path d="m1015.2 227.91v-151.07h49.39q24.591 0 36.938 10.168 12.451 10.168 12.451 30.09 0 10.583-6.0181 18.781-6.018 8.0933-16.394 12.555 12.244 3.4241 19.299 13.074 7.1594 9.5459 7.1594 22.827 0 20.337-13.178 31.958t-37.25 11.621h-52.399zm19.922-70.66v54.37h32.892q13.904 0 21.893-7.1594 8.0933-7.2632 8.0933-19.922 0-27.289-29.675-27.289h-33.203zm0-15.979h30.09q13.074 0 20.856-6.5369 7.8858-6.5369 7.8858-17.743 0-12.451-7.2632-18.054-7.2632-5.7068-22.101-5.7068h-29.468v48.041z"/>
<path d="m1139.5 170.74q0-16.498 6.4331-29.675 6.5369-13.177 18.054-20.337 11.621-7.1594 26.459-7.1594 22.931 0 37.042 15.875 14.215 15.875 14.215 42.23v1.3489q0 16.394-6.3294 29.468-6.2256 12.97-17.95 20.233-11.621 7.2632-26.77 7.2632-22.827 0-37.042-15.875-14.1-15.88-14.1-42.02v-1.3489zm19.299 2.2827q0 18.677 8.6121 29.987 8.7158 11.31 23.242 11.31 14.63 0 23.242-11.414 8.6121-11.517 8.6121-32.166 0-18.469-8.8196-29.883-8.7158-11.517-23.242-11.517-14.215 0-22.931 11.31-8.7158 11.31-8.7158 32.373z"/>
<path d="m1335.1 227.91q-1.6602-3.3203-2.6978-11.829-13.385 13.904-31.958 13.904-16.602 0-27.289-9.3384-10.584-9.4421-10.584-23.865 0-17.535 13.281-27.185 13.385-9.7534 37.561-9.7534h18.677v-8.8196q0-10.065-6.018-15.979-6.0181-6.0181-17.743-6.0181-10.272 0-17.224 5.188-6.9519 5.188-6.9519 12.555h-19.299q0-8.4045 5.9143-16.187 6.0181-7.8858 16.186-12.451 10.272-4.5654 22.516-4.5654 19.403 0 30.402 9.7534 10.998 9.6497 11.414 26.666v51.672q0 15.46 3.9429 24.591v1.6602h-20.129zm-31.854-14.63q9.0271 0 17.12-4.6692 8.0932-4.6692 11.725-12.14v-23.035h-15.045q-35.278 0-35.278 20.648 0 9.0271 6.0181 14.111 6.0181 5.0842 15.46 5.0842z"/>
<path d="m1435.8 132.87q-4.3579-0.72632-9.4422-0.72632-18.884 0-25.629 16.083v79.688h-19.196v-112.27h18.677l0.3113 12.97q9.4421-15.045 26.77-15.045 5.603 0 8.5083 1.4526v17.847z"/>
<path d="m1448.7 170.84q0-25.836 12.244-41.504 12.244-15.771 32.062-15.771 19.714 0 31.232 13.489v-58.521h19.196v159.38h-17.639l-0.9339-12.036q-11.517 14.111-32.062 14.111-19.507 0-31.854-15.979-12.244-15.979-12.244-41.711v-1.4526zm19.196 2.179q0 19.092 7.8858 29.883 7.8857 10.791 21.79 10.791 18.262 0 26.666-16.394v-51.569q-8.6121-15.875-26.459-15.875-14.111 0-21.997 10.895-7.8858 10.895-7.8858 32.269z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

32
ios/.gitignore vendored Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<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"
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>

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 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>$(DEVELOPMENT_LANGUAGE)</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>ThingsBoard 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>

View File

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

View File

@@ -0,0 +1,44 @@
import 'package:fluro/fluro.dart';
import 'package:flutter/widgets.dart';
import 'package:thingsboard_app/core/auth/login/login_page.dart';
import 'package:thingsboard_app/core/context/tb_context.dart';
import 'package:thingsboard_app/core/init/init_app.dart';
import 'package:thingsboard_app/modules/device/devices_page.dart';
import 'package:thingsboard_app/modules/home/home_page.dart';
import 'package:thingsboard_app/modules/profile/profile_page.dart';
class ThingsboardAppRouter {
final router = FluroRouter();
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
late final _tbContext = TbContext(router, routeObserver);
late var initHandler = Handler(handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
return ThingsboardInitApp(tbContext);
});
late var loginHandler = Handler(handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
return LoginPage(_tbContext);
});
late var homeHandler = Handler(handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
return HomePage(_tbContext);
});
late var profileHandler = Handler(handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
return ProfilePage(_tbContext);
});
late var devicesHandler = Handler(handlerFunc: (BuildContext? context, Map<String, dynamic> params) {
return DevicesPage(_tbContext);
});
ThingsboardAppRouter() {
router.define("/", handler: initHandler);
router.define("/login", handler: loginHandler);
router.define("/home", handler: homeHandler);
router.define("/profile", handler: profileHandler);
router.define("/devices", handler: devicesHandler);
}
TbContext get tbContext => _tbContext;
}

View File

@@ -0,0 +1,46 @@
import 'package:flutter/material.dart';
const int _tbPrimaryColor = 0xFF305680;
const int _tbSecondaryColor = 0xFF527dad;
const int _tbDarkPrimaryColor = 0xFF9fa8da;
const tbMatIndigo = MaterialColor(
_tbPrimaryColor,
<int, Color>{
50: Color(0xFFE8EAF6),
100: Color(0xFFC5CAE9),
200: Color(0xFF9FA8DA),
300: Color(0xFF7986CB),
400: Color(0xFF5C6BC0),
500: Color(_tbPrimaryColor),
600: Color(_tbSecondaryColor),
700: Color(0xFF303F9F),
800: Color(0xFF283593),
900: Color(0xFF1A237E),
},);
const tbDarkMatIndigo = MaterialColor(
_tbPrimaryColor,
<int, Color>{
50: Color(0xFFE8EAF6),
100: Color(0xFFC5CAE9),
200: Color(0xFF9FA8DA),
300: Color(0xFF7986CB),
400: Color(0xFF5C6BC0),
500: Color(_tbDarkPrimaryColor),
600: Color(_tbSecondaryColor),
700: Color(0xFF303F9F),
800: Color(_tbPrimaryColor),
900: Color(0xFF1A237E),
},);
ThemeData tbTheme = ThemeData(
primarySwatch: tbMatIndigo,
accentColor: Colors.deepOrange
);
ThemeData tbDarkTheme = ThemeData(
primarySwatch: tbDarkMatIndigo,
accentColor: Colors.deepOrange,
brightness: Brightness.dark
);

View File

@@ -0,0 +1,3 @@
const thingsBoardApiEndpoint = 'https://demo.thingsboard.io';
const username = 'ikulikov82@gmail.com';
const password = 'qwerty';

View File

View File

@@ -0,0 +1,3 @@
abstract class ThingsboardImage {
static final thingsBoardLogoBlue = 'assets/images/thingsboard_logo_blue.svg';
}

View File

@@ -0,0 +1,147 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:thingsboard_app/constants/assets_path.dart';
import 'package:thingsboard_client/thingsboard_client.dart';
import 'package:thingsboard_app/core/context/tb_context.dart';
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
class LoginPage extends TbPageWidget<LoginPage, _LoginPageState> {
LoginPage(TbContext tbContext) : super(tbContext);
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends TbPageState<LoginPage, _LoginPageState> {
final usernameController = TextEditingController();
final passwordController = TextEditingController();
@override
void initState() {
super.initState();
}
@override
void dispose() {
usernameController.dispose();
passwordController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Login to ThingsBoard'),
),
body: ValueListenableBuilder(
valueListenable: loadingNotifier,
builder: (BuildContext context, bool loading, child) {
List<Widget> children = [
SingleChildScrollView(
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 60.0),
child: Center(
child: Container(
width: 300,
height: 150,
child: SvgPicture.asset(ThingsboardImage.thingsBoardLogoBlue,
semanticsLabel: 'ThingsBoard Logo')
)
)
),
Padding(
//padding: const EdgeInsets.only(left:15.0,right: 15.0,top:0,bottom: 0),
padding: EdgeInsets.symmetric(horizontal: 15),
child: TextField(
enabled: !loading,
controller: usernameController,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Username (email)',
hintText: 'Enter valid email id as abc@gmail.com'),
),
),
Padding(
padding: const EdgeInsets.only(
left: 15.0, right: 15.0, top: 15, bottom: 0),
//padding: EdgeInsets.symmetric(horizontal: 15),
child: TextField(
enabled: !loading,
controller: passwordController,
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
hintText: 'Enter secure password'),
),
),
TextButton(
onPressed: loading ? null : () {
//TODO FORGOT PASSWORD SCREEN GOES HERE
},
child: Text(
'Forgot Password?',
style: TextStyle(color: loading ? Colors.black12 : Theme.of(context).colorScheme.primary, fontSize: 15),
),
),
Container(
height: 50,
width: 250,
decoration: BoxDecoration(
color: loading ? Colors.black12 : Theme.of(context).colorScheme.primary, borderRadius: BorderRadius.circular(4)),
child: TextButton(
onPressed: loading ? null : () {
tbContext.tbClient.login(
LoginRequest(usernameController.text, passwordController.text));
},
child: Text(
'Login',
style: TextStyle(color: Colors.white, fontSize: 25),
),
),
),
SizedBox(
height: 130,
),
Text('New User? Create Account')
]
)
)
];
if (loading) {
children.add(
SizedBox.expand(
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
child: Container(
decoration: new BoxDecoration(
color: Colors.grey.shade200.withOpacity(0.2)
),
child: Center(
child: CircularProgressIndicator(),
),
)
)
)
)
);
//children.add(Center(child: CircularProgressIndicator()));
}
return Stack(
children: children,
);
})
);
}
}

View File

@@ -0,0 +1,202 @@
import 'dart:async';
import 'package:fluro/fluro.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:thingsboard_client/thingsboard_client.dart';
import 'package:thingsboard_app/utils/services/tb_secure_storage.dart';
import 'package:thingsboard_app/constants/api_path.dart';
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
enum NotificationType {
info,
warn,
success,
error
}
class TbContext {
bool _initialized = false;
bool isUserLoaded = false;
bool isAuthenticated = false;
final _isLoadingNotifier = ValueNotifier<bool>(false);
GlobalKey<ScaffoldMessengerState> messengerKey = GlobalKey<ScaffoldMessengerState>();
late ThingsboardClient tbClient;
final FluroRouter router;
final RouteObserver<PageRoute> routeObserver;
TbContextState? currentState;
TbContext(this.router, this.routeObserver);
void init() {
assert(() {
if (_initialized) {
throw StateError('TbContext already initialized!');
}
return true;
}());
_initialized = true;
tbClient = ThingsboardClient(thingsBoardApiEndpoint,
storage: TbSecureStorage(),
onUserLoaded: onUserLoaded,
onError: onError,
onLoadStarted: onLoadStarted,
onLoadFinished: onLoadFinished,
computeFunc: <Q, R>(callback, message) => compute(callback, message));
tbClient.init().onError((error, stackTrace) {
print('Error: $error');
print('Stack: $stackTrace');
});
}
void onError(ThingsboardError error) {
print('onError: error=$error');
showErrorNotification(error.message!);
}
void showErrorNotification(String message, {Duration? duration}) {
showNotification(message, NotificationType.error, duration: duration);
}
void showInfoNotification(String message, {Duration? duration}) {
showNotification(message, NotificationType.info, duration: duration);
}
void showWarnNotification(String message, {Duration? duration}) {
showNotification(message, NotificationType.warn, duration: duration);
}
void showSuccessNotification(String message, {Duration? duration}) {
showNotification(message, NotificationType.success, duration: duration);
}
void showNotification(String message, NotificationType type, {Duration? duration}) {
duration ??= const Duration(days: 1);
Color backgroundColor;
var textColor = Color(0xFFFFFFFF);
switch(type) {
case NotificationType.info:
backgroundColor = Color(0xFF323232);
break;
case NotificationType.warn:
backgroundColor = Color(0xFFdc6d1b);
break;
case NotificationType.success:
backgroundColor = Color(0xFF008000);
break;
case NotificationType.error:
backgroundColor = Color(0xFF800000);
break;
}
final snackBar = SnackBar(
duration: duration,
backgroundColor: backgroundColor,
content: Text(message,
style: TextStyle(
color: textColor
),
),
action: SnackBarAction(
label: 'Close',
textColor: textColor,
onPressed: () {
messengerKey.currentState!.hideCurrentSnackBar(reason: SnackBarClosedReason.dismiss);
},
),
);
messengerKey.currentState!.removeCurrentSnackBar();
messengerKey.currentState!.showSnackBar(snackBar);
}
void hideNotification() {
messengerKey.currentState!.removeCurrentSnackBar();
}
void onLoadStarted() {
print('ON LOAD STARTED!');
_isLoadingNotifier.value = true;
}
void onLoadFinished() {
print('ON LOAD FINISHED!');
_isLoadingNotifier.value = false;
}
Future<void> onUserLoaded() async {
try {
print('onUserLoaded: isAuthenticated=${tbClient.isAuthenticated()}');
isUserLoaded = true;
isAuthenticated = tbClient.isAuthenticated();
if (tbClient.isAuthenticated()) {
print('authUser: ${tbClient.getAuthUser()}');
}
updateRouteState();
} catch (e, s) {
print('Error: $e');
print('Stack: $s');
}
}
void updateRouteState() {
if (currentState != null) {
if (tbClient.isAuthenticated()) {
navigateTo('/home', replace: true);
} else {
navigateTo('/login', replace: true, clearStack: true, transition: TransitionType.inFromTop);
}
}
}
void navigateTo(String path, {bool replace = false, bool clearStack = false, TransitionType? transition}) {
if (currentState != null) {
if (transition == null) {
transition = TransitionType.inFromRight;
}
hideNotification();
router.navigateTo(currentState!.context, path, transition: transition, replace: replace, clearStack: clearStack);
}
}
void pop() {
if (currentState != null) {
router.pop(currentState!.context);
}
}
}
mixin HasTbContext {
late final TbContext _tbContext;
void setTbContext(TbContext tbContext) {
_tbContext = tbContext;
}
void setupTbContext(TbContextState currentState) {
_tbContext = currentState.widget.tbContext;
}
void setupCurrentState(TbContextState currentState) {
_tbContext.currentState = currentState;
}
ValueNotifier<bool> get loadingNotifier => _tbContext._isLoadingNotifier;
TbContext get tbContext => _tbContext;
void navigateTo(String path, {bool replace = false}) => _tbContext.navigateTo(path, replace: replace);
void pop() => _tbContext.pop();
void hideNotification() => _tbContext.hideNotification();
void showErrorNotification(String message, {Duration? duration}) => _tbContext.showErrorNotification(message, duration: duration);
void showInfoNotification(String message, {Duration? duration}) => _tbContext.showInfoNotification(message, duration: duration);
void showWarnNotification(String message, {Duration? duration}) => _tbContext.showWarnNotification(message, duration: duration);
void showSuccessNotification(String message, {Duration? duration}) => _tbContext.showSuccessNotification(message, duration: duration);
}

View File

@@ -0,0 +1,68 @@
import 'package:flutter/widgets.dart';
import 'package:thingsboard_app/core/context/tb_context.dart';
abstract class TbContextStatelessWidget extends StatelessWidget with HasTbContext {
TbContextStatelessWidget(TbContext tbContext, {Key? key}) : super(key: key) {
setTbContext(tbContext);
}
}
abstract class TbContextWidget<W extends TbContextWidget<W,S>, S extends TbContextState<W,S>> extends StatefulWidget with HasTbContext {
TbContextWidget(TbContext tbContext, {Key? key}) : super(key: key) {
setTbContext(tbContext);
}
}
abstract class TbContextState<W extends TbContextWidget<W,S>, S extends TbContextState<W,S>> extends State<W> with HasTbContext {
final bool handleLoading;
TbContextState({this.handleLoading = false});
@override
void initState() {
super.initState();
setupTbContext(this);
}
@override
void dispose() {
super.dispose();
}
void updateState() {
setState(() {});
}
}
abstract class TbPageWidget<W extends TbPageWidget<W,S>, S extends TbPageState<W,S>> extends TbContextWidget<W,S> {
TbPageWidget(TbContext tbContext, {Key? key}) : super(tbContext, key: key);
}
abstract class TbPageState<W extends TbPageWidget<W,S>, S extends TbPageState<W,S>> extends TbContextState<W,S> with RouteAware {
TbPageState({bool handleUserLoaded = false}): super(handleLoading: true);
@override
void didChangeDependencies() {
super.didChangeDependencies();
tbContext.routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute);
}
@override
void dispose() {
tbContext.routeObserver.unsubscribe(this);
super.dispose();
}
@override
void didPush() {
setupCurrentState(this);
}
@override
void didPopNext() {
tbContext.hideNotification();
setupCurrentState(this);
}
}

View File

@@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:thingsboard_app/core/context/tb_context.dart';
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
class ThingsboardInitApp extends TbPageWidget<ThingsboardInitApp, _ThingsboardInitAppState> {
ThingsboardInitApp(TbContext tbContext, {Key? key}) : super(tbContext, key: key);
@override
_ThingsboardInitAppState createState() => _ThingsboardInitAppState();
}
class _ThingsboardInitAppState extends TbPageState<ThingsboardInitApp, _ThingsboardInitAppState> {
@override
void initState() {
super.initState();
tbContext.init();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('ThingsBoard Init'),
),
body: Center(
child: CircularProgressIndicator()
)
);
}
}

38
lib/main.dart Normal file
View File

@@ -0,0 +1,38 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:thingsboard_app/config/routes/router.dart';
import 'config/themes/tb_theme.dart';
final appRouter = ThingsboardAppRouter();
void main() {
runApp(ThingsboardApp());
}
class ThingsboardApp extends StatefulWidget {
ThingsboardApp({Key? key}) : super(key: key);
@override
ThingsboardAppState createState() => ThingsboardAppState();
}
class ThingsboardAppState extends State<ThingsboardApp> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
scaffoldMessengerKey: appRouter.tbContext.messengerKey,
title: 'ThingsBoard',
theme: tbTheme,
darkTheme: tbDarkTheme,
onGenerateRoute: appRouter.router.generator,
navigatorObservers: [appRouter.routeObserver],
);
}
}

View File

@@ -0,0 +1,328 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:thingsboard_app/widgets/tb_app_bar.dart';
import 'package:thingsboard_client/thingsboard_client.dart';
import 'package:thingsboard_app/core/context/tb_context.dart';
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
class DeviceInfoCard extends StatelessWidget {
final DeviceInfo device;
final void Function(DeviceInfo device)? onDetails;
DeviceInfoCard(this.device, {this.onDetails});
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
child: ListTile(
title: Text('${device.name}'),
subtitle: Text('${device.type}'),
trailing: IconButton(
icon: Icon(Icons.navigate_next),
onPressed: () {
if (onDetails != null) {
onDetails!(device);
}
},
),
)
)
);
}
}
class FirstPageExceptionIndicator extends StatelessWidget {
const FirstPageExceptionIndicator({
required this.title,
this.message,
this.onTryAgain,
Key? key,
}) : super(key: key);
final String title;
final String? message;
final VoidCallback? onTryAgain;
@override
Widget build(BuildContext context) {
final message = this.message;
return Center(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 16),
child: Column(
children: [
Text(
title,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headline6,
),
if (message != null)
const SizedBox(
height: 16,
),
if (message != null)
Text(
message,
textAlign: TextAlign.center,
),
if (onTryAgain != null)
const SizedBox(
height: 48,
),
if (onTryAgain != null)
SizedBox(
height: 50,
width: double.infinity,
child: ElevatedButton.icon(
onPressed: onTryAgain,
icon: const Icon(
Icons.refresh,
color: Colors.white,
),
label: const Text(
'Try Again',
style: TextStyle(
fontSize: 16,
color: Colors.white,
),
),
),
),
],
),
),
);
}
}
class DevicesPage extends TbPageWidget<DevicesPage, _DevicesPageState> {
DevicesPage(TbContext tbContext) : super(tbContext);
@override
_DevicesPageState createState() => _DevicesPageState();
}
class _DevicesPageState extends TbPageState<DevicesPage, _DevicesPageState> {
final _searchModeNotifier = ValueNotifier<bool>(false);
final PagingController<PageLink, DeviceInfo> _pagingController = PagingController(firstPageKey: PageLink(10));
@override
void initState() {
super.initState();
_pagingController.addPageRequestListener((pageKey) {
_fetchPage(pageKey);
});
}
@override
void dispose() {
_pagingController.dispose();
super.dispose();
}
bool dataLoading = false;
bool scheduleRefresh = false;
void refresh() {
if (dataLoading) {
scheduleRefresh = true;
} else {
_pagingController.refresh();
}
}
Future<void> _fetchPage(PageLink pageKey) async {
dataLoading = true;
try {
hideNotification();
final pageData = await tbContext.tbClient.getDeviceService().getTenantDeviceInfos(pageKey);
final isLastPage = !pageData.hasNext;
if (isLastPage) {
_pagingController.appendLastPage(pageData.data);
} else {
final nextPageKey = pageKey.nextPageLink();
_pagingController.appendPage(pageData.data, nextPageKey);
}
} catch (error) {
_pagingController.error = error;
} finally {
dataLoading = false;
if (scheduleRefresh) {
scheduleRefresh = false;
_pagingController.refresh();
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: TbAppBar(
tbContext,
title: const Text('Devices'),
searchModeNotifier: _searchModeNotifier,
searchHint: 'Search devices',
onSearch: (String searchText) {
_pagingController.firstPageKey.textSearch = searchText;
refresh();
},
),
body: Builder(
builder: (BuildContext context) {
return PagedListView(
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<DeviceInfo>(
itemBuilder: (context, item, index) {
return DeviceInfoCard(
item,
onDetails: (device) {
print('open details: $device');
},
);
},
noMoreItemsIndicatorBuilder: (context) => FirstPageExceptionIndicator(
title: 'No more devices'
),
noItemsFoundIndicatorBuilder: (context) => FirstPageExceptionIndicator(
title: 'No devices found',
message: 'The list is currently empty.',
onTryAgain: () => refresh(),
)
),
);
}
),
bottomNavigationBar: BottomAppBar(
shape: CircularNotchedRectangle(),
notchMargin: 4.0,
child: new Row(
children: <Widget>[
IconButton(icon: Icon(Icons.refresh), onPressed: () {
refresh();
},),
Spacer(),
IconButton(icon: Icon(Icons.search), onPressed: () {
_searchModeNotifier.value = true;
}),
_simplePopup(),
],
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add), onPressed: () {},),
/* SpeedDial(
animatedIcon: AnimatedIcons.menu_close,
animatedIconTheme: IconThemeData(size: 22),
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Colors.white,
visible: true,
curve: Curves.bounceIn,
children: [
// FAB 1
SpeedDialChild(
child: Icon(Icons.refresh),
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Colors.white,
onTap: () {
refresh();
/* setState(() {
var rng = Random();
var pageSize = 1 + rng.nextInt(9);
futureDevices = tbContext.tbClient.getDeviceService().getTenantDeviceInfos(PageLink(pageSize));
}); */
},
label: 'Refresh',
labelStyle: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16.0),
),
// FAB 2
SpeedDialChild(
child: Icon(Icons.logout),
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Colors.white,
onTap: () {
tbContext.tbClient.logout(requestConfig: RequestConfig(ignoreErrors: true));
},
label: 'Logout',
labelStyle: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16.0),
)
],
)*/
);
}
Widget _simplePopup() => PopupMenuButton<int>(
itemBuilder: (context) => [
PopupMenuItem(
value: 1,
child: Text("First"),
),
PopupMenuItem(
value: 2,
child: ListTile(
leading: Icon(Icons.work),
title: Text('Second'),
)
),
],
icon: Icon(Icons.settings),
);
SpeedDial speedDial(context) => SpeedDial(
animatedIcon: AnimatedIcons.menu_close,
animatedIconTheme: IconThemeData(size: 22),
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Colors.white,
visible: true,
curve: Curves.bounceIn,
children: [
// FAB 1
SpeedDialChild(
child: Icon(Icons.refresh),
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Colors.white,
onTap: () {
refresh();
/* setState(() {
var rng = Random();
var pageSize = 1 + rng.nextInt(9);
futureDevices = tbContext.tbClient.getDeviceService().getTenantDeviceInfos(PageLink(pageSize));
}); */
},
label: 'Refresh',
labelStyle: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16.0),
),
// FAB 2
SpeedDialChild(
child: Icon(Icons.logout),
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Colors.white,
onTap: () {
tbContext.tbClient.logout(requestConfig: RequestConfig(ignoreErrors: true));
},
label: 'Logout',
labelStyle: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 16.0),
)
],
);
}

View File

@@ -0,0 +1,48 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:thingsboard_app/widgets/tb_app_bar.dart';
import 'package:thingsboard_app/core/context/tb_context.dart';
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
class HomePage extends TbPageWidget<HomePage, _HomePageState> {
HomePage(TbContext tbContext) : super(tbContext);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends TbPageState<HomePage, _HomePageState> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: TbAppBar(
tbContext,
title: const Text('ThingsBoard'),
),
body: Builder(
builder: (BuildContext context) {
return Center(child:
Column(
children: [
ElevatedButton(
child: Text('Devices'),
onPressed: () {
navigateTo('/devices');
},
)
],
)
);
}),
);
}
}

View File

@@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:thingsboard_app/widgets/tb_app_bar.dart';
import 'package:thingsboard_app/core/context/tb_context.dart';
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
class ProfilePage extends TbPageWidget<ProfilePage, _ProfilePageState> {
ProfilePage(TbContext tbContext) : super(tbContext);
@override
_ProfilePageState createState() => _ProfilePageState();
}
class _ProfilePageState extends TbPageState<ProfilePage, _ProfilePageState> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: TbAppBar(
tbContext,
title: const Text('Profile'),
showProfile: false,
showLogout: true,
),
body: Builder(
builder: (BuildContext context) {
return Center(child: const Text('TODO: Implement!'));
}),
);
}
}

View File

@@ -0,0 +1,23 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:thingsboard_client/thingsboard_client.dart';
class TbSecureStorage implements TbStorage {
final flutterStorage = FlutterSecureStorage();
@override
Future<void> deleteItem(String key) async {
return await flutterStorage.delete(key: key);
}
@override
Future<String?> getItem(String key) async {
return await flutterStorage.read(key: key);
}
@override
Future<void> setItem(String key, String value) async {
return await flutterStorage.write(key: key, value: value);
}
}

149
lib/widgets/tb_app_bar.dart Normal file
View File

@@ -0,0 +1,149 @@
import 'dart:async';
import 'package:stream_transform/stream_transform.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:thingsboard_app/config/themes/tb_theme.dart';
import 'package:thingsboard_app/core/context/tb_context.dart';
import 'package:thingsboard_app/core/context/tb_context_widget.dart';
import 'package:thingsboard_client/thingsboard_client.dart';
class TbAppBar extends TbContextWidget<TbAppBar, _TbAppBarState> implements PreferredSizeWidget {
final Widget? title;
final bool? showProfile;
final bool? showLogout;
final ValueNotifier<bool>? searchModeNotifier;
final String? searchHint;
final void Function(String searchText)? onSearch;
final void Function()? onSearchClosed;
@override
final Size preferredSize;
TbAppBar(TbContext tbContext, {this.title, this.showProfile = true, this.showLogout = false, this.searchModeNotifier, this.searchHint, this.onSearch, this.onSearchClosed}) :
preferredSize = Size.fromHeight(kToolbarHeight + 4),
super(tbContext);
@override
_TbAppBarState createState() => _TbAppBarState();
}
class _TbAppBarState extends TbContextState<TbAppBar, _TbAppBarState> {
final TextEditingController _filter = new TextEditingController();
final _textUpdates = StreamController<String>();
@override
void initState() {
super.initState();
if (widget.searchModeNotifier != null) {
_textUpdates.add('');
_filter.addListener(() {
_textUpdates.add(_filter.text);
});
_textUpdates.stream.debounce(const Duration(milliseconds: 150)).distinct().forEach((element) => widget.onSearch!(element));
}
}
@override
void dispose() {
_filter.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
List<Widget> children = <Widget>[];
if (widget.searchModeNotifier != null) {
children.add(ValueListenableBuilder(
valueListenable: widget.searchModeNotifier!,
builder: (context, bool searchMode, child) {
if (searchMode) {
return buildSearchBar();
} else {
return buildDefaultBar();
}
}
));
} else {
children.add(buildDefaultBar());
}
children.add(
ValueListenableBuilder(
valueListenable: loadingNotifier,
builder: (context, bool loading, child) {
if (loading) {
return LinearProgressIndicator();
} else {
return Container(height: 4);
}
}
)
);
return Column(
children: children,
);
}
AppBar buildSearchBar() {
return AppBar(
centerTitle: true,
title: Theme(
data: tbDarkTheme,
child: TextField(
controller: _filter,
cursorColor: Colors.white,
decoration: new InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.only(left: 15, bottom: 11, top: 15, right: 15),
suffixIcon: new Icon(Icons.search, color: Colors.white),
hintText: widget.searchHint ?? 'Search...',
),
)
),
leading: new IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
_filter.text = '';
widget.searchModeNotifier!.value = false;
},
),
);
}
AppBar buildDefaultBar() {
List<Widget> actions = <Widget>[];
if (widget.showProfile!) {
actions.add(IconButton(
icon: Icon(
Icons.account_circle,
color: Colors.white,
),
onPressed: () {
navigateTo('/profile');
},
));
}
if (widget.showLogout!) {
actions.add(
TextButton(
child: const Text('Logout',
style: TextStyle(
color: Colors.white
),
),
onPressed: () {
tbContext.tbClient.logout(
requestConfig: RequestConfig(ignoreErrors: true));
}
)
);
}
return AppBar(
title: widget.title,
actions: actions,
);
}
}

303
pubspec.lock Normal file
View File

@@ -0,0 +1,303 @@
# 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: "3.1.2"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
dio:
dependency: transitive
description:
name: dio
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
fluro:
dependency: "direct main"
description:
name: fluro
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_launcher_icons:
dependency: "direct dev"
description:
name: flutter_launcher_icons
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.0"
flutter_secure_storage:
dependency: "direct main"
description:
name: flutter_secure_storage
url: "https://pub.dartlang.org"
source: hosted
version: "4.1.0"
flutter_speed_dial:
dependency: "direct main"
description:
name: flutter_speed_dial
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.5"
flutter_svg:
dependency: "direct main"
description:
name: flutter_svg
url: "https://pub.dartlang.org"
source: hosted
version: "0.22.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
image:
dependency: transitive
description:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2"
infinite_scroll_pagination:
dependency: "direct main"
description:
name: infinite_scroll_pagination
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
jwt_decoder:
dependency: transitive
description:
name: jwt_decoder
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
path_drawing:
dependency: transitive
description:
name: path_drawing
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.0"
path_parsing:
dependency: transitive
description:
name: path_parsing
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
petitparser:
dependency: transitive
description:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "4.1.0"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
sliver_tools:
dependency: transitive
description:
name: sliver_tools
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.1"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
stream_transform:
dependency: "direct main"
description:
name: stream_transform
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.1.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.19"
thingsboard_client:
dependency: "direct main"
description:
path: "."
ref: HEAD
resolved-ref: "5b9e4a092f6b5963db22fdcac64ea97215753b92"
url: "git@github.com:thingsboard/dart_thingsboard_client.git"
source: git
version: "1.0.0"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
xml:
dependency: transitive
description:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.0"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
sdks:
dart: ">=2.12.0 <3.0.0"
flutter: ">=1.26.0-17.6.pre"

63
pubspec.yaml Normal file
View File

@@ -0,0 +1,63 @@
name: thingsboard_app
description: Flutter ThingsBoard Mobile Application
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0
environment:
sdk: ">=2.12.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
thingsboard_client:
git: git@github.com:thingsboard/dart_thingsboard_client.git
flutter_secure_storage: ^4.1.0
flutter_speed_dial: ^3.0.5
cupertino_icons: ^1.0.2
fluro: ^2.0.3
flutter_svg: ^0.22.0
infinite_scroll_pagination: ^3.0.1
stream_transform: ^2.0.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_launcher_icons: ^0.9.0
flutter:
uses-material-design: true
assets:
- assets/images/
# 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
flutter_icons:
android: "launcher_icon"
ios: true
image_path: "assets/images/thingsboard.png"

26
test/widget_test.dart Normal file
View File

@@ -0,0 +1,26 @@
// 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:thingsboard_app/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(ThingsboardApp());
// Verify that our counter starts at 0.
expect(find.byWidgetPredicate((widget) {
if (widget is MaterialApp) {
return widget.title == 'ThingsBoard';
}
return false;
}), findsOneWidget);
});
}