diff --git a/.gitignore b/.gitignore
index 34d26916..f1712fac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -91,3 +91,10 @@ lcov.info
!**/*/ios/**/default.pbxuser
!**/*/ios/**/default.perspectivev3
**/*/generated_plugin_registrant.dart
+built_redux/.flutter-plugins-dependencies
+firestore_redux/.flutter-plugins-dependencies
+frideos_library/.flutter-plugins-dependencies
+mvc/.flutter-plugins-dependencies
+mvu/.flutter-plugins-dependencies
+rvms/.flutter-plugins-dependencies
+scoped_model/.flutter-plugins-dependencies
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 00000000..2428e8fc
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,152 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "blocs",
+ "cwd": "blocs",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "bloc_flutter",
+ "cwd": "bloc_flutter",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "bloc_library",
+ "cwd": "bloc_library",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "built_redux",
+ "cwd": "built_redux",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "firebase_flutter_repository",
+ "cwd": "firebase_flutter_repository",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "firebase_rtdb_flutter_repository",
+ "cwd": "firebase_rtdb_flutter_repository",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "firestore_redux",
+ "cwd": "firestore_redux",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "frideos_library",
+ "cwd": "frideos_library",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "inherited_widget",
+ "cwd": "inherited_widget",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "integration_tests",
+ "cwd": "integration_tests",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "mobx",
+ "cwd": "mobx",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "mvc",
+ "cwd": "mvc",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "mvi_base",
+ "cwd": "mvi_base",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "mvi_flutter",
+ "cwd": "mvi_flutter",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "mvu",
+ "cwd": "mvu",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "redux",
+ "cwd": "redux",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "rvms",
+ "cwd": "rvms",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "scoped_model",
+ "cwd": "scoped_model",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "simple_blocs",
+ "cwd": "simple_blocs",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "simple_bloc_flutter",
+ "cwd": "simple_bloc_flutter",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "todos_app_core",
+ "cwd": "todos_app_core",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "todos_repository_core",
+ "cwd": "todos_repository_core",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "todos_repository_simple",
+ "cwd": "todos_repository_simple",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "vanilla",
+ "cwd": "vanilla",
+ "request": "launch",
+ "type": "dart"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/built_redux/.flutter-plugins-dependencies b/built_redux/.flutter-plugins-dependencies
deleted file mode 100644
index 51c6a6c7..00000000
--- a/built_redux/.flutter-plugins-dependencies
+++ /dev/null
@@ -1 +0,0 @@
-{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"path_provider","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.0/","dependencies":[]}],"android":[{"name":"path_provider","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.0/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"path_provider","dependencies":[]}],"date_created":"2020-02-10 11:23:40.410802","version":"1.14.7-pre.38"}
\ No newline at end of file
diff --git a/firestore_redux/.flutter-plugins-dependencies b/firestore_redux/.flutter-plugins-dependencies
deleted file mode 100644
index ba421a8f..00000000
--- a/firestore_redux/.flutter-plugins-dependencies
+++ /dev/null
@@ -1 +0,0 @@
-{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"cloud_firestore","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/cloud_firestore-0.13.1+1/","dependencies":["firebase_core"]},{"name":"firebase_auth","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/firebase_auth-0.15.4/","dependencies":["firebase_core"]},{"name":"firebase_core","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/firebase_core-0.4.3+3/","dependencies":[]},{"name":"path_provider","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.0/","dependencies":[]}],"android":[{"name":"cloud_firestore","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/cloud_firestore-0.13.1+1/","dependencies":["firebase_core"]},{"name":"firebase_auth","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/firebase_auth-0.15.4/","dependencies":["firebase_core"]},{"name":"firebase_core","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/firebase_core-0.4.3+3/","dependencies":[]},{"name":"path_provider","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.0/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[{"name":"firebase_auth_web","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/firebase_auth_web-0.1.2/","dependencies":[]},{"name":"firebase_core_web","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/firebase_core_web-0.1.1+2/","dependencies":[]}]},"dependencyGraph":[{"name":"cloud_firestore","dependencies":["firebase_core"]},{"name":"firebase_auth","dependencies":["firebase_core","firebase_auth_web"]},{"name":"firebase_auth_web","dependencies":[]},{"name":"firebase_core","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","dependencies":[]},{"name":"path_provider","dependencies":[]}],"date_created":"2020-02-10 11:23:57.149758","version":"1.14.7-pre.38"}
\ No newline at end of file
diff --git a/frideos_library/.flutter-plugins-dependencies b/frideos_library/.flutter-plugins-dependencies
deleted file mode 100644
index 4ded62f8..00000000
--- a/frideos_library/.flutter-plugins-dependencies
+++ /dev/null
@@ -1 +0,0 @@
-{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"path_provider","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.0/","dependencies":[]}],"android":[{"name":"path_provider","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.0/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"path_provider","dependencies":[]}],"date_created":"2020-02-10 11:24:01.218565","version":"1.14.7-pre.38"}
\ No newline at end of file
diff --git a/mvc/.flutter-plugins-dependencies b/mvc/.flutter-plugins-dependencies
deleted file mode 100644
index b728d6fd..00000000
--- a/mvc/.flutter-plugins-dependencies
+++ /dev/null
@@ -1 +0,0 @@
-{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"path_provider","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.0/","dependencies":[]}],"android":[{"name":"path_provider","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.0/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"path_provider","dependencies":[]}],"date_created":"2020-02-10 11:24:16.829310","version":"1.14.7-pre.38"}
\ No newline at end of file
diff --git a/mvu/.flutter-plugins-dependencies b/mvu/.flutter-plugins-dependencies
deleted file mode 100644
index 30693179..00000000
--- a/mvu/.flutter-plugins-dependencies
+++ /dev/null
@@ -1 +0,0 @@
-{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"path_provider","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.0/","dependencies":[]}],"android":[{"name":"path_provider","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.0/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"path_provider","dependencies":[]}],"date_created":"2020-02-10 11:24:26.886816","version":"1.14.7-pre.38"}
\ No newline at end of file
diff --git a/mvu/pubspec.yaml b/mvu/pubspec.yaml
index df6c4155..0d521337 100644
--- a/mvu/pubspec.yaml
+++ b/mvu/pubspec.yaml
@@ -19,8 +19,8 @@ environment:
dependencies:
flutter:
sdk: flutter
- built_value: ^6.3.1
- built_collection: ^4.1.0
+ built_value: ^7.1.0
+ built_collection: ^4.3.2
dartea: "^0.5.5"
todos_app_core:
path: ../todos_app_core
@@ -33,8 +33,8 @@ dev_dependencies:
flutter_driver:
sdk: flutter
test:
- build_runner: ^1.2.8
- built_value_generator: ^6.1.0
+ build_runner: any
+ built_value_generator: ^7.1.0
integration_tests:
path: ../integration_tests
diff --git a/rvms/.gitignore b/rvms/.gitignore
new file mode 100644
index 00000000..2ddde2a5
--- /dev/null
+++ b/rvms/.gitignore
@@ -0,0 +1,73 @@
+# 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/
+.dart_tool/
+.flutter-plugins
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Android related
+**/android/**/gradle-wrapper.jar
+**/android/.gradle
+**/android/captures/
+**/android/gradlew
+**/android/gradlew.bat
+**/android/local.properties
+**/android/**/GeneratedPluginRegistrant.java
+
+# iOS/XCode related
+**/ios/**/*.mode1v3
+**/ios/**/*.mode2v3
+**/ios/**/*.moved-aside
+**/ios/**/*.pbxuser
+**/ios/**/*.perspectivev3
+**/ios/**/*sync/
+**/ios/**/.sconsign.dblite
+**/ios/**/.tags*
+**/ios/**/.vagrant/
+**/ios/**/DerivedData/
+**/ios/**/Icon?
+**/ios/**/Pods/
+**/ios/**/.symlinks/
+**/ios/**/profile
+**/ios/**/xcuserdata
+**/ios/.generated/
+**/ios/Flutter/App.framework
+**/ios/Flutter/Flutter.framework
+**/ios/Flutter/Generated.xcconfig
+**/ios/Flutter/app.flx
+**/ios/Flutter/app.zip
+**/ios/Flutter/flutter_assets/
+**/ios/Flutter/flutter_export_environment.sh
+**/ios/ServiceDefinitions.json
+**/ios/Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!**/ios/**/default.mode1v3
+!**/ios/**/default.mode2v3
+!**/ios/**/default.pbxuser
+!**/ios/**/default.perspectivev3
+!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
diff --git a/rvms/.metadata b/rvms/.metadata
new file mode 100644
index 00000000..1b5cec02
--- /dev/null
+++ b/rvms/.metadata
@@ -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: 27321ebbad34b0a3fafe99fac037102196d655ff
+ channel: stable
+
+project_type: app
diff --git a/rvms/.vscode/settings.json b/rvms/.vscode/settings.json
new file mode 100644
index 00000000..97fe5ca4
--- /dev/null
+++ b/rvms/.vscode/settings.json
@@ -0,0 +1,16 @@
+{
+ "spellright.language": [
+ "en"
+ ],
+ "spellright.documentTypes": [
+ "markdown",
+ "latex",
+ "plaintext",
+ "dart"
+ ],
+ "spellright.parserByClass": {
+ "dart": {
+ "parser": "code"
+ }
+ }
+}
\ No newline at end of file
diff --git a/rvms/README.md b/rvms/README.md
new file mode 100644
index 00000000..9ac826e5
--- /dev/null
+++ b/rvms/README.md
@@ -0,0 +1,11 @@
+# rvms
+
+This is an implementation of the architecture sample following the RVMS approach where you have Views over Managers that contain the business logic which uses Services to connect to the outside world.
+
+It uses the following packages:
+
+* `get_it`: as ServiceLocator to access Managers from the UI and Services from Managers
+* `get_it_mixin`: to bind the data inside GetIt to the the Widgets
++ `flutter_command`: as connector between Manager UI
+* `functional_listener`: rx like extension methods for `ValueListenables`
+* `listenable_collections`: We use `ListNotifier` of this package which is a List that behaves like a `ValueNofitier`
\ No newline at end of file
diff --git a/rvms/android/.gitignore b/rvms/android/.gitignore
new file mode 100644
index 00000000..bc2100d8
--- /dev/null
+++ b/rvms/android/.gitignore
@@ -0,0 +1,7 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
diff --git a/rvms/android/app/build.gradle b/rvms/android/app/build.gradle
new file mode 100644
index 00000000..bec322fb
--- /dev/null
+++ b/rvms/android/app/build.gradle
@@ -0,0 +1,67 @@
+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 28
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ lintOptions {
+ disable 'InvalidPackage'
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId "com.example.rvms"
+ minSdkVersion 16
+ targetSdkVersion 28
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test:runner:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
+}
diff --git a/rvms/android/app/src/debug/AndroidManifest.xml b/rvms/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 00000000..95b02ba7
--- /dev/null
+++ b/rvms/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/rvms/android/app/src/main/AndroidManifest.xml b/rvms/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..6727b132
--- /dev/null
+++ b/rvms/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rvms/android/app/src/main/kotlin/com/example/rvms/MainActivity.kt b/rvms/android/app/src/main/kotlin/com/example/rvms/MainActivity.kt
new file mode 100644
index 00000000..6abd27a4
--- /dev/null
+++ b/rvms/android/app/src/main/kotlin/com/example/rvms/MainActivity.kt
@@ -0,0 +1,12 @@
+package com.example.rvms
+
+import androidx.annotation.NonNull;
+import io.flutter.embedding.android.FlutterActivity
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.plugins.GeneratedPluginRegistrant
+
+class MainActivity: FlutterActivity() {
+ override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
+ GeneratedPluginRegistrant.registerWith(flutterEngine);
+ }
+}
diff --git a/rvms/android/app/src/main/kotlin/com/example/scoped_model/MainActivity.kt b/rvms/android/app/src/main/kotlin/com/example/scoped_model/MainActivity.kt
new file mode 100644
index 00000000..2334c261
--- /dev/null
+++ b/rvms/android/app/src/main/kotlin/com/example/scoped_model/MainActivity.kt
@@ -0,0 +1,12 @@
+package com.example.scoped_model
+
+import androidx.annotation.NonNull;
+import io.flutter.embedding.android.FlutterActivity
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.plugins.GeneratedPluginRegistrant
+
+class MainActivity: FlutterActivity() {
+ override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
+ GeneratedPluginRegistrant.registerWith(flutterEngine);
+ }
+}
diff --git a/rvms/android/app/src/main/res/drawable/launch_background.xml b/rvms/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 00000000..304732f8
--- /dev/null
+++ b/rvms/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/rvms/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/rvms/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..db77bb4b
Binary files /dev/null and b/rvms/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/rvms/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/rvms/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..17987b79
Binary files /dev/null and b/rvms/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/rvms/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/rvms/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..09d43914
Binary files /dev/null and b/rvms/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/rvms/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/rvms/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..d5f1c8d3
Binary files /dev/null and b/rvms/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/rvms/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/rvms/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..4d6372ee
Binary files /dev/null and b/rvms/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/rvms/android/app/src/main/res/values/styles.xml b/rvms/android/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000..00fa4417
--- /dev/null
+++ b/rvms/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/rvms/android/app/src/profile/AndroidManifest.xml b/rvms/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 00000000..95b02ba7
--- /dev/null
+++ b/rvms/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/rvms/android/build.gradle b/rvms/android/build.gradle
new file mode 100644
index 00000000..3100ad2d
--- /dev/null
+++ b/rvms/android/build.gradle
@@ -0,0 +1,31 @@
+buildscript {
+ ext.kotlin_version = '1.3.50'
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.5.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
+}
diff --git a/rvms/android/gradle.properties b/rvms/android/gradle.properties
new file mode 100644
index 00000000..38c8d454
--- /dev/null
+++ b/rvms/android/gradle.properties
@@ -0,0 +1,4 @@
+org.gradle.jvmargs=-Xmx1536M
+android.enableR8=true
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/rvms/android/gradle/wrapper/gradle-wrapper.properties b/rvms/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..296b146b
--- /dev/null
+++ b/rvms/android/gradle/wrapper/gradle-wrapper.properties
@@ -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-5.6.2-all.zip
diff --git a/rvms/android/settings.gradle b/rvms/android/settings.gradle
new file mode 100644
index 00000000..5a2f14fb
--- /dev/null
+++ b/rvms/android/settings.gradle
@@ -0,0 +1,15 @@
+include ':app'
+
+def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
+
+def plugins = new Properties()
+def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
+if (pluginsFile.exists()) {
+ pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
+}
+
+plugins.each { name, path ->
+ def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
+ include ":$name"
+ project(":$name").projectDir = pluginDirectory
+}
diff --git a/rvms/ios/.gitignore b/rvms/ios/.gitignore
new file mode 100644
index 00000000..e96ef602
--- /dev/null
+++ b/rvms/ios/.gitignore
@@ -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
diff --git a/rvms/ios/Flutter/AppFrameworkInfo.plist b/rvms/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 00000000..6b4c0f78
--- /dev/null
+++ b/rvms/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ App
+ CFBundleIdentifier
+ io.flutter.flutter.app
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ App
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ MinimumOSVersion
+ 8.0
+
+
diff --git a/rvms/ios/Flutter/Debug.xcconfig b/rvms/ios/Flutter/Debug.xcconfig
new file mode 100644
index 00000000..e8efba11
--- /dev/null
+++ b/rvms/ios/Flutter/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "Generated.xcconfig"
diff --git a/rvms/ios/Flutter/Release.xcconfig b/rvms/ios/Flutter/Release.xcconfig
new file mode 100644
index 00000000..399e9340
--- /dev/null
+++ b/rvms/ios/Flutter/Release.xcconfig
@@ -0,0 +1,2 @@
+#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "Generated.xcconfig"
diff --git a/rvms/ios/Podfile b/rvms/ios/Podfile
new file mode 100644
index 00000000..b30a428b
--- /dev/null
+++ b/rvms/ios/Podfile
@@ -0,0 +1,90 @@
+# Uncomment this line to define a global platform for your project
+# platform :ios, '9.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def parse_KV_file(file, separator='=')
+ file_abs_path = File.expand_path(file)
+ if !File.exists? file_abs_path
+ return [];
+ end
+ generated_key_values = {}
+ skip_line_start_symbols = ["#", "/"]
+ File.foreach(file_abs_path) do |line|
+ next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
+ plugin = line.split(pattern=separator)
+ if plugin.length == 2
+ podname = plugin[0].strip()
+ path = plugin[1].strip()
+ podpath = File.expand_path("#{path}", file_abs_path)
+ generated_key_values[podname] = podpath
+ else
+ puts "Invalid plugin specification: #{line}"
+ end
+ end
+ generated_key_values
+end
+
+target 'Runner' do
+ use_frameworks!
+ use_modular_headers!
+
+ # Flutter Pod
+
+ copied_flutter_dir = File.join(__dir__, 'Flutter')
+ copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
+ copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
+ unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
+ # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
+ # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration.
+ # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
+
+ generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+ end
+ generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
+ cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR'];
+
+ unless File.exist?(copied_framework_path)
+ FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
+ end
+ unless File.exist?(copied_podspec_path)
+ FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir)
+ end
+ end
+
+ # Keep pod path relative so it can be checked into Podfile.lock.
+ pod 'Flutter', :path => 'Flutter'
+
+ # Plugin Pods
+
+ # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
+ # referring to absolute paths on developers' machines.
+ system('rm -rf .symlinks')
+ system('mkdir -p .symlinks/plugins')
+ plugin_pods = parse_KV_file('../.flutter-plugins')
+ plugin_pods.each do |name, path|
+ symlink = File.join('.symlinks', 'plugins', name)
+ File.symlink(path, symlink)
+ pod name, :path => File.join(symlink, 'ios')
+ end
+end
+
+# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
+install! 'cocoapods', :disable_input_output_paths => true
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ target.build_configurations.each do |config|
+ config.build_settings['ENABLE_BITCODE'] = 'NO'
+ end
+ end
+end
diff --git a/rvms/ios/Runner.xcodeproj/project.pbxproj b/rvms/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..1ccce2d4
--- /dev/null
+++ b/rvms/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,518 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
+ 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
+ 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
+ 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
+ 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; };
+ 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 = ""; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
+ 3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B80C3931E831B6300D905FE /* App.framework */,
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEBA1CF902C7004384FC /* Flutter.framework */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ );
+ sourceTree = "";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 97C146F11CF9000F007C117D /* Supporting Files */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+ 97C146F11CF9000F007C117D /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = "Supporting Files";
+ sourceTree = "";
+ };
+/* 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 = "The Chromium Authors";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 3.2";
+ 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\" 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 = "";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_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 = 8.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;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.scopedModel;
+ 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;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_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 = 8.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_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 = 8.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;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.scopedModel;
+ 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;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.scopedModel;
+ 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 */;
+}
diff --git a/rvms/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/rvms/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..1d526a16
--- /dev/null
+++ b/rvms/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/rvms/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/rvms/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 00000000..a28140cf
--- /dev/null
+++ b/rvms/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rvms/ios/Runner.xcworkspace/contents.xcworkspacedata b/rvms/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..1d526a16
--- /dev/null
+++ b/rvms/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/rvms/ios/Runner/AppDelegate.swift b/rvms/ios/Runner/AppDelegate.swift
new file mode 100644
index 00000000..70693e4a
--- /dev/null
+++ b/rvms/ios/Runner/AppDelegate.swift
@@ -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)
+ }
+}
diff --git a/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..d36b1fab
--- /dev/null
+++ b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -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"
+ }
+}
diff --git a/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 00000000..28c6bf03
Binary files /dev/null and b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 00000000..2ccbfd96
Binary files /dev/null and b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 00000000..f091b6b0
Binary files /dev/null and b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 00000000..4cde1211
Binary files /dev/null and b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 00000000..d0ef06e7
Binary files /dev/null and b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 00000000..dcdc2306
Binary files /dev/null and b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 00000000..2ccbfd96
Binary files /dev/null and b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 00000000..c8f9ed8f
Binary files /dev/null and b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 00000000..a6d6b860
Binary files /dev/null and b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 00000000..a6d6b860
Binary files /dev/null and b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 00000000..75b2d164
Binary files /dev/null and b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 00000000..c4df70d3
Binary files /dev/null and b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 00000000..6a84f41e
Binary files /dev/null and b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 00000000..d0e1f585
Binary files /dev/null and b/rvms/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/rvms/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/rvms/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 00000000..0bedcf2f
--- /dev/null
+++ b/rvms/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -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"
+ }
+}
diff --git a/rvms/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/rvms/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 00000000..9da19eac
Binary files /dev/null and b/rvms/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/rvms/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/rvms/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 00000000..9da19eac
Binary files /dev/null and b/rvms/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/rvms/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/rvms/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 00000000..9da19eac
Binary files /dev/null and b/rvms/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/rvms/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/rvms/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 00000000..89c2725b
--- /dev/null
+++ b/rvms/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -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.
\ No newline at end of file
diff --git a/rvms/ios/Runner/Base.lproj/LaunchScreen.storyboard b/rvms/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 00000000..f2e259c7
--- /dev/null
+++ b/rvms/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rvms/ios/Runner/Base.lproj/Main.storyboard b/rvms/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 00000000..f3c28516
--- /dev/null
+++ b/rvms/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rvms/ios/Runner/Info.plist b/rvms/ios/Runner/Info.plist
new file mode 100644
index 00000000..3de9ef1b
--- /dev/null
+++ b/rvms/ios/Runner/Info.plist
@@ -0,0 +1,45 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ rvms
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+
+
diff --git a/rvms/ios/Runner/Runner-Bridging-Header.h b/rvms/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 00000000..7335fdf9
--- /dev/null
+++ b/rvms/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1 @@
+#import "GeneratedPluginRegistrant.h"
\ No newline at end of file
diff --git a/rvms/lib/display_todos/_manager/todo_manager_.dart b/rvms/lib/display_todos/_manager/todo_manager_.dart
new file mode 100644
index 00000000..51d74b4e
--- /dev/null
+++ b/rvms/lib/display_todos/_manager/todo_manager_.dart
@@ -0,0 +1,33 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter_command/flutter_command.dart';
+import 'package:rvms_model_sample/display_todos/_model/todo.dart';
+
+abstract class TodoManager {
+ ValueListenable> get filteredTodos;
+ ValueListenable> get allTodos;
+ ValueListenable get errors;
+
+ Command selectFilterCommand;
+ Command loadTodoCommand;
+ Command clearCompletedCommand;
+ Command upLoadCommand;
+
+ VisibilityFilter get activeFilter;
+
+ void toggleAll();
+
+ /// updates a [Todo] by replacing the item with the same id by the parameter [todo]
+ void updateTodo(Todo todo);
+
+ void removeTodo(Todo todo);
+
+ void addTodo(Todo todo);
+
+ Todo todoById(String id);
+}
+
+enum VisibilityFilter { all, active, completed }
diff --git a/rvms/lib/display_todos/_manager/todo_manager_implementation.dart b/rvms/lib/display_todos/_manager/todo_manager_implementation.dart
new file mode 100644
index 00000000..1fec3039
--- /dev/null
+++ b/rvms/lib/display_todos/_manager/todo_manager_implementation.dart
@@ -0,0 +1,139 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter_command/flutter_command.dart';
+import 'package:listenable_collections/listenable_collections.dart';
+import 'package:rvms_model_sample/display_todos/_manager/todo_manager_.dart';
+import 'package:rvms_model_sample/display_todos/_model/todo.dart';
+import 'package:rvms_model_sample/display_todos/_services/repository_service_.dart';
+
+import '../../locator.dart';
+
+class TodoManagerImplementation implements TodoManager {
+ @override
+ ValueListenable> get allTodos => _todos;
+ final _todos = ListNotifier(data: []);
+
+ @override
+ ValueListenable> get filteredTodos => _filteredTodos;
+ ValueNotifier> _filteredTodos;
+
+ @override
+ ValueListenable get errors => _errors;
+ ValueNotifier _errors;
+
+ @override
+ VisibilityFilter get activeFilter => selectFilterCommand.value;
+
+ @override
+ Command selectFilterCommand;
+ @override
+ Command loadTodoCommand;
+ @override
+ Command clearCompletedCommand;
+ @override
+ Command upLoadCommand;
+
+ TodoManagerImplementation({
+ VisibilityFilter activeFilter,
+ }) {
+ loadTodoCommand = Command.createAsyncNoParamNoResult(loadTodos)
+ ..thrownExceptions.listen((_, __) => _todos.clear());
+
+ /// We wouldn't need to implement this as a command as we are not using any of the
+ /// features that a command gives us.
+ clearCompletedCommand = Command.createSyncNoParamNoResult(() {
+ _todos.removeWhere((todo) => todo.complete);
+ });
+
+ /// if this app didn't have already a snackbar with undo function
+ /// we could pass a changing text for the snackbar that should
+ /// be displayed when the command is finished
+ ///
+ /// I will add the needed code in the UI but comment it out
+ upLoadCommand = Command.createAsync((snackBarText) async {
+ await _uploadItems();
+ return snackBarText;
+ }, '');
+
+ _errors = loadTodoCommand.thrownExceptions.mergeWith(
+ [upLoadCommand.thrownExceptions]).map((error) => error.toString());
+
+ selectFilterCommand = Command.createSync((filter) => filter, activeFilter);
+
+ /// the combineLatest ensures that [todos] get's updated whenever [_todos]
+ /// changes or when [selectFilterCommand is called]
+ _filteredTodos = selectFilterCommand
+ .combineLatest, List>(_todos, (filter, todos) {
+ return todos.where((todo) {
+ switch (filter) {
+ case VisibilityFilter.active:
+ return !todo.complete;
+ case VisibilityFilter.completed:
+ return todo.complete;
+ case VisibilityFilter.all:
+ default:
+ return true;
+ }
+ }).toList();
+ });
+ }
+
+ /// Loads remote data
+ ///
+ /// Call this initially and when the user manually refreshes
+ Future loadTodos() async {
+ _todos.clear();
+ return _todos.addAll(await locator().loadTodos());
+ }
+
+ @override
+ void toggleAll() {
+ var allComplete = _todos.every((todo) => todo.complete);
+ _todos.startTransAction();
+ for (var i = 0; i < _todos.length; i++) {
+ _todos[i] = _todos[i].copy(complete: !allComplete);
+ }
+ _todos.endTransAction();
+ upLoadCommand('Upload finished');
+ }
+
+ /// updates a [Todo] by replacing the item with the same id by the parameter [todo]
+ @override
+ void updateTodo(Todo todo) {
+ assert(todo != null);
+ assert(todo.id != null);
+ var oldTodo = _todos.firstWhere((it) => it.id == todo.id);
+ var replaceIndex = _todos.indexOf(oldTodo);
+ _todos[replaceIndex] = todo;
+ upLoadCommand('Update finished');
+ }
+
+ @override
+ void removeTodo(Todo todo) {
+ _todos.removeWhere((it) => it.id == todo.id);
+ upLoadCommand('Todo deleted');
+ }
+
+ @override
+ void addTodo(Todo todo) {
+ _todos.add(todo);
+ upLoadCommand('Todo added');
+ }
+
+ Future _uploadItems() async {
+ /// to simulate a longer transaction to show that then a spinner
+ /// is automatic displayed
+ /// await Future.delayed(Duration(milliseconds: 2000));
+ return await locator().saveTodos(_todos);
+ }
+
+ @override
+ Todo todoById(String id) {
+ return _todos.firstWhere((it) => it.id == id, orElse: () => null);
+ }
+}
diff --git a/rvms/lib/display_todos/_model/todo.dart b/rvms/lib/display_todos/_model/todo.dart
new file mode 100644
index 00000000..319c186d
--- /dev/null
+++ b/rvms/lib/display_todos/_model/todo.dart
@@ -0,0 +1,58 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'package:todos_app_core/todos_app_core.dart';
+import 'package:todos_repository_core/todos_repository_core.dart';
+
+class Todo {
+ final bool complete;
+ final String id;
+ final String note;
+ final String task;
+
+ Todo(this.task, {this.complete = false, this.note = '', String id})
+ // ignore: unnecessary_this
+ : this.id = id ?? Uuid().generateV4();
+
+ @override
+ int get hashCode =>
+ complete.hashCode ^ task.hashCode ^ note.hashCode ^ id.hashCode;
+
+ @override
+ bool operator ==(Object other) =>
+ identical(this, other) ||
+ other is Todo &&
+ runtimeType == other.runtimeType &&
+ complete == other.complete &&
+ task == other.task &&
+ note == other.note &&
+ id == other.id;
+
+ @override
+ String toString() {
+ return 'Todo{complete: $complete, task: $task, note: $note, id: $id}';
+ }
+
+ TodoEntity toEntity() {
+ return TodoEntity(task, id, note, complete);
+ }
+
+ static Todo fromEntity(TodoEntity entity) {
+ return Todo(
+ entity.task,
+ complete: entity.complete ?? false,
+ note: entity.note,
+ id: entity.id,
+ );
+ }
+
+ Todo copy({String task, bool complete, String note, String id}) {
+ return Todo(
+ task ?? this.task,
+ complete: complete ?? this.complete,
+ note: note ?? this.note,
+ id: id ?? this.id,
+ );
+ }
+}
diff --git a/rvms/lib/display_todos/_model/typedefs.dart b/rvms/lib/display_todos/_model/typedefs.dart
new file mode 100644
index 00000000..df89d8e5
--- /dev/null
+++ b/rvms/lib/display_todos/_model/typedefs.dart
@@ -0,0 +1,21 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'package:rvms_model_sample/display_todos/_model/todo.dart';
+
+typedef TodoAdder = void Function(Todo todo);
+
+typedef TodoRemover = void Function(Todo todo);
+
+typedef TodoUpdater = void Function(
+ Todo todo, {
+ bool complete,
+ String id,
+ String note,
+ String task,
+});
+
+enum AppTab { todos, stats }
+
+enum ExtraAction { toggleAllComplete, clearCompleted }
diff --git a/rvms/lib/display_todos/_services/repository_service_.dart b/rvms/lib/display_todos/_services/repository_service_.dart
new file mode 100644
index 00000000..1d39edaf
--- /dev/null
+++ b/rvms/lib/display_todos/_services/repository_service_.dart
@@ -0,0 +1,18 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'dart:async';
+import 'dart:core';
+
+import 'package:rvms_model_sample/display_todos/_model/todo.dart';
+
+abstract class RepositoryService {
+
+ /// Loads todos first from File storage. If they don't exist or encounter an
+ /// error, it attempts to load the Todos from a Web Service.
+ Future> loadTodos();
+
+ // Persists todos to local disk and the web
+ Future saveTodos(List todos);
+}
diff --git a/rvms/lib/display_todos/_services/repository_service_impl.dart b/rvms/lib/display_todos/_services/repository_service_impl.dart
new file mode 100644
index 00000000..a5d54075
--- /dev/null
+++ b/rvms/lib/display_todos/_services/repository_service_impl.dart
@@ -0,0 +1,57 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'dart:async';
+import 'dart:core';
+
+import 'package:path_provider/path_provider.dart';
+import 'package:rvms_model_sample/display_todos/_model/todo.dart';
+import 'package:rvms_model_sample/display_todos/_services/repository_service_.dart';
+import 'package:todos_repository_core/todos_repository_core.dart';
+import 'package:todos_repository_local_storage/todos_repository_local_storage.dart';
+
+/// A class that glues together our local file storage and web client. It has a
+/// clear responsibility: Load Todos and Persist todos.
+///
+/// In most apps, we use the provided repository. In this case, it makes sense
+/// to demonstrate the built_value serializers, which are used in the
+/// FileStorage part of this app.
+///
+/// Please see the `todos_repository` library for more information about the
+/// Repository pattern.
+class RepositoryServiceImplementation implements RepositoryService {
+ final FileStorage fileStorage;
+ final WebClient webClient;
+
+ const RepositoryServiceImplementation({
+ this.fileStorage = const FileStorage(
+ '__built_redux_sample_app__',
+ getApplicationDocumentsDirectory,
+ ),
+ this.webClient = const WebClient(),
+ });
+
+ /// Loads todos first from File storage. If they don't exist or encounter an
+ /// error, it attempts to load the Todos from a Web Service.
+ @override
+ Future> loadTodos() async {
+ List loadedTodos;
+ try {
+ loadedTodos = await fileStorage.loadTodos();
+ } catch (e) {
+ loadedTodos = await webClient.loadTodos();
+ }
+ return loadedTodos.map(Todo.fromEntity).toList();
+ }
+
+ // Persists todos to local disk and the web
+ @override
+ Future saveTodos(List todos) {
+ final todoEntities = todos.map((it) => it.toEntity()).toList();
+ return Future.wait([
+ fileStorage.saveTodos(todoEntities),
+ webClient.saveTodos(todoEntities),
+ ]);
+ }
+}
diff --git a/rvms/lib/display_todos/detail_screen.dart b/rvms/lib/display_todos/detail_screen.dart
new file mode 100644
index 00000000..cbe8cf9c
--- /dev/null
+++ b/rvms/lib/display_todos/detail_screen.dart
@@ -0,0 +1,105 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:rvms_model_sample/display_todos/_manager/todo_manager_.dart';
+import 'package:rvms_model_sample/display_todos/_model/todo.dart';
+import 'package:rvms_model_sample/edit_todos/add_edit_screen.dart';
+import 'package:rvms_model_sample/locator.dart';
+import 'package:todos_app_core/todos_app_core.dart';
+
+class DetailScreen extends StatelessWidget {
+ final String todoId;
+
+ DetailScreen({
+ @required this.todoId,
+ }) : super(key: ArchSampleKeys.todoDetailsScreen);
+
+ @override
+ Widget build(BuildContext context) {
+ final todoManager = locator();
+ // fallback to empty item. When deleting it, it is null before the screen is gone
+ var todo = todoManager.todoById(todoId) ?? Todo('');
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(ArchSampleLocalizations.of(context).todoDetails),
+ actions: [
+ IconButton(
+ key: ArchSampleKeys.deleteTodoButton,
+ tooltip: ArchSampleLocalizations.of(context).deleteTodo,
+ icon: Icon(Icons.delete),
+ onPressed: () {
+ todoManager.removeTodo(todo);
+ Navigator.pop(context, todo);
+ },
+ )
+ ],
+ ),
+ body: Padding(
+ padding: EdgeInsets.all(16.0),
+ child: ListView(
+ children: [
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: EdgeInsets.only(right: 8.0),
+ child: Checkbox(
+ value: todo.complete,
+ key: ArchSampleKeys.detailsTodoItemCheckbox,
+ onChanged: (complete) {
+ todoManager
+ .updateTodo(todo.copy(complete: !todo.complete));
+ },
+ ),
+ ),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: EdgeInsets.only(
+ top: 8.0,
+ bottom: 16.0,
+ ),
+ child: Text(
+ todo.task,
+ key: ArchSampleKeys.detailsTodoItemTask,
+ style: Theme.of(context).textTheme.headline5,
+ ),
+ ),
+ Text(
+ todo.note,
+ key: ArchSampleKeys.detailsTodoItemNote,
+ style: Theme.of(context).textTheme.subtitle1,
+ )
+ ],
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ floatingActionButton: FloatingActionButton(
+ tooltip: ArchSampleLocalizations.of(context).editTodo,
+ child: Icon(Icons.edit),
+ key: ArchSampleKeys.editTodoFab,
+ onPressed: () {
+ Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (context) {
+ return AddEditScreen(
+ todoId: todoId,
+ key: ArchSampleKeys.editTodoScreen,
+ );
+ },
+ ),
+ );
+ },
+ ),
+ );
+ }
+}
diff --git a/rvms/lib/display_todos/home_screen.dart b/rvms/lib/display_todos/home_screen.dart
new file mode 100644
index 00000000..00df8d38
--- /dev/null
+++ b/rvms/lib/display_todos/home_screen.dart
@@ -0,0 +1,136 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:functional_listener/functional_listener.dart';
+import 'package:get_it_mixin/get_it_mixin.dart';
+import 'package:rvms_model_sample/display_todos/_manager/todo_manager_.dart';
+import 'package:rvms_model_sample/display_todos/_model/typedefs.dart';
+import 'package:rvms_model_sample/display_todos/widgets/extra_actions_button.dart';
+import 'package:rvms_model_sample/display_todos/widgets/filter_button.dart';
+import 'package:rvms_model_sample/display_todos/widgets/stats_counter.dart';
+import 'package:rvms_model_sample/display_todos/widgets/todo_list.dart';
+import 'package:rvms_model_sample/localization.dart';
+import 'package:todos_app_core/todos_app_core.dart';
+
+class HomeScreen extends StatefulWidget with GetItStatefulWidgetMixin {
+ HomeScreen() : super(key: ArchSampleKeys.homeScreen);
+
+ @override
+ State createState() {
+ return HomeScreenState();
+ }
+}
+
+class HomeScreenState extends State with GetItStateMixin {
+ AppTab _activeTab = AppTab.todos;
+
+ @override
+ Widget build(BuildContext context) {
+ /// We use GetIt's start-up synchronization support here
+ if (!allReady()) {
+ return Center(
+ child: CircularProgressIndicator(),
+ );
+ } else {
+ /// Show an Error dialog whenever the TodoManager reports an error
+ registerHandler((TodoManager manager) => manager.errors,
+ (context, message, _) => showErrorDlg(context, message));
+
+ /// if data uploading takes longer than 500ms show a waiting spinner
+ registerHandler(
+ (TodoManager manager) => manager.upLoadCommand.isExecuting
+ .debounce(Duration(milliseconds: 500)),
+ (context, busy, __) => showSpinner(context, busy));
+
+ ///alterrnative snackbar
+ // registerHandler(
+ // (TodoManager manager) => manager.upLoadCommand,
+ // (context, snackText, _) => Scaffold.of(context).showSnackBar(
+ // SnackBar(
+ // content: Text(snackText),
+ // duration: Duration(milliseconds: 800),
+ // ),
+ // ));
+
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(RvmsLocalizations.of(context).appTitle),
+ actions: [
+ FilterButton(isActive: _activeTab == AppTab.todos),
+ ExtraActionsButton()
+ ],
+ ),
+ body: _activeTab == AppTab.todos ? TodoList() : StatsCounter(),
+ floatingActionButton: FloatingActionButton(
+ key: ArchSampleKeys.addTodoFab,
+ onPressed: () {
+ Navigator.pushNamed(context, ArchSampleRoutes.addTodo);
+ },
+ child: Icon(Icons.add),
+ tooltip: ArchSampleLocalizations.of(context).addTodo,
+ ),
+ bottomNavigationBar: BottomNavigationBar(
+ key: ArchSampleKeys.tabs,
+ currentIndex: AppTab.values.indexOf(_activeTab),
+ onTap: (index) {
+ _updateTab(AppTab.values[index]);
+ },
+ items: AppTab.values.map((tab) {
+ return BottomNavigationBarItem(
+ icon: Icon(
+ tab == AppTab.todos ? Icons.list : Icons.show_chart,
+ key: tab == AppTab.stats
+ ? ArchSampleKeys.statsTab
+ : ArchSampleKeys.todoTab,
+ ),
+ label: tab == AppTab.stats
+ ? ArchSampleLocalizations.of(context).stats
+ : ArchSampleLocalizations.of(context).todos,
+ );
+ }).toList(),
+ ),
+ );
+ }
+ }
+
+ void _updateTab(AppTab tab) {
+ setState(() {
+ _activeTab = tab;
+ });
+ }
+
+ void showErrorDlg(BuildContext context, message) {
+ showDialog(
+ context: context,
+ builder: (context) => AlertDialog(
+ title: Text('There was a problem saving your data'),
+ content: Text(message),
+ actions: [
+ TextButton(
+ child: Text('Ok'),
+ onPressed: () => Navigator.of(context).pop(),
+ )
+ ],
+ ));
+ }
+
+ OverlayEntry spinner;
+
+ void showSpinner(BuildContext context, busy) {
+ if (busy && spinner == null) {
+ spinner = OverlayEntry(
+ builder: (context) => Center(
+ child: CircularProgressIndicator(
+ key: Key('busySpinner'),
+ ),
+ ),
+ );
+ Overlay.of(context).insert(spinner);
+ } else {
+ spinner?.remove();
+ spinner = null;
+ }
+ }
+}
diff --git a/rvms/lib/display_todos/widgets/extra_actions_button.dart b/rvms/lib/display_todos/widgets/extra_actions_button.dart
new file mode 100644
index 00000000..b258fafd
--- /dev/null
+++ b/rvms/lib/display_todos/widgets/extra_actions_button.dart
@@ -0,0 +1,45 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:get_it_mixin/get_it_mixin.dart';
+import 'package:rvms_model_sample/display_todos/_manager/todo_manager_.dart';
+import 'package:rvms_model_sample/display_todos/_model/typedefs.dart';
+import 'package:todos_app_core/todos_app_core.dart';
+
+class ExtraActionsButton extends StatelessWidget with GetItMixin {
+ ExtraActionsButton({
+ Key key,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ final allTodos = watchX((TodoManager x) => x.allTodos);
+
+ return PopupMenuButton(
+ key: ArchSampleKeys.extraActionsButton,
+ onSelected: (action) {
+ if (action == ExtraAction.toggleAllComplete) {
+ get().toggleAll();
+ } else if (action == ExtraAction.clearCompleted) {
+ get().clearCompletedCommand();
+ }
+ },
+ itemBuilder: (BuildContext context) => >[
+ PopupMenuItem(
+ key: ArchSampleKeys.toggleAll,
+ value: ExtraAction.toggleAllComplete,
+ child: Text(allTodos.any((it) => !it.complete)
+ ? ArchSampleLocalizations.of(context).markAllIncomplete
+ : ArchSampleLocalizations.of(context).markAllComplete),
+ ),
+ PopupMenuItem(
+ key: ArchSampleKeys.clearCompleted,
+ value: ExtraAction.clearCompleted,
+ child: Text(ArchSampleLocalizations.of(context).clearCompleted),
+ ),
+ ],
+ );
+ }
+}
diff --git a/rvms/lib/display_todos/widgets/filter_button.dart b/rvms/lib/display_todos/widgets/filter_button.dart
new file mode 100644
index 00000000..eff037c3
--- /dev/null
+++ b/rvms/lib/display_todos/widgets/filter_button.dart
@@ -0,0 +1,71 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:rvms_model_sample/display_todos/_manager/todo_manager_.dart';
+import 'package:todos_app_core/todos_app_core.dart';
+
+import '../../locator.dart';
+
+class FilterButton extends StatelessWidget {
+ final bool isActive;
+
+ FilterButton({this.isActive, Key key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return AnimatedOpacity(
+ opacity: isActive ? 1.0 : 0.0,
+ duration: Duration(milliseconds: 150),
+ child: PopupMenuButton(
+ key: ArchSampleKeys.filterButton,
+ tooltip: ArchSampleLocalizations.of(context).filterTodos,
+ onSelected: locator().selectFilterCommand,
+ itemBuilder: (BuildContext context) => _items(context),
+ icon: Icon(Icons.filter_list),
+ ),
+ );
+ }
+
+ List> _items(BuildContext context) {
+ final activeStyle = Theme.of(context)
+ .textTheme
+ .bodyText1
+ .copyWith(color: Theme.of(context).accentColor);
+ final defaultStyle = Theme.of(context).textTheme.bodyText1;
+
+ final activeFilter = locator().activeFilter;
+ return [
+ PopupMenuItem(
+ key: ArchSampleKeys.allFilter,
+ value: VisibilityFilter.all,
+ child: Text(
+ ArchSampleLocalizations.of(context).showAll,
+ style:
+ activeFilter == VisibilityFilter.all ? activeStyle : defaultStyle,
+ ),
+ ),
+ PopupMenuItem(
+ key: ArchSampleKeys.activeFilter,
+ value: VisibilityFilter.active,
+ child: Text(
+ ArchSampleLocalizations.of(context).showActive,
+ style: activeFilter == VisibilityFilter.active
+ ? activeStyle
+ : defaultStyle,
+ ),
+ ),
+ PopupMenuItem(
+ key: ArchSampleKeys.completedFilter,
+ value: VisibilityFilter.completed,
+ child: Text(
+ ArchSampleLocalizations.of(context).showCompleted,
+ style: activeFilter == VisibilityFilter.completed
+ ? activeStyle
+ : defaultStyle,
+ ),
+ ),
+ ];
+ }
+}
diff --git a/rvms/lib/display_todos/widgets/stats_counter.dart b/rvms/lib/display_todos/widgets/stats_counter.dart
new file mode 100644
index 00000000..24917084
--- /dev/null
+++ b/rvms/lib/display_todos/widgets/stats_counter.dart
@@ -0,0 +1,62 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:get_it_mixin/get_it_mixin.dart';
+import 'package:rvms_model_sample/display_todos/_manager/todo_manager_.dart';
+import 'package:rvms_model_sample/display_todos/_model/todo.dart';
+import 'package:todos_app_core/todos_app_core.dart';
+
+class StatsCounter extends StatelessWidget with GetItMixin {
+ StatsCounter() : super(key: ArchSampleKeys.statsCounter);
+
+ bool isActive(Todo todo) => !todo.complete;
+
+ bool isCompleted(Todo todo) => todo.complete;
+
+ @override
+ Widget build(BuildContext context) {
+ final todos = watchX((TodoManager x) => x.allTodos);
+
+ var numCompleted = todos.where(isCompleted).toList().length;
+ var numActive = todos.where(isActive).toList().length;
+
+ return Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Padding(
+ padding: EdgeInsets.only(bottom: 8.0),
+ child: Text(
+ ArchSampleLocalizations.of(context).completedTodos,
+ style: Theme.of(context).textTheme.headline6,
+ ),
+ ),
+ Padding(
+ padding: EdgeInsets.only(bottom: 24.0),
+ child: Text(
+ '$numCompleted',
+ key: ArchSampleKeys.statsNumCompleted,
+ style: Theme.of(context).textTheme.subtitle1,
+ ),
+ ),
+ Padding(
+ padding: EdgeInsets.only(bottom: 8.0),
+ child: Text(ArchSampleLocalizations.of(context).activeTodos,
+ style: Theme.of(context).textTheme.headline6),
+ ),
+ Padding(
+ padding: EdgeInsets.only(bottom: 24.0),
+ child: Text(
+ '$numActive',
+ key: ArchSampleKeys.statsNumActive,
+ style: Theme.of(context).textTheme.subtitle1,
+ ),
+ )
+ ],
+ ),
+ );
+ }
+}
diff --git a/rvms/lib/display_todos/widgets/todo_item.dart b/rvms/lib/display_todos/widgets/todo_item.dart
new file mode 100644
index 00000000..9963b3b2
--- /dev/null
+++ b/rvms/lib/display_todos/widgets/todo_item.dart
@@ -0,0 +1,50 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:rvms_model_sample/display_todos/_model/todo.dart';
+import 'package:todos_app_core/todos_app_core.dart';
+
+class TodoItem extends StatelessWidget {
+ final DismissDirectionCallback onDismissed;
+ final GestureTapCallback onTap;
+ final ValueChanged onCheckboxChanged;
+ final Todo todo;
+
+ TodoItem({
+ @required this.onDismissed,
+ @required this.onTap,
+ @required this.onCheckboxChanged,
+ @required this.todo,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Dismissible(
+ key: ArchSampleKeys.todoItem(todo.id),
+ onDismissed: onDismissed,
+ child: ListTile(
+ onTap: onTap,
+ leading: Checkbox(
+ key: ArchSampleKeys.todoItemCheckbox(todo.id),
+ value: todo.complete,
+ onChanged: onCheckboxChanged,
+ ),
+ title: Text(
+ todo.task,
+ key: ArchSampleKeys.todoItemTask(todo.id),
+ style: Theme.of(context).textTheme.headline6,
+ ),
+ subtitle: Text(
+ todo.note,
+ key: ArchSampleKeys.todoItemNote(todo.id),
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ style: Theme.of(context).textTheme.subtitle1,
+ ),
+ ),
+ );
+ }
+}
diff --git a/rvms/lib/display_todos/widgets/todo_list.dart b/rvms/lib/display_todos/widgets/todo_list.dart
new file mode 100644
index 00000000..c2cc59e7
--- /dev/null
+++ b/rvms/lib/display_todos/widgets/todo_list.dart
@@ -0,0 +1,81 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:get_it_mixin/get_it_mixin.dart';
+import 'package:rvms_model_sample/display_todos/_manager/todo_manager_.dart';
+import 'package:rvms_model_sample/display_todos/_model/todo.dart';
+import 'package:rvms_model_sample/display_todos/detail_screen.dart';
+import 'package:rvms_model_sample/display_todos/widgets/todo_item.dart';
+import 'package:todos_app_core/todos_app_core.dart';
+
+class TodoList extends StatelessWidget with GetItMixin {
+ TodoList({Key key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ final todos = watchX((TodoManager x) => x.filteredTodos);
+
+ return ListView.builder(
+ key: ArchSampleKeys.todoList,
+ itemCount: todos.length,
+ itemBuilder: (BuildContext context, int index) {
+ final todo = todos[index];
+
+ return TodoItem(
+ todo: todo,
+ onDismissed: (_) {
+ _removeTodo(context, todo);
+ },
+ onTap: () {
+ Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (_) {
+ return DetailScreen(
+ todoId: todo.id,
+ );
+ },
+ ),
+ ).then((todo) {
+ if (todo is Todo) {
+ _showUndoSnackbar(context, todo);
+ }
+ });
+ },
+ onCheckboxChanged: (complete) {
+ var toggled = todo.copy(complete: !todo.complete);
+ get().updateTodo(toggled);
+ },
+ );
+ },
+ );
+ }
+
+ void _removeTodo(BuildContext context, Todo todo) {
+ get().removeTodo(todo);
+
+ _showUndoSnackbar(context, todo);
+ }
+
+ void _showUndoSnackbar(BuildContext context, Todo todo) {
+ Scaffold.of(context).showSnackBar(
+ SnackBar(
+ key: ArchSampleKeys.snackbar,
+ duration: Duration(seconds: 2),
+ content: Text(
+ ArchSampleLocalizations.of(context).todoDeleted(todo.task),
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ ),
+ action: SnackBarAction(
+ key: ArchSampleKeys.snackbarAction(todo.id),
+ label: ArchSampleLocalizations.of(context).undo,
+ onPressed: () {
+ get().addTodo(todo);
+ },
+ ),
+ ),
+ );
+ }
+}
diff --git a/rvms/lib/edit_todos/add_edit_screen.dart b/rvms/lib/edit_todos/add_edit_screen.dart
new file mode 100644
index 00000000..d1bc3567
--- /dev/null
+++ b/rvms/lib/edit_todos/add_edit_screen.dart
@@ -0,0 +1,107 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:rvms_model_sample/display_todos/_manager/todo_manager_.dart';
+import 'package:rvms_model_sample/display_todos/_model/todo.dart';
+import 'package:rvms_model_sample/locator.dart';
+import 'package:todos_app_core/todos_app_core.dart';
+
+class AddEditScreen extends StatefulWidget {
+ final String todoId;
+
+ AddEditScreen({
+ Key key,
+ this.todoId,
+ }) : super(key: key ?? ArchSampleKeys.addTodoScreen);
+ @override
+ _AddEditScreenState createState() => _AddEditScreenState();
+}
+
+class _AddEditScreenState extends State {
+ static final GlobalKey _formKey = GlobalKey();
+
+ String _task;
+ String _note;
+
+ bool get isEditing => widget.todoId != null;
+
+ @override
+ Widget build(BuildContext context) {
+ final localizations = ArchSampleLocalizations.of(context);
+ final textTheme = Theme.of(context).textTheme;
+
+ var task = locator().todoById(widget.todoId);
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(
+ isEditing ? localizations.editTodo : localizations.addTodo,
+ ),
+ ),
+ body: Padding(
+ padding: EdgeInsets.all(16.0),
+ child: Form(
+ key: _formKey,
+ autovalidateMode: AutovalidateMode.disabled,
+ onWillPop: () {
+ return Future(() => true);
+ },
+ child: ListView(
+ children: [
+ TextFormField(
+ initialValue: task?.task ?? '',
+ key: ArchSampleKeys.taskField,
+ autofocus: !isEditing,
+ style: textTheme.headline5,
+ decoration: InputDecoration(
+ hintText: localizations.newTodoHint,
+ ),
+ validator: (val) {
+ return val.trim().isEmpty
+ ? localizations.emptyTodoError
+ : null;
+ },
+ onSaved: (value) => _task = value,
+ ),
+ TextFormField(
+ initialValue: task?.note ?? '',
+ key: ArchSampleKeys.noteField,
+ maxLines: 10,
+ style: textTheme.subtitle1,
+ decoration: InputDecoration(
+ hintText: localizations.notesHint,
+ ),
+ onSaved: (value) => _note = value,
+ )
+ ],
+ ),
+ ),
+ ),
+ floatingActionButton: FloatingActionButton(
+ key:
+ isEditing ? ArchSampleKeys.saveTodoFab : ArchSampleKeys.saveNewTodo,
+ tooltip: isEditing ? localizations.saveChanges : localizations.addTodo,
+ child: Icon(isEditing ? Icons.check : Icons.add),
+ onPressed: () {
+ final form = _formKey.currentState;
+ if (form.validate()) {
+ form.save();
+
+ final todoManager = locator();
+ if (isEditing) {
+ final todo = todoManager.todoById(widget.todoId);
+ todoManager.updateTodo(todo.copy(task: _task, note: _note));
+ } else {
+ todoManager.addTodo(Todo(_task, note: _note));
+ }
+
+ Navigator.pop(context);
+ }
+ },
+ ),
+ );
+ }
+}
diff --git a/rvms/lib/localization.dart b/rvms/lib/localization.dart
new file mode 100644
index 00000000..a011b757
--- /dev/null
+++ b/rvms/lib/localization.dart
@@ -0,0 +1,29 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+
+class RvmsLocalizations {
+ static RvmsLocalizations of(BuildContext context) {
+ return Localizations.of(context, RvmsLocalizations);
+ }
+
+ String get appTitle => 'rvms example';
+}
+
+class RvmsLocalizationsDelegate
+ extends LocalizationsDelegate {
+ @override
+ Future load(Locale locale) =>
+ Future(() => RvmsLocalizations());
+
+ @override
+ bool shouldReload(RvmsLocalizationsDelegate old) => false;
+
+ @override
+ bool isSupported(Locale locale) =>
+ locale.languageCode.toLowerCase().contains('en');
+}
diff --git a/rvms/lib/locator.dart b/rvms/lib/locator.dart
new file mode 100644
index 00000000..80fc19a7
--- /dev/null
+++ b/rvms/lib/locator.dart
@@ -0,0 +1,17 @@
+import 'package:get_it/get_it.dart';
+import 'package:rvms_model_sample/display_todos/_manager/todo_manager_.dart';
+import 'package:rvms_model_sample/display_todos/_manager/todo_manager_implementation.dart';
+import 'package:rvms_model_sample/display_todos/_services/repository_service_.dart';
+import 'package:rvms_model_sample/display_todos/_services/repository_service_impl.dart';
+
+final locator = GetIt.instance;
+
+void initLocator() {
+ locator
+ .registerSingleton(RepositoryServiceImplementation());
+ locator.registerSingletonAsync(() async {
+ final manager = TodoManagerImplementation();
+ await manager.loadTodos();
+ return manager;
+ });
+}
diff --git a/rvms/lib/main.dart b/rvms/lib/main.dart
new file mode 100644
index 00000000..fa4a907f
--- /dev/null
+++ b/rvms/lib/main.dart
@@ -0,0 +1,12 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:rvms_model_sample/locator.dart';
+import 'package:rvms_model_sample/startup/app.dart';
+
+void main() {
+ initLocator();
+ runApp(RvmsApp());
+}
diff --git a/rvms/lib/startup/app.dart b/rvms/lib/startup/app.dart
new file mode 100644
index 00000000..baa5076c
--- /dev/null
+++ b/rvms/lib/startup/app.dart
@@ -0,0 +1,27 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:rvms_model_sample/display_todos/home_screen.dart';
+import 'package:rvms_model_sample/edit_todos/add_edit_screen.dart';
+import 'package:rvms_model_sample/localization.dart';
+import 'package:todos_app_core/todos_app_core.dart';
+
+class RvmsApp extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: RvmsLocalizations().appTitle,
+ theme: ArchSampleTheme.theme,
+ localizationsDelegates: [
+ ArchSampleLocalizationsDelegate(),
+ RvmsLocalizationsDelegate(),
+ ],
+ routes: {
+ ArchSampleRoutes.home: (context) => HomeScreen(),
+ ArchSampleRoutes.addTodo: (context) => AddEditScreen(),
+ },
+ );
+ }
+}
diff --git a/rvms/pubspec.yaml b/rvms/pubspec.yaml
new file mode 100644
index 00000000..4befce0d
--- /dev/null
+++ b/rvms/pubspec.yaml
@@ -0,0 +1,67 @@
+name: rvms_model_sample
+description: A new Flutter project.
+
+version: 1.0.0+1
+
+environment:
+ sdk: ">=2.1.0 <3.0.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ todos_app_core:
+ path: ../todos_app_core
+ todos_repository_local_storage:
+ path: ../todos_repository_local_storage
+ flutter_command: any
+ functional_listener: any
+ get_it: any
+ get_it_mixin: any
+ listenable_collections: ^0.0.5
+
+dev_dependencies:
+ flutter_driver:
+ sdk: flutter
+ flutter_test:
+ sdk: flutter
+ integration_tests:
+ path: ../integration_tests
+ mockito: null
+ test: null
+
+# For information on the generic Dart part of this file, see the
+# following page: https://dart.dev/tools/pub/pubspec
+# The following section is specific to Flutter.
+flutter:
+
+ # The following line ensures that the Material Icons font is
+ # included with your application, so that you can use the icons in
+ # the material Icons class.
+ uses-material-design: true
+ # To add assets to your application, add an assets section, like this:
+ # assets:
+ # - images/a_dot_burr.jpeg
+ # - images/a_dot_ham.jpeg
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.dev/assets-and-images/#resolution-aware.
+ # For details regarding adding assets from package dependencies, see
+ # https://flutter.dev/assets-and-images/#from-packages
+ # To add custom fonts to your application, add a fonts section here,
+ # in this "flutter" section. Each entry in this list should have a
+ # "family" key with the font family name, and a "fonts" key with a
+ # list giving the asset and other descriptors for the font. For
+ # example:
+ # fonts:
+ # - family: Schyler
+ # fonts:
+ # - asset: fonts/Schyler-Regular.ttf
+ # - asset: fonts/Schyler-Italic.ttf
+ # style: italic
+ # - family: Trajan Pro
+ # fonts:
+ # - asset: fonts/TrajanPro.ttf
+ # - asset: fonts/TrajanPro_Bold.ttf
+ # weight: 700
+ #
+ # For details regarding fonts from package dependencies,
+ # see https://flutter.dev/custom-fonts/#from-packages
diff --git a/rvms/test/todo_manager_test.dart b/rvms/test/todo_manager_test.dart
new file mode 100644
index 00000000..4fcb8490
--- /dev/null
+++ b/rvms/test/todo_manager_test.dart
@@ -0,0 +1,183 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:get_it/get_it.dart';
+import 'package:rvms_model_sample/display_todos/_manager/todo_manager_.dart';
+import 'package:rvms_model_sample/display_todos/_manager/todo_manager_implementation.dart';
+import 'package:rvms_model_sample/display_todos/_model/todo.dart';
+import 'package:rvms_model_sample/display_todos/_services/repository_service_.dart';
+import 'package:test/test.dart';
+
+final locator = GetIt.instance;
+main() {
+ group('TodoManagerImplementation', () {
+ setUp(() async {
+ await locator.reset();
+ });
+ test('should check if there are completed todos', () async {
+ locator.registerSingleton(MockRepositoryService([
+ Todo('a'),
+ Todo('b'),
+ Todo('c', complete: true),
+ ]));
+ final todoManager = TodoManagerImplementation();
+ await todoManager.loadTodos();
+
+ expect(todoManager.filteredTodos.value.any((it) => it.complete), true);
+ });
+
+ test('should calculate the number of active todos', () async {
+ locator.registerSingleton(MockRepositoryService([
+ Todo('a'),
+ Todo('b'),
+ Todo('c', complete: true),
+ ]));
+ final todoManager = TodoManagerImplementation();
+ await todoManager.loadTodos();
+
+ expect(
+ todoManager.filteredTodos.value
+ .where((it) => !it.complete)
+ .toList()
+ .length,
+ 2);
+ });
+
+ test('should calculate the number of completed todos', () async {
+ locator.registerSingleton(MockRepositoryService([
+ Todo('a'),
+ Todo('b'),
+ Todo('c', complete: true),
+ ]));
+ final todoManager = TodoManagerImplementation();
+ await todoManager.loadTodos();
+
+ expect(
+ todoManager.filteredTodos.value
+ .where((it) => it.complete)
+ .toList()
+ .length,
+ 1);
+ });
+
+ test('should return all todos if the VisibilityFilter is all', () async {
+ final todos = [
+ Todo('a'),
+ Todo('b'),
+ Todo('c', complete: true),
+ ];
+ locator
+ .registerSingleton(MockRepositoryService(todos));
+ final todoManager = TodoManagerImplementation();
+ await todoManager.loadTodos();
+
+ expect(todoManager.filteredTodos.value, todos);
+ });
+
+ test('should return active todos if the VisibilityFilter is active',
+ () async {
+ final todo1 = Todo('a');
+ final todo2 = Todo('b');
+ final todo3 = Todo('c', complete: true);
+ final todos = [
+ todo1,
+ todo2,
+ todo3,
+ ];
+ locator
+ .registerSingleton(MockRepositoryService(todos));
+ final todoManager =
+ TodoManagerImplementation(activeFilter: VisibilityFilter.active);
+
+ await todoManager.loadTodos();
+ expect(todoManager.filteredTodos.value, [
+ todo1,
+ todo2,
+ ]);
+ });
+
+ test('should return completed todos if the VisibilityFilter is completed',
+ () async {
+ final todo1 = Todo('a');
+ final todo2 = Todo('b');
+ final todo3 = Todo('c', complete: true);
+ final todos = [
+ todo1,
+ todo2,
+ todo3,
+ ];
+ locator
+ .registerSingleton(MockRepositoryService(todos));
+ final todoManager =
+ TodoManagerImplementation(activeFilter: VisibilityFilter.completed);
+ await todoManager.loadTodos();
+
+ expect(todoManager.filteredTodos.value, [todo3]);
+ });
+
+ test('should clear the completed todos', () async {
+ final todo1 = Todo('a');
+ final todo2 = Todo('b');
+ final todo3 = Todo('c', complete: true);
+ final todos = [
+ todo1,
+ todo2,
+ todo3,
+ ];
+ locator
+ .registerSingleton(MockRepositoryService(todos));
+ final todoManager = TodoManagerImplementation();
+ await todoManager.loadTodos();
+
+ todoManager.clearCompletedCommand();
+
+ expect(todoManager.filteredTodos.value, [
+ todo1,
+ todo2,
+ ]);
+ });
+
+ test('toggle all as complete or incomplete', () async {
+ final todo1 = Todo('a');
+ final todo2 = Todo('b');
+ final todo3 = Todo('c', complete: true);
+ final todos = [
+ todo1,
+ todo2,
+ todo3,
+ ];
+ locator
+ .registerSingleton(MockRepositoryService(todos));
+ final todoManager = TodoManagerImplementation();
+
+ await todoManager.loadTodos();
+
+ // Toggle all complete
+ todoManager.toggleAll();
+ expect(todoManager.filteredTodos.value.every((t) => t.complete), isTrue);
+
+ // Toggle all incomplete
+ todoManager.toggleAll();
+ expect(todoManager.filteredTodos.value.every((t) => !t.complete), isTrue);
+ });
+ });
+}
+
+class MockRepositoryService extends RepositoryService {
+ List entities;
+
+ MockRepositoryService(List todos) : entities = todos;
+
+ @override
+ Future> loadTodos() {
+ return Future.value(entities);
+ }
+
+ @override
+ Future saveTodos(List todos) {
+ return Future.sync(() => entities = todos);
+ }
+}
diff --git a/rvms/test_driver/todo_app.dart b/rvms/test_driver/todo_app.dart
new file mode 100644
index 00000000..d73c245e
--- /dev/null
+++ b/rvms/test_driver/todo_app.dart
@@ -0,0 +1,13 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+// This line imports the extension
+import 'package:flutter_driver/driver_extension.dart';
+import 'package:rvms_model_sample/main.dart' as app;
+
+void main() {
+ enableFlutterDriverExtension();
+
+ app.main();
+}
diff --git a/rvms/test_driver/todo_app_test.dart b/rvms/test_driver/todo_app_test.dart
new file mode 100644
index 00000000..14ddb566
--- /dev/null
+++ b/rvms/test_driver/todo_app_test.dart
@@ -0,0 +1,9 @@
+// Copyright 2018 The Flutter Architecture Sample Authors. All rights reserved.
+// Use of this source code is governed by the MIT license that can be found
+// in the LICENSE file.
+
+import 'package:integration_tests/integration_tests.dart' as integrationTests;
+
+main() {
+ integrationTests.main();
+}
diff --git a/scoped_model/.flutter-plugins-dependencies b/scoped_model/.flutter-plugins-dependencies
deleted file mode 100644
index d4d3c8cc..00000000
--- a/scoped_model/.flutter-plugins-dependencies
+++ /dev/null
@@ -1 +0,0 @@
-{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"path_provider","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.0/","dependencies":[]}],"android":[{"name":"path_provider","path":"/Users/remirousselet/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.0/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"path_provider","dependencies":[]}],"date_created":"2020-02-10 11:24:35.748663","version":"1.14.7-pre.38"}
\ No newline at end of file
diff --git a/states_rebuilder/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png~merged b/states_rebuilder/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png~merged
new file mode 100644
index 00000000..dc9ada47
Binary files /dev/null and b/states_rebuilder/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png~merged differ